From storybook
Configures interactive args and controls for Storybook stories to enable dynamic component demos with TypeScript inference, custom types, and event actions.
npx claudepluginhub thebushidocollective/han --plugin storybookThis skill is limited to using the following tools:
Configure interactive controls and args to make stories dynamic and explorable, allowing designers and developers to test component variations in real-time.
Creates or modifies Storybook stories in CSF3 format for React components, showcasing variations with TypeScript type safety and ensuring builds succeed.
Writes React Native Storybook stories in CSF for .stories.tsx files, adding stories to components, configuring argTypes, decorators, parameters, controls, actions, and addons.
Generates CSF3 Storybook stories for React/Vue components with variant coverage and state matrices. Use when creating stories or scanning for missing ones.
Share bugs, ideas, or general feedback.
Configure interactive controls and args to make stories dynamic and explorable, allowing designers and developers to test component variations in real-time.
Args are inputs to components that Storybook tracks and makes interactive:
export const Primary: Story = {
args: {
label: 'Button',
primary: true,
size: 'medium',
onClick: () => alert('clicked'),
},
};
ArgTypes define metadata about args, including control types and documentation:
const meta = {
component: Button,
argTypes: {
backgroundColor: {
control: 'color',
description: 'Background color of the button',
},
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large'],
description: 'Size variant',
},
onClick: {
action: 'clicked',
},
},
} satisfies Meta<typeof Button>;
Storybook provides various control types for different data types:
text - String inputnumber - Number input with validationboolean - Checkbox togglecolor - Color pickerdate - Date pickerselect - Dropdown menuradio - Radio buttonsrange - Slider with min/maxobject - JSON editorarray - Array editorLet Storybook auto-generate controls from TypeScript types:
interface ButtonProps {
label: string;
primary?: boolean;
size?: 'small' | 'medium' | 'large';
backgroundColor?: string;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({ ... }) => { ... };
const meta = {
component: Button,
// Controls inferred from ButtonProps
} satisfies Meta<typeof Button>;
Override auto-inferred controls for better UX:
const meta = {
component: ColorPicker,
argTypes: {
color: {
control: 'color', // Override default text input
},
opacity: {
control: { type: 'range', min: 0, max: 1, step: 0.1 },
},
preset: {
control: 'select',
options: ['primary', 'secondary', 'success', 'warning', 'danger'],
},
},
} satisfies Meta<typeof ColorPicker>;
Track event callbacks in the Actions panel:
const meta = {
component: Form,
argTypes: {
onSubmit: { action: 'submitted' },
onChange: { action: 'changed' },
onError: { action: 'error occurred' },
},
} satisfies Meta<typeof Form>;
export const Default: Story = {
args: {
onSubmit: (data) => console.log('Form data:', data),
},
};
Provide default args at the meta level:
const meta = {
component: Slider,
args: {
min: 0,
max: 100,
step: 1,
value: 50,
},
argTypes: {
value: {
control: { type: 'range', min: 0, max: 100, step: 1 },
},
},
} satisfies Meta<typeof Slider>;
Add descriptions to help users understand each arg:
const meta = {
component: Tooltip,
argTypes: {
placement: {
control: 'select',
options: ['top', 'right', 'bottom', 'left'],
description: 'Position of the tooltip relative to its trigger',
table: {
defaultValue: { summary: 'top' },
type: { summary: 'string' },
},
},
delay: {
control: { type: 'number', min: 0, max: 2000, step: 100 },
description: 'Delay in milliseconds before showing the tooltip',
},
},
} satisfies Meta<typeof Tooltip>;
type ButtonVariant = 'primary' | 'secondary' | 'danger';
const meta = {
component: Button,
argTypes: {
variant: {
control: 'radio',
options: ['primary', 'secondary', 'danger'] satisfies ButtonVariant[],
},
},
} satisfies Meta<typeof Button>;
const meta = {
component: Chart,
argTypes: {
data: {
control: 'object',
description: 'Chart data points',
},
options: {
control: 'object',
description: 'Chart configuration',
},
},
} satisfies Meta<typeof Chart>;
export const Default: Story = {
args: {
data: [
{ x: 0, y: 10 },
{ x: 1, y: 20 },
{ x: 2, y: 15 },
],
options: {
showLegend: true,
animate: true,
},
},
};
Hide irrelevant controls based on other arg values:
const meta = {
component: Input,
argTypes: {
type: {
control: 'select',
options: ['text', 'number', 'email', 'password'],
},
min: {
control: 'number',
if: { arg: 'type', eq: 'number' }, // Only show for number inputs
},
max: {
control: 'number',
if: { arg: 'type', eq: 'number' },
},
showPasswordToggle: {
control: 'boolean',
if: { arg: 'type', eq: 'password' },
},
},
} satisfies Meta<typeof Input>;
Disable controls for props that shouldn't be editable:
const meta = {
component: DataTable,
argTypes: {
data: {
control: false, // Disable control (use args instead)
},
onSort: {
table: { disable: true }, // Hide from docs table
},
},
} satisfies Meta<typeof DataTable>;
Organize controls into logical categories:
const meta = {
component: Modal,
argTypes: {
// Appearance
title: {
control: 'text',
table: { category: 'Appearance' },
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
table: { category: 'Appearance' },
},
// Behavior
closeOnEscape: {
control: 'boolean',
table: { category: 'Behavior' },
},
closeOnOverlayClick: {
control: 'boolean',
table: { category: 'Behavior' },
},
// Events
onClose: {
action: 'closed',
table: { category: 'Events' },
},
},
} satisfies Meta<typeof Modal>;
Create custom controls for specialized inputs:
import { useArgs } from '@storybook/preview-api';
const meta = {
component: GradientPicker,
argTypes: {
gradient: {
control: {
type: 'object',
},
},
},
} satisfies Meta<typeof GradientPicker>;
export const Custom: Story = {
render: (args) => {
const [{ gradient }, updateArgs] = useArgs();
return (
<GradientPicker
{...args}
gradient={gradient}
onChange={(newGradient) => updateArgs({ gradient: newGradient })}
/>
);
},
};
Generate argTypes programmatically:
const themes = ['light', 'dark', 'system'] as const;
const meta = {
component: ThemeProvider,
argTypes: {
theme: {
control: 'select',
options: themes,
mapping: Object.fromEntries(
themes.map(theme => [theme, theme])
),
},
},
} satisfies Meta<typeof ThemeProvider>;
// Bad
export const Example: Story = {
render: (args) => <Button {...args} label="Hardcoded" />,
};
// Good
export const Example: Story = {
args: {
label: 'Hardcoded',
},
};
// Bad - Large mock data in controls
export const WithData: Story = {
args: {
items: Array.from({ length: 1000 }, (_, i) => ({ id: i, ... })),
},
argTypes: {
items: { control: 'object' }, // Don't make editable
},
};
// Good - Disable control for mock data
export const WithData: Story = {
args: {
items: mockLargeDataset,
},
argTypes: {
items: { control: false },
},
};
// Bad
export const Story1: Story = {
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
};
export const Story2: Story = {
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
};
// Good - Define at meta level
const meta = {
component: Button,
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
} satisfies Meta<typeof Button>;