文档
Storybook 文档

控件

观看视频教程

Storybook 控件为您提供了一个图形化用户界面,可以动态地与组件的参数进行交互,而无需编写代码。它会在您的组件示例(“故事”)旁边创建一个插件面板,以便您可以实时编辑它们。

控件不需要对您的组件进行任何修改。控件的故事是

  • 方便的。根据 React/Vue/Angular 等组件自动生成控件。
  • 可移植的。在文档、测试甚至设计中重用您的交互式故事。
  • 丰富的。自定义控件和交互式数据以满足您的特定需求。

要使用控件插件,您需要使用 参数 编写您的故事。Storybook 将根据您的参数及其可以推断出的关于您的组件的信息自动生成 UI 控件。但是,您仍然可以使用 argTypes 进一步配置控件,请参见下文。

如果您使用的是 Storybook 6 之前的旧版样式的故事,请查看 参数和控件迁移指南,了解如何转换您现有的参数故事。

选择控件类型

默认情况下,Storybook 会根据每个参数的初始值选择一个控件。这将与特定参数类型(例如 booleanstring)配合良好。要启用它们,请将 component 注解添加到故事文件的默认导出中,它将用于推断控件并使用 react-docgen(一个 React 组件的文档生成器,也包括对 TypeScript 的一流支持)为您的组件自动生成匹配的 argTypes

Button.stories.ts|tsx
import type { Meta } from '@storybook/react';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
};
 
export default meta;

例如,假设您的故事上有一个 variant 参数,它应该是 primarysecondary

Button.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
};
 
export default meta;
type Story = StoryObj<typeof Button>;
 
export const Primary: Story = {
  args: {
    variant: 'primary',
  },
};

默认情况下,Storybook 会为 variant 参数呈现一个自由文本输入

Essential addon Controls using a string

只要您在自动生成的文本控件中输入有效的字符串,它就可以工作。但是,鉴于组件仅接受 primarysecondary 作为变体,因此它不是我们场景的最佳 UI。让我们用 Storybook 的单选组件替换它。

我们可以通过为 variant 属性声明自定义 argType 来指定使用哪些控件。ArgTypes 编码参数的基本元数据,例如参数的名称、描述和 defaultValue。这些内容会由 Storybook 文档自动填充。

ArgTypes 还可以包含用户可以覆盖的任意注释。由于 variant 是组件的属性,因此让我们将该注释放在默认导出上。

Button.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    variant: {
      options: ['primary', 'secondary'],
      control: { type: 'radio' },
    },
  },
};
 
export default meta;

ArgTypes 是一项强大的功能,可用于自定义故事的控件。有关更多信息,请参阅有关使用 argTypes 注释 自定义控件 的文档。

这将输入替换为单选组,以获得更直观的体验。

Essential Control addon with a radio group

自定义控件类型匹配器

可以使用 正则表达式 从参数的名称自动推断控件,但目前仅适用于颜色选择器和日期选择器控件。如果您已使用 Storybook CLI 设置项目,则它应该已在 .storybook/preview.js 中自动创建了以下默认值

控件默认正则表达式描述
颜色/(background|color)$/i将为匹配它的参数显示颜色选择器 UI
日期/Date$/将为匹配它的参数显示日期选择器 UI

如果您未使用 CLI 设置配置,或者如果要定义自己的模式,请在 controls 参数中使用 matchers 属性

.storybook/preview.ts
// Replace your-framework with the framework you are using (e.g., react, vue3)
import { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
  },
};
 
export default preview;

完全自定义参数

到目前为止,我们仅使用基于我们为其编写故事的组件的自动生成的控件。如果我们正在编写 复杂的故事,我们可能希望为不属于组件的参数添加控件。例如,以下是如何使用 footer 参数填充子组件

Page.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { Page } from './Page';
 
type PagePropsAndCustomArgs = React.ComponentProps<typeof Page> & { footer?: string };
 
const meta: Meta<PagePropsAndCustomArgs> = {
  component: Page,
  render: ({ footer, ...args }) => (
    <Page {...args}>
      <footer>{footer}</footer>
    </Page>
  ),
};
export default meta;
 
type Story = StoryObj<PagePropsAndCustomArgs>;
 
export const CustomFooter: Story = {
  args: {
    footer: 'Built with Storybook',
  },
};

默认情况下,Storybook 会为所有满足以下条件的参数添加控件:

使用 argTypes,您可以更改每个控件的显示和行为。

处理复杂值

在处理非原始值时,您会发现遇到一些限制。最明显的问题是并非所有值都可以在 URL 的 args 参数中表示,从而失去了共享和深度链接到此类状态的能力。除此之外,诸如 JSX 之类的复杂值无法在管理器(例如,Controls 附加组件)和预览(您的故事)之间同步。

