文档
Storybook 文档

Args

观看视频教程

一个 story 是一个具有一组参数的组件,这些参数定义了组件应如何渲染。“Args”是 Storybook 定义这些参数的机制,它们在一个 JavaScript 对象中。Args 可用于动态更改 props、slots、styles、inputs 等。它允许 Storybook 及其插件实时编辑组件。你*不必*修改你的底层组件代码来使用 args。

当一个 arg 的值发生变化时,组件会重新渲染,这使得你可以通过影响 args 的插件在 Storybook 的 UI 中与组件进行交互。

要了解如何以及为何要编写 story,请参阅介绍。要了解 args 的工作原理,请继续阅读。

Args 对象

args 对象可以在story组件全局级别定义。它是一个 JSON 可序列化对象,由字符串键组成,键的匹配值类型是有效的,可以传递到您的框架的组件中。

Story args

要定义单个 story 的 args,请使用 args CSF story 键

Button.stories.ts|tsx
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

这些 args 只会应用于它们所关联的 story,尽管你可以通过 JavaScript 对象复用重用它们。

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 = {
  component: Button,
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};
 
export const PrimaryLongName: Story = {
  args: {
    ...Primary.args,
    label: 'Primary with a really long name',
  },
};

在上面的例子中,我们使用了 ES 2015 的对象展开语法。

Component args

你也可以在组件级别定义 args,它们将应用于组件的所有 stories,除非你覆盖它们。要做到这一点,请在 default CSF 导出上使用 args 键。

Button.stories.ts|tsx
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  //👇 Creates specific argTypes
  argTypes: {
    backgroundColor: { control: 'color' },
  },
  args: {
    //👇 Now all Button stories will be primary.
    primary: true,
  },
} satisfies Meta<typeof Button>;
 
export default meta;

Global args

你也可以在全局级别定义 args,它们将应用于每个组件的 stories,除非你覆盖它们。要做到这一点,请在 preview.js|ts 的默认导出中定义 args 属性。

.storybook/preview.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  // The default value of the theme arg for all stories
  args: { theme: 'light' },
};
 
export default preview;

对于大多数全局 args 的用法,globals 是定义全局应用设置(例如主题)的更好工具。使用 globals 可以让用户通过工具栏菜单更改值。

Args composition

你可以将 story 的参数分开,以便在其他 stories 中组合。以下是如何为同一个组件的多个 stories 组合 args:

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 = {
  component: Button,
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};
 
export const Secondary: Story = {
  args: {
    ...Primary.args,
    primary: false,
  },
};

如果你发现自己为组件的大部分 stories 重用了相同的 args,你应该考虑使用 组件级别的 args

当编写由其他组件组成的复合组件的 stories 时,args 非常有用。复合组件通常会将它们的参数不变地传递给它们的子组件,同样,它们的 stories 也可以是它们的子组件 stories 的组合。使用 args,你可以直接组合参数。

Page.stories.ts|tsx
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Page } from './Page';
 
//👇 Imports all Header stories
import * as HeaderStories from './Header.stories';
 
const meta = {
  component: Page,
} satisfies Meta<typeof Page>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const LoggedIn: Story = {
  args: {
    ...HeaderStories.LoggedIn.args,
  },
};

Args 可以修改组件的任何方面

你可以在你的 stories 中使用 args 来配置组件的外观,类似于在应用程序中所做的那样。例如,这是你可以使用 footer arg 来填充子组件的方法。

Page.stories.ts|tsx
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Page } from './Page';
 
type PagePropsAndCustomArgs = React.ComponentProps<typeof Page> & { footer?: string };
 
const meta = {
  component: Page,
  render: ({ footer, ...args }) => (
    <Page {...args}>
      <footer>{footer}</footer>
    </Page>
  ),
} satisfies Meta<PagePropsAndCustomArgs>;
export default meta;
 
type Story = StoryObj<typeof meta>;
 
export const CustomFooter = {
  args: {
    footer: 'Built with Storybook',
  },
} satisfies Story;

