加入直播会话:周四,美国东部时间上午 11 点,Storybook 9 发布与 AMA
文档
Storybook Docs

装饰器

装饰器是一种将额外的“渲染”功能包装到 story 中的方式。许多插件定义了装饰器,以增强您的 stories 的渲染功能或收集有关 story 如何渲染的详细信息。

编写 stories 时,装饰器通常用于将 stories 包装到额外的标记或模拟上下文。

用额外标记包装 stories

有些组件需要一个“线束”才能以有用的方式渲染。例如,如果一个组件紧贴其边缘,您可能希望在 Storybook 中为其添加间距。使用装饰器为该组件的所有 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

用于模拟的“上下文”

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

  • args - story 的参数。您可以在装饰器中使用一些 args,并在 story 实现本身中省略它们。
  • argTypes- Storybook 的 argTypes 允许您自定义和微调 stories 的 args
  • globals - Storybook 全局 全局变量。特别是,您可以使用工具栏功能,通过 Storybook 的 UI 来更改这些值。
  • hooks - Storybook 的 API hooks (例如,useArgs)。
  • parameters- story 的静态元数据,最常用于控制 Storybook 特性和插件的行为。
  • viewMode - Storybook 当前的活动窗口(例如,canvas、docs)。

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

.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;

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

使用装饰器提供数据

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

Story 装饰器

要为单个 story 定义装饰器,请在命名导出上使用 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 或组件仅用作装饰器,这是很有用的。特别是Source 文档块在此情况下效果最佳。

组件装饰器

要为组件的所有 stories 定义装饰器,请使用默认 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;

全局装饰器

我们还可以通过 .storybook/preview.js|ts 文件的 decorators 导出为**所有 stories**设置装饰器(这是您配置所有 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;

装饰器继承

与 parameters 类似,装饰器可以全局定义、在组件级别定义以及为单个 story 定义(如我们所见)。

一旦 story 渲染,所有与 story 相关的装饰器将按以下顺序运行:

  • 全局装饰器,按定义顺序
  • 组件装饰器,按定义顺序
  • Story 装饰器,按定义顺序,从最内层的装饰器开始,然后依次向外、向上层级运行