文档
Storybook Docs

Decorators

Decorator 是一种将 Story 包装在额外的“渲染”功能中的方法。许多插件通过定义 decorators 来增强您的 Stories,以提供额外的渲染功能或收集有关您的 Story 如何渲染的详细信息。

在编写 Stories 时,decorators 通常用于用额外的标记或模拟的上下文来包装 Stories。

用额外的标记包装 Stories

有些组件需要一个“载体”才能以有用的方式进行渲染。例如,如果一个组件一直延伸到其边缘,您可能希望在 Storybook 中对其进行间距处理。使用 decorator 为组件的所有 Stories 添加间距。

Story without padding

YourComponent.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 { YourComponent } from './YourComponent';
 
const meta = {
  component: YourComponent,
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
} satisfies Meta<typeof YourComponent>;
 
export default meta;

Story with padding

用于模拟的“上下文”

Decorator 函数的第二个参数是 **story context**,它包含以下属性:

  • args - Story 的参数。您可以在 decorators 中使用某些 args,并将它们放置在 Story 实现本身中。
  • argTypes- Storybook 的 argTypes 允许您自定义和微调您的 Story args
  • globals - Storybook 范围内的 globals。特别是,您可以使用 toolbar 功能,让您使用 Storybook 的 UI 来更改这些值。
  • hooks - Storybook 的 API hooks(例如 useArgs)。
  • parameters- Story 的静态元数据,最常用于控制 Storybook 的功能和插件的行为。
  • viewMode- Storybook 当前活动窗口(例如,canvas、docs)。

此上下文可用于根据 Story 的参数或其他元数据调整 decorator 的行为。例如,您可以创建一个 decorator,通过定义 parameters.pageLayout = 'page'(或 'page-mobile')来允许您选择性地将布局应用于 Story:

.storybook/preview.tsx
import React from 'react';
 
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  decorators: [
    // 👇 Defining the decorator in the preview file applies it to all stories
    (Story, { parameters }) => {
      // 👇 Make it configurable by reading from parameters
      const { pageLayout } = parameters;
      switch (pageLayout) {
        case 'page':
          return (
            // Your page layout is probably a little more complex than this ;)
            <div className="page-layout">
              <Story />
            </div>
          );
        case 'page-mobile':
          return (
            <div className="page-mobile-layout">
              <Story />
            </div>
          );
        default:
          // In the default case, don't apply a layout
          return <Story />;
      }
    },
  ],
};
 
export default preview;

另一个示例,请参阅有关 配置模拟提供程序 的部分,其中演示了如何使用相同的技术来更改提供给组件的主题。

使用 decorators 提供数据

如果您的组件已“连接”并且需要侧载数据才能渲染,您可以使用 decorators 以模拟的方式提供该数据,而无需重构组件以接受该数据作为 arg。有几种方法可以实现这一点。具体取决于您加载数据的方式。在 在 Storybook 中构建页面 部分了解更多信息。

Story decorators

要为单个 Story 定义 decorator,请在命名导出中使用 decorators 键。

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 = {
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
};

确保 Story 保持为被测组件的“纯”渲染,并且任何额外的 HTML 或组件仅用作 decorators 是很有用的。特别是 Source Doc Block 在您执行此操作时效果最佳。

Component decorators

要为组件的所有 Stories 定义 decorator,请使用默认 CSF 导出中的 decorators 键。

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,
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
} satisfies Meta<typeof Button>;
 
export default meta;

Global decorators

我们还可以通过 .storybook/preview.js|ts 文件中的 decorators 导出为 **所有 Stories** 设置 decorator(这是您配置所有 Stories 的文件)。

.storybook/preview.tsx
import React from 'react';
 
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Preview } from '@storybook/your-framework';
 
const preview: Preview = {
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
};
 
export default preview;

Decorator 继承

与 parameters 类似,decorators 可以全局、在组件级别和单个 Story 级别定义(正如我们已经看到的)。

与 Story 相关的所有 decorators 将在 Story 渲染后按以下顺序运行:

  • Global decorators,按其定义的顺序。
  • Component decorators,按其定义的顺序。
  • Story decorators,按其定义的顺序,从最内层的 decorator 开始,向上层级移动,顺序相同。