import type { Meta, StoryObj } from '@storybook/vue3' import TitanStackedSelectRenderer from './TitanStackedSelectRenderer.vue' import type { StackedSelectOption } from '@/types/components' const meta = { component: TitanStackedSelectRenderer, title: 'UI/TitanStackedSelectRenderer', tags: ['autodocs'], argTypes: { options: { control: 'object', description: 'Array of stacked select options with title and description' }, label: { control: 'text', description: 'Label text displayed above the select options' }, disabled: { control: 'boolean', description: 'Disable all options' }, error: { control: 'boolean', description: 'Show error state' }, errorMessage: { control: 'text', description: 'Error message text' }, isSelected: { control: false, description: 'Function to determine if an option is selected' }, handleSelect: { control: false, description: 'Function called when an option is clicked', action: 'selected' }, getStateClasses: { control: false, description: 'Function to get CSS classes based on option state' }, getOptionAriaAttributes: { control: false, description: 'Function to get ARIA attributes for an option' } } } satisfies Meta export default meta type Story = StoryObj // Mock options for stories const serviceOptions: StackedSelectOption[] = [ { value: 'traditional-religious', label: 'traditional-religious', title: 'Traditional religious service', description: 'A religious ceremony at a place of worship.' }, { value: 'traditional-secular', label: 'traditional-secular', title: 'Traditional secular service', description: 'A ceremony at a funeral home or other location.' }, { value: 'celebration-of-life', label: 'celebration-of-life', title: 'Celebration of life', description: 'A personalized gathering focused on celebrating the life lived.' }, { value: 'memorial-only', label: 'memorial-only', title: 'Memorial service only', description: 'A service without the body present, often after cremation.' } ] // Helper functions for stories const createIsSelected = (selectedValue: string) => (option: StackedSelectOption) => option.value === selectedValue const createMultiIsSelected = (selectedValues: string[]) => (option: StackedSelectOption) => selectedValues.includes(option.value) const createHandleSelect = () => (option: StackedSelectOption) => { console.log('Selected option:', option.value) } const getStateClasses = (selectedValue: string) => (option: StackedSelectOption) => { const selected = option.value === selectedValue return { base: 'w-full p-4 rounded-lg border-2 text-left', state: selected ? 'bg-titan-purple-500 text-white border-titan-purple-500 shadow-sm' : 'bg-titan-green-800/10 text-titan-green-800 border-titan-green-800/20 hover:bg-titan-green-800/15', disabled: option.disabled ? 'opacity-50 cursor-not-allowed' : '' } } const getOptionAriaAttributes = (selectedValue: string) => (option: StackedSelectOption) => ({ role: 'radio', 'aria-checked': option.value === selectedValue, 'aria-disabled': option.disabled }) const getMultiStateClasses = (selectedValues: string[]) => (option: StackedSelectOption) => { const selected = selectedValues.includes(option.value) return { base: 'w-full px-4 py-3 rounded-sm border text-left transition-colors', state: selected ? 'bg-titan-purple-500 text-white border-titan-purple-500 shadow-sm' : 'bg-titan-green-800/10 text-titan-green-800 border-titan-green-800/20 hover:bg-titan-green-800/15', disabled: option.disabled ? 'opacity-50 cursor-not-allowed' : '' } } const getMultiOptionAriaAttributes = (selectedValues: string[]) => (option: StackedSelectOption) => ({ role: 'checkbox', 'aria-checked': selectedValues.includes(option.value), 'aria-disabled': option.disabled }) /** * Default stacked renderer showing service type selection */ export const Default: Story = { args: { options: serviceOptions, label: 'WITH A:', isSelected: createIsSelected('traditional-religious'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('traditional-religious'), getOptionAriaAttributes: getOptionAriaAttributes('traditional-religious') } } /** * Stacked renderer with no selection (all options unselected) */ export const NoSelection: Story = { args: { options: serviceOptions, label: 'CHOOSE A SERVICE TYPE:', isSelected: createIsSelected(''), handleSelect: createHandleSelect(), getStateClasses: getStateClasses(''), getOptionAriaAttributes: getOptionAriaAttributes('') } } /** * Stacked renderer with different selected option */ export const CelebrationSelected: Story = { args: { options: serviceOptions, label: 'SELECT YOUR SERVICE:', isSelected: createIsSelected('celebration-of-life'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('celebration-of-life'), getOptionAriaAttributes: getOptionAriaAttributes('celebration-of-life') } } /** * Stacked renderer with disabled option */ export const WithDisabledOption: Story = { args: { options: [ ...serviceOptions.slice(0, 2), { value: 'celebration-of-life', label: 'celebration-of-life', title: 'Celebration of life', description: 'A personalized gathering focused on celebrating the life lived.', disabled: true }, serviceOptions[3] ], label: 'SELECT YOUR SERVICE:', isSelected: createIsSelected('traditional-religious'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('traditional-religious'), getOptionAriaAttributes: getOptionAriaAttributes('traditional-religious') } } /** * Stacked renderer without label */ export const NoLabel: Story = { args: { options: serviceOptions, isSelected: createIsSelected('traditional-secular'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('traditional-secular'), getOptionAriaAttributes: getOptionAriaAttributes('traditional-secular') } } /** * Stacked renderer with error state */ export const ErrorState: Story = { args: { options: serviceOptions, label: 'SELECT YOUR SERVICE:', error: true, errorMessage: 'Please select a service type to continue', isSelected: createIsSelected(''), handleSelect: createHandleSelect(), getStateClasses: getStateClasses(''), getOptionAriaAttributes: getOptionAriaAttributes('') } } /** * Multiple selection layout preview using textarea-style cards */ export const MultipleSelection: Story = { args: { options: serviceOptions, label: 'SELECT ALL THAT APPLY:', multiple: true, isSelected: createMultiIsSelected(['traditional-religious', 'celebration-of-life']), handleSelect: createHandleSelect(), getStateClasses: getMultiStateClasses(['traditional-religious', 'celebration-of-life']), getOptionAriaAttributes: getMultiOptionAriaAttributes(['traditional-religious', 'celebration-of-life']) } } /** * Stacked renderer with shorter descriptions */ export const ShortDescriptions: Story = { args: { options: [ { value: 'basic', label: 'basic', title: 'Basic service', description: 'Essential arrangements.' }, { value: 'standard', label: 'standard', title: 'Standard service', description: 'Complete traditional service.' }, { value: 'premium', label: 'premium', title: 'Premium service', description: 'Enhanced experience with extras.' } ], label: 'CHOOSE A PACKAGE:', isSelected: createIsSelected('standard'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('standard'), getOptionAriaAttributes: getOptionAriaAttributes('standard') } } /** * Stacked renderer with long descriptions (overflow handling) */ export const LongDescriptions: Story = { args: { options: [ { value: 'full-service', label: 'full-service', title: 'Full-service traditional funeral', description: 'This comprehensive package includes viewing, visitation, formal funeral service at a place of worship or funeral home, transportation to the cemetery or crematory, and professional services for coordinating all aspects of the arrangements including coordination with clergy, musicians, and other vendors.' }, { value: 'direct-burial', label: 'direct-burial', title: 'Direct burial service', description: 'A simple and affordable option that includes immediate burial without embalming, viewing, or ceremony. The deceased is placed in a casket and buried shortly after death, with memorial services held separately if desired.' } ], label: 'SELECT SERVICE LEVEL:', isSelected: createIsSelected('full-service'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('full-service'), getOptionAriaAttributes: getOptionAriaAttributes('full-service') } } /** * Stacked renderer with two options (minimal case) */ export const TwoOptions: Story = { args: { options: [ { value: 'yes', label: 'yes', title: 'Yes, include viewing', description: 'Family and friends can view the deceased before the service.' }, { value: 'no', label: 'no', title: 'No viewing needed', description: 'Proceed directly to service without viewing.' } ], label: 'WOULD YOU LIKE A VIEWING?', isSelected: createIsSelected('yes'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('yes'), getOptionAriaAttributes: getOptionAriaAttributes('yes') } } /** * Interactive playground with all controls */ export const Playground: Story = { args: { options: serviceOptions, label: 'SELECT YOUR SERVICE:', disabled: false, error: false, errorMessage: '', isSelected: createIsSelected('traditional-religious'), handleSelect: createHandleSelect(), getStateClasses: getStateClasses('traditional-religious'), getOptionAriaAttributes: getOptionAriaAttributes('traditional-religious') } }