通过 URL 设置 args

你也可以通过在 URL 中添加一个 args 查询参数来覆盖活动 story 的初始 args 集。通常,你会使用 Controls 来处理这个问题。例如,你可以通过 Storybook 的 URL 设置一个 sizestyle arg。

?path=/story/avatar--default&args=style:rounded;size:100

为了防止 XSS 攻击,URL 中提供的 arg 的键和值仅限于字母数字字符、空格、下划线和连字符。任何其他类型都将被忽略并从 URL 中删除,但你仍然可以在 Controls 面板和你的 story 中使用它们。

args 参数始终是一组 key: value 对,由分号 ; 分隔。值将被强制转换为它们各自的 argTypes(这可能是自动推断的)。对象和数组是支持的。特殊值 nullundefined 可以通过添加感叹号 ! 前缀来设置。例如,args=obj.key:val;arr[0]:one;arr[1]:two;nil:!null 将被解释为

{
  obj: { key: 'val' },
  arr: ['one', 'two'],
  nil: null
}

类似地,日期和颜色也有特殊格式。Date 对象将编码为 !date(value),其中 value 表示为 ISO 日期字符串。颜色编码为 !hex(value)!rgba(value)!hsla(value)。请注意,rgb(a) 和 hsl(a) 在 URL 中不应包含空格或百分号。

通过 URL 指定的 args 将扩展并覆盖 story 上设置的任何 args 的默认值。

从 story 内部设置 args

交互式组件通常需要由其包含的组件或页面来控制,以便响应事件、修改其状态并将这些更改反映在 UI 中。例如,当用户切换开关组件时,开关应被选中,并且 Storybook 中显示的 arg 应反映该更改。要实现这一点,你可以使用从 storybook/preview-api 导出的 useArgs API。

my-component/component.stories.ts|tsx
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useArgs } from 'storybook/preview-api';
 
import { Checkbox } from './checkbox';
 
const meta = {
  title: 'Inputs/Checkbox',
  component: Checkbox,
} satisfies Meta<typeof Checkbox>;
export default meta;
 
type Story = StoryObj<typeof Checkbox>;
 
export const Example = {
  args: {
    isChecked: false,
    label: 'Try Me!',
  },
  /**
   * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name.
   * If you are not concerned with linting, you may use an arrow function.
   */
  render: function Render(args) {
    const [{ isChecked }, updateArgs] = useArgs();
 
    function onChange() {
      updateArgs({ isChecked: !isChecked });
    }
 
    return <Checkbox {...args} onChange={onChange} isChecked={isChecked} />;
  },
} satisfies Story;

映射到复杂 arg 值

JSX 元素等复杂值无法序列化到管理器(例如 Controls 面板)或与 URL 同步。Arg 值可以通过 argTypes 中的 mapping 属性从简单的字符串“映射”到复杂类型,以解决此限制。它适用于任何 arg,但在与 select 控件类型一起使用时最有用。

Example.stories.ts|tsx
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
 
import { Example } from './Example';
 
const meta = {
  component: Example,
  argTypes: {
    label: {
      control: { type: 'select' },
      options: ['Normal', 'Bold', 'Italic'],
      mapping: {
        Bold: <b>Bold</b>,
        Italic: <i>Italic</i>,
      },
    },
  },
} satisfies Meta<typeof Example>;
 
export default meta;

请注意,mapping 不必是详尽的。如果 arg 值不是 mapping 的属性,则直接使用该值。 mapping 中的键始终对应于 arg *值*,而不是它们在 options 数组中的索引。

在插件中使用 Args

如果你正在编写一个插件,并且希望读取或更新 args,请使用从 storybook/manager-api 导出的 useArgs hook。

my-addon/src/manager.js|ts
import { useArgs } from 'storybook/manager-api';
 
const [args, updateArgs, resetArgs] = useArgs();
 
// To update one or more args:
updateArgs({ key: 'value' });
 
// To reset one (or more) args:
resetArgs((argNames: ['key']));
 
// To reset all args
resetArgs();