解决此问题的一种方法是使用原始值(例如,字符串)作为参数值,并添加自定义的 render 函数,在渲染之前将其转换为复杂的对应项。这并不是最好的方法(见下文),但肯定是最灵活的。

YourComponent.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { YourComponent } from './your-component';
 
const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
  //👇 Creates specific argTypes with options
  argTypes: {
    propertyA: {
      options: ['Item One', 'Item Two', 'Item Three'],
      control: { type: 'select' }, // Automatically inferred when 'options' is defined
    },
    propertyB: {
      options: ['Another Item One', 'Another Item Two', 'Another Item Three'],
    },
  },
};
 
export default meta;
type Story = StoryObj<typeof YourComponent>;
 
const someFunction = (valuePropertyA, valuePropertyB) => {
  // Do some logic here
};
 
export const ExampleStory: Story = {
  render: (args) => {
    const { propertyA, propertyB } = args;
    //👇 Assigns the function result to a variable
    const someFunctionResult = someFunction(propertyA, propertyB);
 
    return <YourComponent {...args} someProperty={someFunctionResult} />;
  },
  args: {
    propertyA: 'Item One',
    propertyB: 'Another Item One',
  },
};

除非您需要函数的灵活性,否则在渲染之前将原始值映射到复杂值的更简单方法是定义一个 mapping;此外,您可以指定 control.labels 来为您的复选框、单选按钮或选择输入配置自定义标签。

Button.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from './icons';
 
const arrows = { ArrowUp, ArrowDown, ArrowLeft, ArrowRight };
 
const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    arrow: {
      options: Object.keys(arrows), // An array of serializable values
      mapping: arrows, // Maps serializable option values to complex arg values
      control: {
        type: 'select', // Type 'select' is automatically inferred when 'options' is defined
        labels: {
          // 'labels' maps option values to string labels
          ArrowUp: 'Up',
          ArrowDown: 'Down',
          ArrowLeft: 'Left',
          ArrowRight: 'Right',
        },
      },
    },
  },
};
 
export default meta;

请注意,mappingcontrol.labels 不必是详尽的。如果当前选定的选项未列出,则按字面原样使用。

从控件创建和编辑故事

Controls 附加组件允许您直接从 Controls 面板创建或编辑故事。

创建新故事

打开故事的 Controls 面板并调整控件的值。然后将这些更改另存为新故事。

如果您正在处理尚未有任何故事的组件,则可以点击侧边栏中的 ➕ 按钮搜索您的组件,并为您创建一个基本故事。

编辑故事

您还可以更新控件的值,然后将更改保存到故事中。故事文件的代码将为您更新。

禁用创建和编辑故事

如果您不想允许从 Controls 面板创建或编辑故事,则可以通过将 disableSaveFromUI 参数设置为 true 来禁用此功能,该参数位于 .storybook/preview.js 文件中的 parameters.controls 参数中。

配置

Controls 附加组件可以通过两种方式进行配置

  • 可以通过控制注释配置单个控件。
  • 可以通过参数配置附加组件的外观。

注释

如上所示,您可以使用 argTypes 字段中组件或故事的“control”注释来配置单个控件。下面是一个简化的示例和表格,其中包含所有可用的控件。

数据类型控件描述
布尔值布尔值提供一个切换按钮,用于在可能的状态之间切换。
argTypes: { active: { control: 'boolean' }}
数字数字提供一个数字输入,以包含所有可能值的范围。
argTypes: { even: { control: { type: 'number', min:1, max:30, step: 2 } }}
范围提供一个范围滑块组件,以包含所有可能的值。
argTypes: { odd: { control: { type: 'range', min: 1, max: 30, step: 3 } }}
对象对象提供一个基于 JSON 的编辑器组件来处理对象的值。
也允许以原始模式进行编辑。
argTypes: { user: { control: 'object' }}
数组对象提供一个基于 JSON 的编辑器组件来处理数组的值。
也允许以原始模式进行编辑。
argTypes: { odd: { control: 'object' }}
文件提供一个文件输入组件,该组件返回一个 URL 数组。
可以进一步自定义以接受特定文件类型。
argTypes: { avatar: { control: { type: 'file', accept: '.png' } }}
枚举单选按钮根据可用选项提供一组单选按钮。
argTypes: { contact: { control: 'radio', options: ['email', 'phone', 'mail'] }}
内联单选按钮根据可用选项提供一组内联单选按钮。
argTypes: { contact: { control: 'inline-radio', options: ['email', 'phone', 'mail'] }}
复选框提供一组复选框组件,用于选择多个选项。
argTypes: { contact: { control: 'check', options: ['email', 'phone', 'mail'] }}
内联复选框提供一组内联复选框组件,用于选择多个选项。
argTypes: { contact: { control: 'inline-check', options: ['email', 'phone', 'mail'] }}
选择提供一个下拉列表组件来处理单值选择。 argTypes: { age: { control: 'select', options: [20, 30, 40, 50] }}
多选提供一个下拉列表,允许选择多个值。 argTypes: { countries: { control: 'multi-select', options: ['USA', 'Canada', 'Mexico'] }}
字符串文本提供一个自由格式的文本输入。
argTypes: { label: { control: 'text' }}
颜色提供一个颜色选择器组件来处理颜色值。
可以另外配置以包含一组颜色预设。
argTypes: { color: { control: { type: 'color', presetColors: ['red', 'green']} }}
日期提供一个日期选择器组件来处理日期选择。 argTypes: { startDate: { control: 'date' }}

