Files
web-ui/src/components/Dropdown.stories.tsx
Beatrice Dellacà 29a4e8c2ee
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
add width, v0.1.10
2026-02-23 19:57:19 +01:00

169 lines
4.9 KiB
TypeScript

import { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Dropdown } from './Dropdown';
const choices = [
{ id: 'draft', label: 'Draft' },
{ id: 'review', label: 'In review' },
{ id: 'published', label: 'Published' },
];
const meta = {
title: 'Components/Dropdown',
component: Dropdown,
tags: ['autodocs'],
parameters: {
docs: {
description: {
component:
'Styled select component with label, error state, stacked/inline layout, and multiple sizes.',
},
},
},
argTypes: {
label: {
description: 'Label text shown above (stacked) or on the left (inline).',
control: 'text',
table: { type: { summary: 'string' } },
},
value: {
description: 'Current selected value (must match one `choices[].id`).',
control: 'text',
table: { type: { summary: 'string' } },
},
choices: {
description: 'Options list in `{ id: string; label: string }` format.',
control: 'object',
table: { type: { summary: 'Array<{ id: string; label: string }>' } },
},
size: {
description: 'Control size.',
options: ['sm', 'md', 'lg', 'full'],
control: 'inline-radio',
table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } },
},
width: {
description: 'Control width constraint.',
options: ['sm', 'md', 'lg', 'full'],
control: 'inline-radio',
table: { type: { summary: "'sm' | 'md' | 'lg' | 'full'" } },
},
layout: {
description: 'Label/control layout mode.',
options: ['stacked', 'inline'],
control: 'inline-radio',
table: { type: { summary: "'stacked' | 'inline'" } },
},
disabled: {
description: 'Disables the field.',
control: 'boolean',
table: { type: { summary: 'boolean' } },
},
required: {
description: 'Sets the native HTML `required` attribute.',
control: 'boolean',
table: { type: { summary: 'boolean' } },
},
error: {
description: 'Error message shown below the field.',
control: 'text',
table: { type: { summary: 'string' } },
},
className: {
description: 'Extra CSS classes for the wrapper.',
control: 'text',
table: { type: { summary: 'string' } },
},
selectClassName: {
description: 'Extra CSS classes for the `<select>` element.',
control: 'text',
table: { type: { summary: 'string' } },
},
onChange: {
description: 'Callback fired with the newly selected value.',
action: 'changed',
table: { type: { summary: '(value: string) => void' } },
},
},
args: {
label: 'Status',
value: 'draft',
choices,
size: 'md',
width: 'md',
layout: 'stacked',
},
} satisfies Meta<typeof Dropdown>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Stacked: Story = {
render: (args) => {
const [value, setValue] = useState(args.value);
return (
<Dropdown
{...args}
value={value}
onChange={(next) => {
setValue(next);
args.onChange?.(next);
}}
/>
);
},
};
export const Inline: Story = {
args: {
layout: 'inline',
size: 'sm',
},
render: (args) => {
const [value, setValue] = useState(args.value);
return (
<Dropdown
{...args}
value={value}
onChange={(next) => {
setValue(next);
args.onChange?.(next);
}}
/>
);
},
};
export const Disabled: Story = {
args: {
disabled: true,
},
};
export const WithError: Story = {
args: {
error: 'Please choose a valid status',
},
};
export const SizeMatrix: Story = {
render: (args) => {
const [value, setValue] = useState(args.value);
return (
<div className="grid grid-cols-1 gap-3">
<Dropdown {...args} value={value} size="sm" label="Small" onChange={setValue} />
<Dropdown {...args} value={value} size="md" label="Medium" onChange={setValue} />
<Dropdown {...args} value={value} size="lg" label="Large" onChange={setValue} />
<Dropdown
{...args}
value={value}
size="full"
width="full"
label="Full"
onChange={setValue}
/>
</div>
);
},
};