import React from 'react'; import type { Meta, StoryObj } from '@storybook/react-vite'; import { SingleSelect } from './SingleSelect'; // Sample data for stories interface Option { id: string; name: string; category?: string; description?: string; color?: string; } const basicOptions: Option[] = [ { id: '1', name: 'Apple' }, { id: '2', name: 'Banana' }, { id: '3', name: 'Orange' }, { id: '4', name: 'Grapes' }, { id: '5', name: 'Strawberry' }, ]; const colorOptions: Option[] = [ { id: 'red', name: 'Red', color: '#ff0000' }, { id: 'blue', name: 'Blue', color: '#0000ff' }, { id: 'green', name: 'Green', color: '#00ff00' }, { id: 'yellow', name: 'Yellow', color: '#ffff00' }, { id: 'purple', name: 'Purple', color: '#800080' }, ]; const meta: Meta = { title: 'UI kit/Form/Inputs/SingleSelect', component: SingleSelect, parameters: { layout: 'centered', }, tags: ['autodocs'], }; export default meta; type Story = StoryObj>; // Basic Usage Stories export const Default: Story = { args: { id: 'single-select-default', label: 'Select an option', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose an option...', }, }; export const SearchMode: Story = { args: { id: 'single-select-search', mode: 'search', label: 'Search for an option', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Search options...', }, parameters: { docs: { description: { story: 'Single select in search mode where users can type to filter options.', }, }, }, }; export const WithDefaultValue: Story = { args: { id: 'single-select-default-value', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose a fruit...', defaultValue: '2', }, parameters: { docs: { description: { story: 'Single select with a pre-selected default value (Banana).', }, }, }, }; export const Controlled: Story = { render: args => { const [value, setValue] = React.useState('1'); return (
Selected value: {value || 'none'}
); }, args: { id: 'single-select-controlled', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose a fruit...', }, parameters: { docs: { description: { story: 'Controlled single select where the value is managed externally with state.', }, }, }, }; export const DisabledState: Story = { args: { id: 'single-select-disabled', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Disabled select', disabled: true, defaultValue: '2', }, parameters: { docs: { description: { story: 'Single select in disabled state with a pre-selected value.', }, }, }, }; export const WithNullableOption: Story = { args: { id: 'single-select-nullable', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose a fruit...', nullable: true, }, parameters: { docs: { description: { story: 'Single select with nullable option allowing users to clear selection.', }, }, }, }; export const CheckedIndicatorEnd: Story = { args: { id: 'single-select-indicator-end', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose a fruit...', checkedIndicator: 'end', defaultValue: '1', }, parameters: { docs: { description: { story: 'Single select with checked indicator positioned at the end of options.', }, }, }, }; export const NoCheckedIndicator: Story = { args: { id: 'single-select-no-indicator', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose a fruit...', checkedIndicator: false, defaultValue: '1', }, parameters: { docs: { description: { story: 'Single select without any checked indicator.', }, }, }, }; export const WithCustomValueRendering: Story = { args: { id: 'single-select-custom-value', label: 'Select color', options: colorOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose a color...', defaultValue: 'red', renderValue: (option, labelId) => (
{option.name}
), }, parameters: { docs: { description: { story: 'Single select with custom value rendering in the trigger.', }, }, }, }; // Grouping Examples export const WithGroupedOptions: Story = { args: { id: 'single-select-grouped', label: 'Select food item', options: [ { id: 'fruits', name: 'Fruits' }, { id: 'vegetables', name: 'Vegetables' }, ], getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose food...', getGroupOptions: option => { if (option.id === 'fruits') { return [ { id: 'apple', name: 'Apple' }, { id: 'banana', name: 'Banana' }, { id: 'orange', name: 'Orange' }, ]; } if (option.id === 'vegetables') { return [ { id: 'carrot', name: 'Carrot' }, { id: 'broccoli', name: 'Broccoli' }, { id: 'spinach', name: 'Spinach' }, ]; } return undefined; }, }, parameters: { docs: { description: { story: 'Single select with grouped options organized by categories.', }, }, }, }; // Edge Cases export const EmptyOptions: Story = { args: { id: 'single-select-empty', label: 'Select option', options: [], getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'No options available', emptyOptionsLabel: 'No options found', }, parameters: { docs: { description: { story: 'Single select with empty options list and custom empty state message.', }, }, }, }; export const WithDisabledOptions: Story = { args: { id: 'single-select-disabled-options', label: 'Select fruit', options: basicOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Some options are disabled...', disabledValues: ['2', '4'], // Banana and Grapes are disabled }, parameters: { docs: { description: { story: 'Single select with some options disabled (Banana and Grapes).', }, }, }, }; // Search Mode Examples export const SearchWithQuery: Story = { render: args => { const [query, setQuery] = React.useState(null); const [value, setValue] = React.useState(null); const filteredOptions = React.useMemo(() => { if (!query) return basicOptions; return basicOptions.filter(option => option.name.toLowerCase().includes(query.toLowerCase())); }, [query]); return (
Query: {query || ''} | Selected: {value || 'none'}
); }, args: { id: 'single-select-search-query', label: 'Search fruits', getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Type to search...', }, parameters: { docs: { description: { story: 'Search mode with controlled query and filtered options showing real-time search.', }, }, }, }; const largeDatasetOptions: Option[] = Array.from({ length: 3000 }, (_, i) => { const extraWords = i % 10; const longText = Array.from({ length: extraWords }, (_, j) => `word${j + 1}`).join(' '); return { id: `item-${i + 1}`, name: `Item ${i + 1}${longText ? ` ${longText}` : ''}`, }; }); export const WithLargeDataset: Story = { render: args => { const [query, setQuery] = React.useState(null); const [value, setValue] = React.useState(null); const filteredOptions = React.useMemo(() => { if (!query) return largeDatasetOptions; return largeDatasetOptions.filter(option => option.name.toLowerCase().includes(query.toLowerCase())); }, [query]); return ( ); }, args: { id: 'single-select-large-dataset', label: 'Select item', options: largeDatasetOptions, getOptionValue: option => option.id, getOptionLabel: option => option.name, placeholder: 'Choose from 3000 items...', virtualize: { popoverWidth: 500, }, mode: 'search', }, argTypes: { options: { control: false, }, }, parameters: { docs: { description: { story: 'Single select with 3000 items to test performance with large datasets (virtualized).', }, }, }, };