当值更改时,date 控件会将日期转换为 UNIX 时间戳。这是一个已知的限制,将在未来的版本中修复。如果您需要表示实际日期,则需要更新故事的实现并将值转换为日期对象。

Gizmo.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { Gizmo } from './Gizmo';
 
const meta: Meta<typeof Gizmo> = {
  component: Gizmo,
  argTypes: {
    canRotate: {
      control: 'boolean',
    },
    width: {
      control: { type: 'number', min: 400, max: 1200, step: 50 },
    },
    height: {
      control: { type: 'range', min: 200, max: 1500, step: 50 },
    },
    rawData: {
      control: 'object',
    },
    coordinates: {
      control: 'object',
    },
    texture: {
      control: {
        type: 'file',
        accept: '.png',
      },
    },
    position: {
      control: 'radio',
      options: ['left', 'right', 'center'],
    },
    rotationAxis: {
      control: 'check',
      options: ['x', 'y', 'z'],
    },
    scaling: {
      control: 'select',
      options: [10, 50, 75, 100, 200],
    },
    label: {
      control: 'text',
    },
    meshColors: {
      control: {
        type: 'color',
        presetColors: ['#ff0000', '#00ff00', '#0000ff'],
      },
    },
    revisionDate: {
      control: 'date',
    },
  },
};
 
export default meta;

除非提供其他配置,否则数值数据类型将默认为 number 控件。

参数

Controls 支持以下配置 参数,全局或每个故事基础上。

显示每个属性的完整文档

由于 Controls 基于与 Storybook Docs 相同的引擎构建,因此它还可以使用扩展参数(默认为 false)在控件旁边显示属性文档。这意味着您可以在控件面板中嵌入完整的 Controls 文档块。描述和默认值渲染可以像文档块一样 自定义

要全局启用扩展模式,请将以下内容添加到 .storybook/preview.js

.storybook/preview.ts
// Replace your-framework with the framework you are using (e.g., react, vue3)
import { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    controls: { expanded: true },
  },
};
 
export default preview;

以下是生成的 UI 的外观

Controls addon expanded

指定初始预设颜色色块

对于 color 控件,您可以指定 presetColors 的数组,在 argTypes 中的 control 上,或作为 controls 命名空间下的参数。

.storybook/preview.ts
// Replace your-framework with the framework you are using (e.g., react, vue3)
import { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  parameters: {
    controls: {
      presetColors: [{ color: '#ff4785', title: 'Coral' }, 'rgba(0, 159, 183, 1)', '#fe4a49'],
    },
  },
};
 
export default preview;

颜色预设可以定义为包含 colortitle 属性的对象,或者一个简单的 CSS 颜色字符串。这些颜色将会作为色板显示在颜色选择器中。当您将鼠标悬停在颜色色板上时,您将能够看到它的标题。如果未指定标题,则默认使用最接近的 CSS 颜色名称。

筛选控件

在某些特定情况下,您可能需要在控件面板中仅显示有限数量的控件,或者除了特定的一组控件之外的所有控件。

为了实现这一点,您可以在 controls 参数中使用可选的 includeexclude 配置字段,您可以将其定义为字符串数组或正则表达式。

考虑以下故事片段

YourComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { YourComponent } from './YourComponent';
 
const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
};
 
export default meta;
type Story = StoryObj<typeof YourComponent>;
 
export const ArrayInclude: Story = {
  parameters: {
    controls: { include: ['foo', 'bar'] },
  },
};
 
export const RegexInclude: Story = {
  parameters: {
    controls: { include: /^hello*/ },
  },
};
 
export const ArrayExclude: Story = {
  parameters: {
    controls: { exclude: ['foo', 'bar'] },
  },
};
 
