Storybook参数与控制配置Skill storybook-args-controls

这个技能用于在Storybook中配置交互式参数和控件,使组件故事动态化和可探索,便于设计师和开发人员实时测试组件变体。关键词:Storybook, 参数, 控制, 交互式, 组件开发, 前端测试, UI组件, 动态演示。

前端开发 0 次安装 0 次浏览 更新于 3/25/2026

名称: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:使用参数测试交互