Docs
Storybook Docs

装饰器

Watch a video tutorial

装饰器是一种将故事封装在额外“渲染”功能中的方法。许多插件定义了装饰器,以通过额外的渲染来增强你的故事或收集有关你的故事如何渲染的详细信息。

¥A decorator is a way to wrap a story in extra “rendering” functionality. Many addons define decorators to augment your stories with extra rendering or gather details about how your story renders.

编写故事时,装饰器通常用于用额外的标记或上下文模拟封装故事。

¥When writing stories, decorators are typically used to wrap stories with extra markup or context mocking.

使用额外标记封装故事

¥Wrap stories with extra markup

一些组件需要“线束”才能以有用的方式渲染。例如,如果组件一直运行到其边缘,你可能希望在 Storybook 内将其留出空间。使用装饰器为组件的所有故事添加间距。

¥Some components require a “harness” to render in a useful way. For instance, if a component runs right up to its edges, you might want to space it inside Storybook. Use a decorator to add spacing for all stories of the component.

Story without padding

YourComponent.stories.ts|tsx
import type { Meta } from '@storybook/react';
 
import { YourComponent } from './YourComponent';
 
const meta: Meta<typeof YourComponent> = {
  component: YourComponent,
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
};
 
export default meta;

Story with padding

用于模拟的“上下文”

¥“Context” for mocking

装饰器函数的第二个参数是故事上下文,其中包含以下属性:

¥The second argument to a decorator function is the story context which contains the properties:

  • args - 故事参数。你可以在装饰器中使用一些 args,并将它们放在故事实现本身中。

    ¥args - the story arguments. You can use some args in your decorators and drop them in the story implementation itself.

  • argTypes- Storybook 的 argTypes 允许你自定义和微调你的故事 args

    ¥argTypes- Storybook's argTypes allow you to customize and fine-tune your stories args.

  • globals - Storybook 范围 globals。特别是,你可以使用 工具栏功能 来允许你使用 Storybook 的 UI 更改这些值。

    ¥globals - Storybook-wide globals. In particular you can use the toolbars feature to allow you to change these values using Storybook’s UI.

  • hooks - Storybook 的 API 钩子(例如 useArgs)。

    ¥hooks - Storybook's API hooks (e.g., useArgs).

  • parameters- 故事的静态元数据,最常用于控制 Storybook 的功能和附加组件的行为。

    ¥parameters- the story's static metadata, most commonly used to control Storybook's behavior of features and addons.

  • viewMode- Storybook 的当前活动窗口(例如,画布、文档)。

    ¥viewMode- Storybook's current active window (e.g., canvas, docs).

此上下文可用于根据故事的参数或其他元数据调整装饰器的行为。例如,你可以通过定义 parameters.pageLayout = 'page'(或 'page-mobile')创建一个装饰器,使你可以选择将布局应用于故事::

¥This context can be used to adjust the behavior of your decorator based on the story's arguments or other metadata. For example, you could create a decorator that allows you to optionally apply a layout to the story, by defining parameters.pageLayout = 'page' (or 'page-mobile'): :

.storybook/preview.tsx
import React from 'react';
 
import type { Preview } from '@storybook/react';
 
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;

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

¥For another example, see the section on configuring the mock provider, which demonstrates how to use the same technique to change which theme is provided to the component.

使用装饰器提供数据

¥Using decorators to provide data

如果你的组件是“连接的”并且需要侧载数据来渲染,则可以使用装饰器以模拟方式提供该数据,而无需重构组件以将该数据作为参数。有几种实现这一点的技术。具体取决于你加载数据的具体方式。在 在 Storybook 中构建页面 部分阅读更多内容。

¥If your components are “connected” and require side-loaded data to render, you can use decorators to provide that data in a mocked way without having to refactor your components to take that data as an arg. There are several techniques to achieve this. Depending on exactly how you are loading that data. Read more in the building pages in Storybook section.

故事装饰器

¥Story decorators

要为单个故事定义装饰器,请在命名导出上使用 decorators 键:

¥To define a decorator for a single story, use the decorators key on a named export:

Button.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
};
 
export default meta;
type Story = StoryObj<typeof Button>;
 
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>
    ),
  ],
};

确保故事仍然是被测组件的“纯”渲染,并且任何额外的 HTML 或组件仅用作装饰器很有用。特别是当你执行此操作时,来源 Doc Block 效果最佳。

¥It is useful to ensure that the story remains a “pure” rendering of the component under test and that any extra HTML or components are used only as decorators. In particular the Source Doc Block works best when you do this.

组件装饰器

¥Component decorators

要为组件的所有故事定义装饰器,请使用默认 CSF 导出的 decorators 键:

¥To define a decorator for all stories of a component, use the decorators key of the default CSF export:

Button.stories.ts|tsx
import type { Meta, StoryObj } from '@storybook/react';
 
import { Button } from './Button';
 
const meta: Meta<typeof Button> = {
  component: Button,
  decorators: [
    (Story) => (
      <div style={{ margin: '3em' }}>
        {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it  */}
        <Story />
      </div>
    ),
  ],
};
 
export default meta;

全局装饰器

¥Global decorators

我们还可以通过 .storybook/preview.js 文件的 decorators 导出(这是你配置所有故事的文件)为所有故事设置一个装饰器:

¥We can also set a decorator for all stories via the decorators export of your .storybook/preview.js file (this is the file where you configure all stories):

.storybook/preview.tsx
import React from 'react';
 
import { Preview } from '@storybook/react';
 
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 inheritance

与参数一样,装饰器可以在全局、组件级别和单个故事中定义(如我们所见)。

¥Like parameters, decorators can be defined globally, at the component level, and for a single story (as we’ve seen).

故事渲染后,所有与故事相关的装饰器都将按以下顺序运行:

¥All decorators relevant to a story will run in the following order once the story renders:

  • 全局装饰器,按定义顺序排列

    ¥Global decorators, in the order they are defined

  • 组件装饰器,按定义顺序

    ¥Component decorators, in the order they are defined

  • 故事装饰器,按定义顺序排列

    ¥Story decorators, in the order they are defined