export const RegexExclude: Story = {
  parameters: {
    controls: { exclude: /^hello*/ },
  },
};

排序控件

默认情况下,控件是无序的,并使用 args 数据处理的顺序 (none)。此外,您可以按参数名称按字母顺序排序 (alpha),或者将必需的参数放在前面 (requiredFirst)。

考虑以下代码片段以强制将必需的参数放在前面

YourComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { YourComponent } from './YourComponent';
 
const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
  parameters: { controls: { sort: 'requiredFirst' } },
};
 
export default meta;

禁用特定属性的控件

除了此处已记录的功能外,还可以为各个属性禁用控件。

假设您想关闭组件故事中名为 foo 的属性的控件。以下示例说明了如何操作

YourComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { YourComponent } from './YourComponent';
 
const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
  argTypes: {
    // foo is the property we want to remove from the UI
    foo: {
      table: {
        disable: true,
      },
    },
  },
};
 
export default meta;

导致 Storybook UI 中发生以下更改

前面的示例还从表格中删除了属性文档。在某些情况下,这没有问题。但是,有时您可能希望在没有控件的情况下呈现属性文档。以下示例说明了如何操作

YourComponent.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { YourComponent } from './YourComponent';
 
const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
  argTypes: {
    // foo is the property we want to remove from the UI
    foo: {
      control: false,
    },
  },
};
 
export default meta;

与其他 Storybook 属性(例如 装饰器)一样,您可以针对更细粒度的案例在故事级别应用相同的模式。

条件控件

在某些情况下,能够根据另一个控件的值有条件地排除控件非常有用。Controls 支持这些用例的基本版本,使用 if,它可以采用简单的查询对象来确定是否包含控件。

考虑一个“高级”设置的集合,只有当用户切换“高级”切换按钮时才会显示。

Button.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    label: { control: 'text' }, // Always shows the control
    advanced: { control: 'boolean' },
    // Only enabled if advanced is true
    margin: { control: 'number', if: { arg: 'advanced' } },
    padding: { control: 'number', if: { arg: 'advanced' } },
    cornerRadius: { control: 'number', if: { arg: 'advanced' } },
  },
};
 
export default meta;

或者考虑一个约束条件,如果用户设置了一个控件的值,则用户无法设置另一个值。

Button.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    // Button can be passed a label or an image, not both
    label: {
      control: 'text',
      if: { arg: 'image', truthy: false },
    },
    image: {
      control: { type: 'select', options: ['foo.jpg', 'bar.jpg'] },
      if: { arg: 'label', truthy: false },
    },
  },
};
 
export default meta;

查询对象必须包含 argglobal 目标

字段类型含义
arg字符串要测试的参数的 ID。
global字符串要测试的全局变量的 ID。

它还可以包含最多一个以下运算符

运算符类型含义
truthy布尔值目标值是否为真值?
exists布尔值目标值是否已定义?
eqany目标值是否等于提供的值?
neqany目标值是否不等于提供的值?

如果未提供运算符,则等效于 { truthy: true }

API

参数

此插件向 Storybook 贡献了以下 参数,位于 controls 命名空间下

disable

类型:boolean

禁用此插件的行为。如果您希望为整个 Storybook 禁用此插件,则应在注册 addon-essentials 时执行此操作。有关更多信息,请参阅 基本插件的文档

此参数最有用,因为它允许在更具体的级别覆盖。例如,如果此参数在项目级别设置为 true,则可以通过在元数据(组件)或故事级别将其设置为 false 来重新启用它。

exclude

类型:string[] | RegExp

指定要从 Controls 插件面板中排除的属性。任何名称与正则表达式匹配或属于数组一部分的属性都将被排除在外。请参阅上面 用法示例

expanded

类型:boolean

在 Controls 插件面板中显示每个属性的完整文档,包括描述和默认值。请参阅上面 用法示例

include

类型:string[] | RegExp

指定要包含在 Controls 插件面板中的属性。任何名称与正则表达式不匹配或不属于数组一部分的属性都将被排除在外。请参阅上面 用法示例

presetColors

类型:(string | { color: string; title?: string })[]

指定颜色选择器控件的预设颜色样本。颜色值可以是任何有效的 CSS 颜色。请参阅上面使用示例

sort

类型:'none' | 'alpha' | 'requiredFirst'

默认值:'none'

指定控件的排序方式。

  • none:未排序,按参数类型处理的顺序显示
  • alpha:按参数类型的名称按字母顺序排序
  • requiredFirst:与alpha相同,但所有必需的参数类型都先显示

disableSaveFromUI

类型:boolean

默认值:false

禁用从“控件”面板创建或编辑故事的功能。