Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 213 additions & 146 deletions src/components/form-field/listbox/listbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,163 +1,230 @@
/* eslint-disable react/jsx-props-no-spreading */
import type { Meta, StoryObj } from "@storybook/react";
import React, { FC } from "react";
import { FormField } from "../form-field";
import type { Meta, StoryObj } from '@storybook/react-vite'

Check failure on line 2 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

Module '"@storybook/react-vite"' has no exported member 'StoryObj'.

Check failure on line 2 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

Module '"@storybook/react-vite"' has no exported member 'Meta'.
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import should use '@storybook/react' instead of '@storybook/react-vite' to maintain consistency with all other story files in the codebase. All other story files import from '@storybook/react'.

Suggested change
import type { Meta, StoryObj } from '@storybook/react-vite'
import type { Meta, StoryObj } from '@storybook/react'

Copilot uses AI. Check for mistakes.
import { FC, useState } from 'react'
import { FormField } from '../form-field'

type ListboxStoryArgs = {
label: string
description: string
placeholder: string
width: number
disabled: boolean
}

const meta: Meta<typeof FormField.Listbox> = {
title: "Input/Listbox",
component: FormField.Listbox,
};
const meta: Meta<ListboxStoryArgs> = {
title: 'Input/Listbox',
args: {
label: 'Label',
description: 'Description',
placeholder: 'Select...',
width: 288,
disabled: false,
},
argTypes: {
label: { control: 'text' },
description: { control: 'text' },
placeholder: { control: 'text' },
width: { control: { type: 'range', min: 200, max: 360, step: 16 } },
disabled: { control: 'boolean' },
},
}
Comment on lines +14 to +30
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The meta configuration is missing the 'component' property. While using custom args for story controls is valid, the meta object should still include a reference to the component being documented. Add component: FormField.Listbox to the meta configuration to maintain consistency with Storybook best practices and other story files in the codebase.

Copilot uses AI. Check for mistakes.

export default meta;
export default meta

type Story = StoryObj<typeof FormField.Listbox>;
type Story = StoryObj<ListboxStoryArgs>

interface Person {
id: number;
name: string;
isDead?: boolean;
id: number
name: string
isDead?: boolean
}

const people: Person[] = [
{ id: 1, name: "John Lennon", isDead: true },
{ id: 2, name: "Kenton Towne" },
{ id: 3, name: "Therese Wunsch" },
{ id: 4, name: "Benedict Kessler" },
{ id: 5, name: "Katelyn Rohan" },
];

const ListboxTextWithHooks = () => {
const [selectedPerson, setSelectedPerson] = React.useState<null | Person>(null);

return (
<FormField>
<FormField.LabelGroup>
<FormField.Label htmlFor="value">Label</FormField.Label>
<FormField.Description id="value-description">Description</FormField.Description>
</FormField.LabelGroup>
<FormField.Listbox value={selectedPerson} onChange={setSelectedPerson}>
<FormField.Listbox.Button>
<FormField.Listbox.Button.TextValue
value={selectedPerson?.name ?? null}
placeholder="Select..."
/>
</FormField.Listbox.Button>
<FormField.Listbox.Options>
{people.map((person) => (
<FormField.Listbox.Option
value={person}
key={person.id}
disabled={person.isDead}
>
<FormField.Listbox.Option.TextOption>
{person.name}
</FormField.Listbox.Option.TextOption>
</FormField.Listbox.Option>
))}
</FormField.Listbox.Options>
</FormField.Listbox>
</FormField>
);
};

const ListboxTextWithMultiplePropWithHooks = () => {
const [selectedPeople, setSelectedPeople] = React.useState<Person[]>([]);

return (
<FormField>
<FormField.LabelGroup>
<FormField.Label htmlFor="value">Label</FormField.Label>
<FormField.Description id="value-description">Description</FormField.Description>
</FormField.LabelGroup>
<FormField.Listbox<Person> value={selectedPeople} onChange={setSelectedPeople} multiple>
<FormField.Listbox.Button>
<FormField.Listbox.Button.TextValue
value={
selectedPeople.length > 0
? selectedPeople.map((person) => person.name).join(", ")
: null
}
placeholder="Select..."
/>
</FormField.Listbox.Button>
<FormField.Listbox.Options>
{people.map((person) => (
<FormField.Listbox.Option
value={person}
key={person.id}
disabled={person.isDead}
>
<FormField.Listbox.Option.TextOption>
{person.name}
</FormField.Listbox.Option.TextOption>
</FormField.Listbox.Option>
))}
</FormField.Listbox.Options>
</FormField.Listbox>
</FormField>
);
};

const ListboxBadgeWithHooks: FC<{ disabled?: boolean }> = ({ disabled }) => {
const [selectedPerson, setSelectedPerson] = React.useState<null | Person>(null);

return (
<FormField>
<FormField.LabelGroup>
<FormField.Label htmlFor="value">Label</FormField.Label>
<FormField.Description id="value-description">Description</FormField.Description>
</FormField.LabelGroup>

<FormField.Listbox value={selectedPerson} onChange={setSelectedPerson}>
<FormField.Listbox.Button disabled={disabled}>
<FormField.Listbox.Button.BadgeValue
value={selectedPerson?.name ?? null}
placeholder="Select …"
/>
</FormField.Listbox.Button>

<FormField.Listbox.Options>
{people.map((person) => (
<FormField.Listbox.Option value={person} key={person.id}>
<FormField.Listbox.Option.BadgeOption>
{person.name}
</FormField.Listbox.Option.BadgeOption>
</FormField.Listbox.Option>
))}
</FormField.Listbox.Options>
</FormField.Listbox>
</FormField>
);
};
{ id: 1, name: 'John Lennon', isDead: true },
{ id: 2, name: 'Kenton Towne' },
{ id: 3, name: 'Therese Wunsch' },
{ id: 4, name: 'Benedict Kessler' },
{ id: 5, name: 'Katelyn Rohan' },
]

