名称:storybook-args-controls 用户可调用:false 描述:用于配置Storybook故事中的交互式控件和参数。帮助创建动态、可探索的组件演示,并具有适当的类型约束。 允许工具:
- 读取
- 写入
- 编辑
- Bash
- Grep
- Glob
Storybook - 参数与控制
配置交互式控件和参数,使故事动态化和可探索,允许设计师和开发人员实时测试组件变体。
关键概念
参数
参数是组件的输入,Storybook跟踪并使其交互:
export const Primary: Story = {
args: {
label: '按钮',
primary: true,
size: '中等',
onClick: () => alert('已点击'),
},
};
参数类型
参数类型定义参数的元数据,包括控件类型和文档:
const meta = {
component: Button,
argTypes: {
backgroundColor: {
control: 'color',
description: '按钮的背景颜色',
},
size: {
control: { type: 'select' },
options: ['小', '中等', '大'],
description: '大小变体',
},
onClick: {
action: '已点击',
},
},
} satisfies Meta<typeof Button>;
控件类型
Storybook提供各种控件类型用于不同数据类型:
text- 文本输入number- 数字输入带验证boolean- 复选框切换color- 颜色选择器date- 日期选择器select- 下拉菜单radio- 单选按钮range- 滑块带最小/最大值object- JSON编辑器array- 数组编辑器
最佳实践
1. 从TypeScript推断控件
让Storybook从TypeScript类型自动生成控件:
interface ButtonProps {
label: string;
primary?: boolean;
size?: '小' | '中等' | '大';
backgroundColor?: string;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({ ... }) => { ... };
const meta = {
component: Button,
// 从ButtonProps推断控件
} satisfies Meta<typeof Button>;
2. 根据需要自定义控件类型
覆盖自动推断的控件以改进用户体验:
const meta = {
component: ColorPicker,
argTypes: {
color: {
control: 'color', // 覆盖默认文本输入
},
opacity: {
control: { type: 'range', min: 0, max: 1, step: 0.1 },
},
preset: {
control: 'select',
options: ['主要', '次要', '成功', '警告', '危险'],
},
},
} satisfies Meta<typeof ColorPicker>;
3. 使用动作处理事件处理器
在动作面板中跟踪事件回调:
const meta = {
component: Form,
argTypes: {
onSubmit: { action: '已提交' },
onChange: { action: '已更改' },
onError: { action: '发生错误' },
},
} satisfies Meta<typeof Form>;
export const Default: Story = {
args: {
onSubmit: (data) => console.log('表单数据:', data),
},
};
4. 设置合理的默认值
在元级别提供默认参数:
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>;
5. 文档化参数
添加描述以帮助用户理解每个参数:
const meta = {
component: Tooltip,
argTypes: {
placement: {
control: 'select',
options: ['顶部', '右侧', '底部', '左侧'],
description: '工具提示相对于触发器的位置',
table: {
defaultValue: { summary: '顶部' },
type: { summary: '字符串' },
},
},
delay: {
control: { type: 'number', min: 0, max: 2000, step: 100 },
description: '显示工具提示前的延迟(毫秒)',
},
},
} satisfies Meta<typeof Tooltip>;
常见模式
枚举/联合类型控件
type ButtonVariant = '主要' | '次要' | '危险';
const meta = {
component: Button,
argTypes: {
variant: {
control: 'radio',
options: ['主要', '次要', '危险'] 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: ['文本', '数字', '电子邮件', '密码'],
},
min: {
control: 'number',
if: { arg: 'type', eq: '数字' }, // 仅对数字输入显示
},
max: {
control: 'number',
if: { arg: 'type', eq: '数字' },
},
showPasswordToggle: {
control: 'boolean',
if: { arg: 'type', eq: '密码' },
},
},
} satisfies Meta<typeof Input>;
禁用控件
禁用不应编辑的属性控件:
const meta = {
component: DataTable,
argTypes: {
data: {
control: false, // 禁用控件(使用参数代替)
},
onSort: {
table: { disable: true }, // 从文档表格隐藏
},
},
} satisfies Meta<typeof DataTable>;
分组控件
将控件组织成逻辑类别:
const meta = {
component: Modal,
argTypes: {
// 外观
title: {
control: 'text',
table: { category: '外观' },
},
size: {
control: 'select',
options: ['小', '中等', '大'],
table: { category: '外观' },
},
// 行为
closeOnEscape: {
control: 'boolean',
table: { category: '行为' },
},
closeOnOverlayClick: {
control: 'boolean',
table: { category: '行为' },
},
// 事件
onClose: {
action: '已关闭',
table: { category: '事件' },
},
},
} 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 })}
/>
);
},
};
动态参数类型
以编程方式生成参数类型:
const themes = ['浅色', '深色', '系统'] 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="硬编码" />,
};
// 正确
export const Example: Story = {
args: {
label: '硬编码',
},
};
❌ 不要对静态数据使用控件
// 错误 - 控件中的大型模拟数据
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: ['小', '大'] },
},
};
export const Story2: Story = {
argTypes: {
size: { control: 'select', options: ['小', '大'] },
},
};
// 正确 - 在元级别定义
const meta = {
component: Button,
argTypes: {
size: { control: 'select', options: ['小', '大'] },
},
} satisfies Meta<typeof Button>;
相关技能
- storybook-story-writing:编写结构良好的故事
- storybook-component-documentation:从控件自动生成文档
- storybook-play-functions:使用参数测试交互