import type { Args, Meta, StoryObj } from '@storybook/vue3-vite' import type { MenuItem } from 'primevue/menuitem' import { computed } from 'vue' import type { CpAccordionBaseProps } from '@/components/CpAccordion.vue' import CpAccordion from '@/components/CpAccordion.vue' import CpButton from '@/components/CpButton.vue' /** Storybook controls: component props remain a discriminated union in `CpAccordion.vue`. */ type CpAccordionStoryArgs = CpAccordionBaseProps & { actions?: MenuItem[] hideActionTrigger?: boolean iconPosition?: 'leading' | 'trailing' } const defaultStoryActions: MenuItem[] = [ { icon: 'copy', label: 'Duplicate', command: () => {}, }, { icon: 'trash-2', label: 'Delete', command: () => {}, isCritical: true, }, ] const meta = { title: 'Molecules/CpAccordion', component: CpAccordion, argTypes: { title: { control: 'text', description: 'Accordion header title', }, defaultOpenState: { control: 'boolean', description: 'Initial open state when the accordion is rendered', }, hideActionTrigger: { control: 'boolean', description: 'Whether to hide the inline action trigger (leading icon mode)', }, iconPosition: { control: 'select', options: ['leading', 'trailing'], description: 'Chevron icon position. `trailing` disables actions.', }, quickOptionsLimit: { control: 'number', description: 'Number of quick-access actions to display inline', }, actions: { control: 'object', description: 'MenuItem[] (leading only). Editing as JSON drops `command` functions; use Reset to restore working actions.', }, }, } as Meta export default meta type Story = StoryObj const sampleContentStyle = 'padding: 16px; font-size: 13px; color: #374151; background: repeating-linear-gradient(45deg, #fff9c4, #fff9c4 4px, #fffde7 4px, #fffde7 8px);' const wrapperStyle = 'box-sizing: border-box; width: 100%; max-width: 1200px; margin: 0 auto; padding: 20px; width: 500px;' const defaultRender = (args: Args) => ({ components: { CpAccordion }, setup() { const accordionArgs = computed(() => { if (args.iconPosition === 'trailing') { const a = args as CpAccordionStoryArgs & Args return { defaultOpenState: a.defaultOpenState, iconPosition: 'trailing' as const, quickOptionsLimit: a.quickOptionsLimit, title: a.title, } } return args }) return { accordionArgs, sampleContentStyle, wrapperStyle } }, template: `
`, }) /** * Default accordion with a leading chevron icon, open by default and two quick actions. */ export const Default: Story = { args: { title: 'I have a title.', defaultOpenState: true, iconPosition: 'leading', quickOptionsLimit: 2, hideActionTrigger: false, actions: defaultStoryActions, }, render: defaultRender, } /* -------------------------------------------------------------------------- */ /* Icon position */ /* -------------------------------------------------------------------------- */ /** * The chevron is rendered at the beginning of the header. This is the default. * Actions can be combined with this layout. */ export const LeadingIcon: Story = { args: { ...Default.args, iconPosition: 'leading', title: 'Leading chevron', }, render: defaultRender, } /** * The chevron is rendered at the end of the header. This variant is * action-less by design (the component enforces it through its prop types). */ export const TrailingIcon: Story = { args: { title: 'Trailing chevron', defaultOpenState: false, iconPosition: 'trailing', quickOptionsLimit: 0, }, render: defaultRender, } /* -------------------------------------------------------------------------- */ /* Open state */ /* -------------------------------------------------------------------------- */ /** * The accordion renders open on first mount. */ export const OpenByDefault: Story = { args: { ...Default.args, defaultOpenState: true, title: 'Open on mount', }, render: defaultRender, } /** * The accordion renders closed on first mount. */ export const ClosedByDefault: Story = { args: { ...Default.args, defaultOpenState: false, title: 'Closed on mount', }, render: defaultRender, } /* -------------------------------------------------------------------------- */ /* Actions & quick options */ /* -------------------------------------------------------------------------- */ /** * Pass `actions` (a `MenuItem[]`) to reveal a contextual menu on hover/focus. * Only available in `leading` icon mode. */ export const WithActions: Story = { args: { ...Default.args, title: 'Accordion with a contextual menu', quickOptionsLimit: 0, actions: defaultStoryActions, }, render: defaultRender, } /** * Use `quickOptionsLimit` to display N action triggers inline at the start of * the header in addition to the overflow menu. */ export const WithQuickOptions: Story = { args: { ...Default.args, title: 'With quick options', quickOptionsLimit: 2, actions: defaultStoryActions, }, render: defaultRender, } /** * Same as `WithActions`, but the inline action trigger (the `…` button) is * hidden. Useful when actions are surfaced elsewhere. */ export const HideActionTrigger: Story = { args: { ...Default.args, title: 'Action trigger hidden', hideActionTrigger: true, actions: defaultStoryActions, }, render: defaultRender, } /** * An accordion without any inline actions. */ export const WithoutActions: Story = { args: { title: 'Plain accordion', defaultOpenState: true, iconPosition: 'leading', quickOptionsLimit: 0, hideActionTrigger: false, actions: [], }, render: defaultRender, } /* -------------------------------------------------------------------------- */ /* Slots */ /* -------------------------------------------------------------------------- */ /** * The `leading-slot` lets you append rich content next to the title * (e.g. meta, badges). */ export const WithLeadingSlot: Story = { args: { ...Default.args, title: 'Accordion with leading slot', actions: [], quickOptionsLimit: 0, }, render: (args: Args) => ({ components: { CpAccordion }, setup() { return { args, sampleContentStyle, wrapperStyle } }, template: `
`, }), } /** * The `trailing-slot` is shown on the right of the header. It supports any * custom content (e.g. timestamps, counters). */ export const WithTrailingSlot: Story = { args: { ...Default.args, title: 'Accordion with trailing slot', actions: [], quickOptionsLimit: 0, }, render: (args: Args) => ({ components: { CpAccordion }, setup() { return { args, sampleContentStyle, wrapperStyle, CpButton } }, template: `
`, }), }