const ListboxTextWithHooks = ({
label,
description,
placeholder,
}: {
label: string
description: string
placeholder: string
}) => {
const [selectedPerson, setSelectedPerson] = useState<null | Person>(null)

return (
<FormField>

Check failure on line 62 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
<FormField.LabelGroup>

Check failure on line 63 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
<FormField.Label htmlFor='value'>{label}</FormField.Label>

Check failure on line 64 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
<FormField.Description id='value-description'>

Check failure on line 65 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
{description}
</FormField.Description>
</FormField.LabelGroup>
<FormField.Listbox value={selectedPerson} onChange={setSelectedPerson}>

Check failure on line 69 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
<FormField.Listbox.Button>

Check failure on line 70 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
<FormField.Listbox.Button.TextValue

Check failure on line 71 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
value={selectedPerson?.name ?? null}
placeholder={placeholder}
/>
</FormField.Listbox.Button>
<FormField.Listbox.Options>

Check failure on line 76 in src/components/form-field/listbox/listbox.stories.tsx

View workflow job for this annotation

GitHub Actions / format-check

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
{people.map((person) => (
<FormField.Listbox.Option
value={person}
key={person.id}
disabled={person.isDead}
>
<FormField.Listbox.Option.TextOption>
{person.name}
</FormField.Listbox.Option.TextOption>
</FormField.Listbox.Option>
))}
</FormField.Listbox.Options>
</FormField.Listbox>
</FormField>
)
}

const ListboxBadgeWithHooks: FC<{
disabled?: boolean
label: string
description: string
placeholder: string
}> = ({ disabled, label, description, placeholder }) => {
const [selectedPerson, setSelectedPerson] = useState<null | Person>(null)

return (
<FormField>
<FormField.LabelGroup>
<FormField.Label htmlFor='value'>{label}</FormField.Label>
<FormField.Description id='value-description'>
{description}
</FormField.Description>
</FormField.LabelGroup>

<FormField.Listbox value={selectedPerson} onChange={setSelectedPerson}>
<FormField.Listbox.Button disabled={disabled}>
<FormField.Listbox.Button.BadgeValue
value={selectedPerson?.name ?? null}
placeholder={placeholder}
/>
</FormField.Listbox.Button>

<FormField.Listbox.Options>
{people.map((person) => (
<FormField.Listbox.Option value={person} key={person.id}>
<FormField.Listbox.Option.BadgeOption>
{person.name}
</FormField.Listbox.Option.BadgeOption>
</FormField.Listbox.Option>
))}
</FormField.Listbox.Options>
</FormField.Listbox>
</FormField>
)
}

const ListboxMultiWithHooks = ({
label,
description,
placeholder,
}: {
label: string
description: string
placeholder: string
}) => {
const [selectedPeople, setSelectedPeople] = useState<Person[]>([])
const selectedLabels = selectedPeople.map((person) => person.name).join(', ')

return (
<FormField>
<FormField.LabelGroup>
<FormField.Label htmlFor='value'>{label}</FormField.Label>
<FormField.Description id='value-description'>
{description}
</FormField.Description>
</FormField.LabelGroup>
<FormField.Listbox
value={selectedPeople}
onChange={setSelectedPeople}
multiple
>
<FormField.Listbox.Button>
<FormField.Listbox.Button.TextValue
value={selectedLabels || null}
placeholder={placeholder}
/>
</FormField.Listbox.Button>
<FormField.Listbox.Options>
{people.map((person) => (
<FormField.Listbox.Option
value={person}
key={person.id}
disabled={person.isDead}
>
<FormField.Listbox.Option.TextOption>
{person.name}
</FormField.Listbox.Option.TextOption>
</FormField.Listbox.Option>
))}
</FormField.Listbox.Options>
</FormField.Listbox>
</FormField>
)
}

export const Default: Story = {
render: () => (
<div className="w-72">
<ListboxTextWithHooks />
</div>
),
};
render: ({ label, description, placeholder, width }) => (
<div style={{ width }}>
<ListboxTextWithHooks
label={label}
description={description}
placeholder={placeholder}
/>
</div>
),
}

export const Badge: Story = {
render: () => (
<div className="w-72">
<ListboxBadgeWithHooks />
</div>
),
};
render: ({ label, description, placeholder, width, disabled }) => (
<div style={{ width }}>
<ListboxBadgeWithHooks
label={label}
description={description}
placeholder={placeholder}
disabled={disabled}
/>
</div>
),
}

export const Disabled: Story = {
render: () => (
<div className="w-72">
<ListboxBadgeWithHooks disabled />
</div>
),
};
render: ({ label, description, placeholder, width }) => (
<div style={{ width }}>
<ListboxBadgeWithHooks
label={label}
description={description}
placeholder={placeholder}
disabled
/>
</div>
),
}

export const Multiple: Story = {
render: () => (
<div className="w-72">
<ListboxTextWithMultiplePropWithHooks />
</div>
),
};
render: ({ label, description, placeholder, width }) => (
<div style={{ width }}>
<ListboxMultiWithHooks
label={label}
description={description}
placeholder={placeholder}
/>
</div>
),
}
Comment on lines +2 to +230
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses single quotes for string literals, which is inconsistent with the rest of the codebase that predominantly uses double quotes. Update all string literals to use double quotes to maintain consistency with the project's coding style.

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +230
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code omits semicolons at the end of statements, which is inconsistent with the rest of the codebase that uses semicolons. Add semicolons to maintain consistency with the project's coding style.

Copilot uses AI. Check for mistakes.
Loading