import type { Meta, StoryObj } from '@storybook/nextjs-vite'; import { useState } from 'react'; import { Autocomplete, AutocompleteOption } from '../components/ui/autocomplete'; const meta: Meta = { title: 'UI/Autocomplete', component: Autocomplete, }; export default meta; type Story = StoryObj; const sampleOptions: AutocompleteOption[] = [ { label: 'Enterprise', value: 'enterprise' }, { label: 'Startup', value: 'startup' }, { label: 'SMB', value: 'smb' }, { label: 'Government', value: 'government' }, { label: 'Education', value: 'education' }, { label: 'Non-Profit', value: 'nonprofit' }, { label: 'Healthcare', value: 'healthcare' }, { label: 'Finance', value: 'finance' }, { label: 'Retail', value: 'retail' }, { label: 'Technology', value: 'technology' }, ]; // ─── Single Select Stories ─── /** * Single select (default mode) — no selection. */ export const Single: Story = { render: function Render() { const [value, setValue] = useState(null); return ( ); }, }; /** * Single select with a pre-selected value. */ export const SingleWithValue: Story = { render: function Render() { const [value, setValue] = useState('enterprise'); return ( ); }, }; /** * Single select disabled. */ export const SingleDisabled: Story = { render: function Render() { const [value, setValue] = useState('startup'); return ( ); }, }; /** * Single select with error. */ export const SingleWithError: Story = { render: function Render() { const [value, setValue] = useState(null); return ( ); }, }; // ─── Multiple Select Stories ─── /** * Multiple select with no initial selection. */ export const Multiple: Story = { render: function Render() { const [value, setValue] = useState([]); return ( ); }, }; /** * Multiple select with pre-selected values. */ export const MultipleWithValues: Story = { render: function Render() { const [value, setValue] = useState(['enterprise', 'startup', 'smb']); return ( ); }, }; /** * Multiple select with max selection limit. */ export const MultipleWithMaxItems: Story = { render: function Render() { const [value, setValue] = useState(['enterprise', 'startup']); return ( ); }, }; /** * Multiple select disabled. */ export const MultipleDisabled: Story = { render: function Render() { const [value, setValue] = useState(['enterprise', 'startup']); return ( ); }, }; /** * Multiple select with validation error. */ export const MultipleWithError: Story = { render: function Render() { const [value, setValue] = useState([]); return ( ); }, }; /** * Multiple select with freeSolo mode. */ export const MultipleFreeSolo: Story = { render: function Render() { const [value, setValue] = useState([]); return ( ); }, }; /** * Multiple select with limitTags. */ export const MultipleLimitTags: Story = { render: function Render() { const [value, setValue] = useState(['enterprise', 'startup', 'smb', 'government', 'education']); return ( ); }, }; /** * Multiple select with custom option rendering. */ export const MultipleCustomOptionRender: Story = { render: function Render() { const [value, setValue] = useState([]); return ( (
{option.label}
)} /> ); }, }; /** * Loading state. */ export const Loading: Story = { render: function Render() { const [value, setValue] = useState([]); return ( ); }, }; // ─── Creatable Stories ─── /** * Single select with creatable — type a value that doesn't exist and click "+ Create". */ export const SingleCreatable: Story = { render: function Render() { const [options, setOptions] = useState(sampleOptions); const [value, setValue] = useState(null); return ( { setOptions((prev) => [...prev, { label: input, value: input }]); }} /> ); }, }; /** * Multiple select with creatable — create new tags on the fly. */ export const MultipleCreatable: Story = { render: function Render() { const [options, setOptions] = useState(sampleOptions); const [value, setValue] = useState([]); return ( { setOptions((prev) => [...prev, { label: input, value: input }]); }} /> ); }, }; // ─── Server-Side Filter Stories ─── /** * Simulated server-side filtering — client filter disabled, options updated via onInputChange. */ export const ServerSideFilter: Story = { render: function Render() { const [value, setValue] = useState(null); const [filteredOpts, setFilteredOpts] = useState(sampleOptions); const [loading, setLoading] = useState(false); const handleInputChange = (input: string) => { setLoading(true); // simulate server delay setTimeout(() => { const lower = input.toLowerCase(); setFilteredOpts( input ? sampleOptions.filter((o) => o.label.toLowerCase().includes(lower)) : sampleOptions ); setLoading(false); }, 400); }; return ( ); }, }; /** * Server-side filter + creatable — create when the server returns no results. */ export const ServerSideFilterCreatable: Story = { render: function Render() { const [allOptions, setAllOptions] = useState(sampleOptions); const [filteredOpts, setFilteredOpts] = useState(sampleOptions); const [value, setValue] = useState([]); const [loading, setLoading] = useState(false); const handleInputChange = (input: string) => { setLoading(true); setTimeout(() => { const lower = input.toLowerCase(); setFilteredOpts( input ? allOptions.filter((o) => o.label.toLowerCase().includes(lower)) : allOptions ); setLoading(false); }, 400); }; return ( { const newOpt = { label: input, value: input }; setAllOptions((prev) => [...prev, newOpt]); setFilteredOpts((prev) => [...prev, newOpt]); }} loading={loading} loadingText="Searching..." /> ); }, }; // ─── No Chevron Stories ─── /** * Single select without chevron icon. */ export const SingleNoChevron: Story = { render: function Render() { const [value, setValue] = useState(null); return ( ); }, }; /** * Multiple select without chevron icon. */ export const MultipleNoChevron: Story = { render: function Render() { const [value, setValue] = useState(['enterprise', 'startup']); return ( ); }, }; // ─── Clear On Open Stories ─── /** * Single select that keeps the selected value text when opening the dropdown. * Try selecting "Enterprise", then click the input again — the text stays so you can edit it. */ export const SingleKeepValueOnOpen: Story = { render: function Render() { const [value, setValue] = useState('enterprise'); return ( ); }, }; /** * Default behavior — input clears when opening (for comparison). */ export const SingleClearOnOpen: Story = { render: function Render() { const [value, setValue] = useState('enterprise'); return ( ); }, }; // ─── Overview ─── /** * All variants displayed together for comparison. */ export const AllVariants: Story = { render: function Render() { const [singleValue, setSingleValue] = useState(null); const [singleWithValue, setSingleWithValue] = useState('enterprise'); const [multiValue1, setMultiValue1] = useState([]); const [multiValue2, setMultiValue2] = useState(['enterprise', 'startup', 'smb']); const [multiValue3, setMultiValue3] = useState(['enterprise', 'startup']); return (
{}} label="Single Disabled" disabled />
); }, };