import type { Meta, StoryObj } from '@storybook/react' import { useState, type ComponentProps, type ComponentType } from 'react' import { expect } from 'storybook/test' import { Svg3DPerspectiveGrid } from '@chainlink/blocks-icons' import { Combobox, ComboboxBadge, ComboboxClear, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxLabel, ComboboxList, ComboboxSelectAll, ComboboxSeparator, ComboboxTagsValue, ComboboxTrigger, ComboboxValue, } from './Combobox' type ComboboxStoryProps = Omit, 'children'> type DemoOption = { value: string label: string src: string } type NetworkOption = { value: string label: string } const demoOptions: DemoOption[] = [ { value: 'arbitrum', label: 'Arbitrum', src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/arbitrum.svg?auto=compress%2Cformat', }, { value: 'avalanche', label: 'Avalanche', src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/avalanche.svg?auto=compress%2Cformat', }, { value: 'base', label: 'Base', src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/base.svg?auto=compress%2Cformat', }, { value: 'ethereum', label: 'Ethereum', src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/ethereum.svg?auto=compress%2Cformat', }, { value: 'solana', label: 'Solana', src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/solana.svg?auto=compress%2Cformat', }, { value: 'polygon', label: 'Polygon', src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/polygon.svg?auto=compress%2Cformat', }, ] const meta = { component: Combobox as ComponentType, title: 'Form/Combobox', subcomponents: { ComboboxTrigger, ComboboxValue, ComboboxTagsValue, ComboboxClear, ComboboxContent, ComboboxInput, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxEmpty, ComboboxSelectAll, ComboboxSeparator, ComboboxBadge, }, argTypes: { disabled: { control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, }, }, clearable: { control: 'boolean', table: { type: { summary: 'boolean' }, defaultValue: { summary: 'true' }, }, }, }, args: { disabled: false, clearable: true, }, } satisfies Meta export default meta type Story = StoryObj export const Default: Story = { render: (_args) => { const options = [ { value: 'arbitrum', label: 'Arbitrum' }, { value: 'avalanche', label: 'Avalanche' }, { value: 'base', label: 'Base' }, { value: 'ethereum', label: 'Ethereum' }, ] return ( opt.label} > No results found. {(option) => ( {option.label} )} ) }, } export const NotClearable: Story = { parameters: { docs: { description: { story: 'Use `clearable={false}` when a selected value is required or should only change through another selection.', }, }, }, render: (_args) => { const options = [ { value: 'arbitrum', label: 'Arbitrum' }, { value: 'avalanche', label: 'Avalanche' }, { value: 'base', label: 'Base' }, { value: 'ethereum', label: 'Ethereum' }, ] return ( opt.label} > No results found. {(option) => ( {option.label} )} ) }, play: async ({ canvasElement }) => { const trigger = canvasElement.querySelector( '[data-slot="combobox-trigger"]', ) as HTMLElement await expect( trigger.querySelector('[data-slot="combobox-clear"]'), ).toBeNull() }, } export const SmallSize: Story = { render: (_args) => { const options = [ { value: 'arbitrum', label: 'Arbitrum' }, { value: 'avalanche', label: 'Avalanche' }, { value: 'base', label: 'Base' }, { value: 'ethereum', label: 'Ethereum' }, ] return ( opt.label} size="sm" > No results found. {(option) => ( {option.label} )} ) }, } export const OpenState: Story = { render: (args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return ( opt.label} {...openProp} {...args} > No results found. {(option: NetworkOption) => ( {option.label} )} ) }, } export const XsSize: Story = { render: (args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return ( opt.label} {...openProp} {...args} size="xs" > No results found. {(option: DemoOption) => ( {option.label} )} ) }, } export const WithIcons: Story = { render: (args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return ( opt.label} {...openProp} {...args} > {(option: DemoOption) => ( <> {option.label} )} No results found. {(option: DemoOption) => ( {option.label} )} ) }, } export const WithImages: Story = { render: (args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return ( opt.label} {...openProp} {...args} > {(option: DemoOption) => ( <> {option.label} {option.label} )} No results found. {(option: DemoOption) => ( {option.label} {option.label} )} ) }, } export const ClearButton: Story = { render: (args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return ( opt.label} defaultValue={demoOptions[3]} {...openProp} {...args} > {(option: DemoOption) => ( <> {option.label} {option.label} )} No results found. {(option: DemoOption) => ( {option.label} {option.label} )} ) }, } export const EmptyStates: Story = { parameters: { docs: { description: { story: 'Shows the empty result message when filtering removes every item from the list.', }, }, }, render: (_args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return (
opt.label} defaultInputValue="zzz" {...openProp} > No results found. {(option: DemoOption) => ( {option.label} )} opt.label} defaultInputValue="zzz" {...openProp} size="sm" > No results found. {(option: DemoOption) => ( {option.label} )} opt.label} defaultInputValue="zzz" {...openProp} size="xs" > No results found. {(option: DemoOption) => ( {option.label} )}
) }, } export const EmptyStatesMulti: Story = { parameters: { docs: { description: { story: 'Shows the empty result message for multi-select comboboxes across available sizes.', }, }, }, render: (_args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return (
opt.label} defaultInputValue="zzz" {...openProp} > placeholder="Default"> {(opt: DemoOption) => opt.label} No results found. {(option: DemoOption) => ( {option.label} )} opt.label} defaultInputValue="zzz" {...openProp} size="sm" > placeholder="Small"> {(opt: DemoOption) => opt.label} No results found. {(option: DemoOption) => ( {option.label} )} opt.label} defaultInputValue="zzz" {...openProp} size="xs" > placeholder="Extra small"> {(opt: DemoOption) => opt.label} No results found. {(option: DemoOption) => ( {option.label} )}
) }, } export const Disabled: Story = { render: (args) => (
opt.label} {...args} disabled > No results found. {(option: DemoOption) => ( {option.label} )} opt.label} defaultValue={demoOptions[3]} {...args} disabled > No results found. {(option: DemoOption) => ( {option.label} )}
), } function MultiSelectDemo({ openProp = {}, maxCount = 3, }: { openProp?: { defaultOpen?: boolean } maxCount?: number }) { const [selected, setSelected] = useState([ demoOptions[0], demoOptions[3], demoOptions[4], demoOptions[5], ]) return ( opt.label} value={selected} onValueChange={(v: unknown) => setSelected((v ?? []) as DemoOption[])} {...openProp} > placeholder="Select networks..." maxCount={maxCount} > {(opt: DemoOption) => ( <> {opt.label} {opt.label} )} No results found. {(option: DemoOption) => ( {option.label} {option.label} )} ) } export const MultiSelect: Story = { parameters: { docs: { description: { story: 'Use multi-select when users can choose several values from the same option set.', }, }, }, render: (_args, context) => { const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {} return }, }