重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
storybook-args-controls by thebushidocollective/han
npx skills add https://github.com/thebushidocollective/han --skill storybook-args-controls配置交互式控件和参数,使故事变得动态且可探索,让设计师和开发者能够实时测试组件的各种变化。
Args 是组件的输入,Storybook 会跟踪这些输入并使其具有交互性:
export const Primary: Story = {
args: {
label: 'Button',
primary: true,
size: 'medium',
onClick: () => alert('clicked'),
},
};
ArgTypes 定义了关于 args 的元数据,包括控件类型和文档说明:
const meta = {
component: Button,
argTypes: {
backgroundColor: {
control: 'color',
description: '按钮的背景颜色',
},
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large'],
description: '尺寸变体',
},
onClick: {
action: 'clicked',
},
},
} satisfies Meta<typeof Button>;
Storybook 为不同的数据类型提供了多种控件类型:
text - 字符串输入number - 带验证的数字输入广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
boolean - 复选框切换color - 颜色选择器date - 日期选择器select - 下拉菜单radio - 单选按钮range - 带最小/最大值的滑块object - JSON 编辑器array - 数组编辑器让 Storybook 根据 TypeScript 类型自动生成控件:
interface ButtonProps {
label: string;
primary?: boolean;
size?: 'small' | 'medium' | 'large';
backgroundColor?: string;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({ ... }) => { ... };
const meta = {
component: Button,
// 从 ButtonProps 推断控件
} satisfies Meta<typeof Button>;
为了更好地用户体验,覆盖自动推断的控件:
const meta = {
component: ColorPicker,
argTypes: {
color: {
control: 'color', // 覆盖默认的文本输入
},
opacity: {
control: { type: 'range', min: 0, max: 1, step: 0.1 },
},
preset: {
control: 'select',
options: ['primary', 'secondary', 'success', 'warning', 'danger'],
},
},
} satisfies Meta<typeof ColorPicker>;
在 Actions 面板中跟踪事件回调:
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),
},
};
在元数据级别提供默认参数:
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>;
添加描述以帮助用户理解每个参数:
const meta = {
component: Tooltip,
argTypes: {
placement: {
control: 'select',
options: ['top', 'right', 'bottom', 'left'],
description: '工具提示相对于其触发器的位置',
table: {
defaultValue: { summary: 'top' },
type: { summary: 'string' },
},
},
delay: {
control: { type: 'number', min: 0, max: 2000, step: 100 },
description: '显示工具提示前的延迟(毫秒)',
},
},
} 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: '图表数据点',
},
options: {
control: 'object',
description: '图表配置',
},
},
} 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,
},
},
};
根据其他参数值隐藏不相关的控件:
const meta = {
component: Input,
argTypes: {
type: {
control: 'select',
options: ['text', 'number', 'email', 'password'],
},
min: {
control: 'number',
if: { arg: 'type', eq: 'number' }, // 仅对数字输入显示
},
max: {
control: 'number',
if: { arg: 'type', eq: 'number' },
},
showPasswordToggle: {
control: 'boolean',
if: { arg: 'type', eq: 'password' },
},
},
} satisfies Meta<typeof Input>;
禁用不应被编辑的属性的控件:
const meta = {
component: DataTable,
argTypes: {
data: {
control: false, // 禁用控件(改用 args)
},
onSort: {
table: { disable: true }, // 从文档表格中隐藏
},
},
} satisfies Meta<typeof DataTable>;
将控件组织到逻辑类别中:
const meta = {
component: Modal,
argTypes: {
// 外观
title: {
control: 'text',
table: { category: 'Appearance' },
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
table: { category: 'Appearance' },
},
// 行为
closeOnEscape: {
control: 'boolean',
table: { category: 'Behavior' },
},
closeOnOverlayClick: {
control: 'boolean',
table: { category: 'Behavior' },
},
// 事件
onClose: {
action: 'closed',
table: { category: 'Events' },
},
},
} satisfies Meta<typeof Modal>;
为专门的输入创建自定义控件:
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 })}
/>
);
},
};
以编程方式生成 argTypes:
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>;
// 错误示例
export const Example: Story = {
render: (args) => <Button {...args} label="Hardcoded" />,
};
// 正确示例
export const Example: Story = {
args: {
label: 'Hardcoded',
},
};
// 错误示例 - 控件中包含大型模拟数据
export const WithData: Story = {
args: {
items: Array.from({ length: 1000 }, (_, i) => ({ id: i, ... })),
},
argTypes: {
items: { control: 'object' }, // 不要使其可编辑
},
};
// 正确示例 - 对模拟数据禁用控件
export const WithData: Story = {
args: {
items: mockLargeDataset,
},
argTypes: {
items: { control: false },
},
};
// 错误示例
export const Story1: Story = {
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
};
export const Story2: Story = {
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
};
// 正确示例 - 在元数据级别定义
const meta = {
component: Button,
argTypes: {
size: { control: 'select', options: ['small', 'large'] },
},
} satisfies Meta<typeof Button>;
每周安装次数
63
代码仓库
GitHub 星标数
128
首次出现
2026年1月22日
安全审计
已安装于
gemini-cli54
codex54
opencode53
github-copilot49
kimi-cli47
cursor47
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>;
Weekly Installs
63
Repository
GitHub Stars
128
First Seen
Jan 22, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli54
codex54
opencode53
github-copilot49
kimi-cli47
cursor47
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
125,600 周安装