import type { Meta, StoryObj } from '@storybook/vue3-vite' import type { MultiSelectProps } from 'primevue/multiselect' import { ref } from 'vue' import MultiSelect from './MultiSelect.vue' import type { SelectOption } from './types' // Combine our component props with PrimeVue MultiSelect props interface ExtendedProps extends Partial { // Our custom props label?: string showSearchBox?: boolean showSelectedCount?: boolean showClearButton?: boolean searchPlaceholder?: string listMaxHeight?: string popoverMinWidth?: string popoverMaxWidth?: string // Override modelValue type to match our Option type modelValue?: SelectOption[] } const meta: Meta = { title: 'Components/Input/MultiSelect/Accessibility', component: MultiSelect, tags: ['autodocs'], parameters: { docs: { description: { component: ` # MultiSelect Accessibility Guide This MultiSelect component provides full keyboard accessibility and screen reader support following WCAG 2.1 AA guidelines. ## Keyboard Navigation - **Tab** - Focus the trigger button - **Enter/Space** - Open/close dropdown when focused - **Arrow Up/Down** - Navigate through options when dropdown is open - **Enter/Space** - Select/deselect options when navigating - **Escape** - Close dropdown ## Screen Reader Support - Uses \`role="combobox"\` to identify as dropdown - \`aria-haspopup="listbox"\` indicates popup contains list - \`aria-expanded\` shows dropdown state - \`aria-label\` provides accessible name with i18n fallback - Selected count announced to screen readers ## Testing Instructions 1. **Tab Navigation**: Use Tab key to focus the component 2. **Keyboard Opening**: Press Enter or Space to open dropdown 3. **Option Navigation**: Use Arrow keys to navigate options 4. **Selection**: Press Enter/Space to select options 5. **Closing**: Press Escape to close dropdown 6. **Screen Reader**: Test with screen reader software Try these stories with keyboard-only navigation! ` } } }, argTypes: { label: { control: 'text', description: 'Label for the trigger button' }, showSearchBox: { control: 'boolean', description: 'Show search box in dropdown header' }, showSelectedCount: { control: 'boolean', description: 'Show selected count in dropdown header' }, showClearButton: { control: 'boolean', description: 'Show clear all button in dropdown header' } } } export default meta type Story = StoryObj const frameworkOptions = [ { name: 'React', value: 'react' }, { name: 'Vue', value: 'vue' }, { name: 'Angular', value: 'angular' }, { name: 'Svelte', value: 'svelte' }, { name: 'TypeScript', value: 'typescript' }, { name: 'JavaScript', value: 'javascript' } ] export const KeyboardNavigationDemo: Story = { render: (args) => ({ components: { MultiSelect }, setup() { const selectedFrameworks = ref([]) const searchQuery = ref('') return { args: { ...args, options: frameworkOptions, modelValue: selectedFrameworks, 'onUpdate:modelValue': (value: SelectOption[]) => { selectedFrameworks.value = value }, 'onUpdate:searchQuery': (value: string) => { searchQuery.value = value } }, selectedFrameworks, searchQuery } }, template: `

🎯 Keyboard Navigation Test

Use your keyboard to navigate this MultiSelect:

  1. Tab to focus the dropdown
  2. Enter/Space to open dropdown
  3. Arrow Up/Down to navigate options
  4. Enter/Space to select options
  5. Escape to close dropdown

Selected: {{ selectedFrameworks.map(f => f.name).join(', ') || 'None' }}

` }), args: { label: 'Choose Frameworks', showSearchBox: true, showSelectedCount: true, showClearButton: true } } export const ScreenReaderFriendly: Story = { render: (args) => ({ components: { MultiSelect }, setup() { const selectedColors = ref([]) const selectedSizes = ref([]) const colorOptions = [ { name: 'Red', value: 'red' }, { name: 'Blue', value: 'blue' }, { name: 'Green', value: 'green' }, { name: 'Yellow', value: 'yellow' } ] const sizeOptions = [ { name: 'Small', value: 'sm' }, { name: 'Medium', value: 'md' }, { name: 'Large', value: 'lg' }, { name: 'Extra Large', value: 'xl' } ] return { selectedColors, selectedSizes, colorOptions, sizeOptions, args } }, template: `

♿ Screen Reader Test

These dropdowns have proper ARIA attributes and labels for screen readers:

  • role="combobox" identifies as dropdown
  • aria-haspopup="listbox" indicates popup type
  • aria-expanded shows open/closed state
  • aria-label provides accessible name
  • Selection count announced to assistive technology

{{ selectedColors.length }} color(s) selected

{{ selectedSizes.length }} size(s) selected

` }) } export const FocusManagement: Story = { render: (args) => ({ components: { MultiSelect }, setup() { const selectedItems = ref([]) const focusTestOptions = [ { name: 'Option A', value: 'a' }, { name: 'Option B', value: 'b' }, { name: 'Option C', value: 'c' } ] return { selectedItems, focusTestOptions, args } }, template: `

🎯 Focus Management Test

Test focus behavior with multiple form elements:

Test: Tab through all elements and verify focus rings are visible and logical.
` }) } export const AccessibilityChecklist: Story = { render: () => ({ template: `

♿ MultiSelect Accessibility Checklist

✅ Implemented Features

  • ✓ Keyboard Navigation: Tab, Enter, Space, Arrow keys, Escape
  • ✓ ARIA Attributes: role, aria-haspopup, aria-expanded, aria-label
  • ✓ Focus Management: Visible focus rings and logical tab order
  • ✓ Internationalization: Translatable aria-label fallbacks
  • ✓ Screen Reader Support: Proper announcements and state
  • ✓ Color Contrast: Meets WCAG AA requirements

📋 Testing Guidelines

  1. Keyboard Only: Navigate using only keyboard
  2. Screen Reader: Test with NVDA, JAWS, or VoiceOver
  3. Focus Visible: Ensure focus rings are always visible
  4. Tab Order: Verify logical progression
  5. Announcements: Check state changes are announced
  6. Escape Behavior: Escape always closes dropdown

🎯 Quick Test

Close your eyes, use only the keyboard, and try to select multiple options from any dropdown above. If you can successfully navigate and make selections, the accessibility implementation is working!

` }) }