文档
Storybook 文档

交互

Storybook 中的 play 函数允许你在故事渲染后模拟用户交互以运行。使用 交互 插件,你可以可视化并调试这些交互。

交互的 Play 函数

故事以结构化的方式隔离和捕获组件状态。在开发组件时,你可以快速循环浏览故事以验证外观和感觉。每个故事都指定了再现特定状态所需的所有输入。你甚至可以模拟上下文和 API 调用,从而能够处理组件的大多数用例。但是,对于需要用户交互的状态该怎么办呢?

例如,单击按钮以打开/关闭对话框、拖动列表项以重新排序或填写表单以检查验证错误。要测试这些行为,你必须像用户一样与组件进行交互。交互式故事使你能够使用 play 函数自动化这些交互。它们是 Storybook 渲染完成后运行的小代码片段,模拟用户与组件交互的确切步骤。

由 Testing Library 和 Vitest 提供支持

交互使用名为 @storybook/test 的包编写。它提供了 Testing LibraryVitest 的 Storybook 仪器版本。这为你提供了熟悉的开发人员友好的语法来与 DOM 交互并进行断言,但还增加了额外的遥测以帮助调试。

设置交互插件

默认情况下,如果你为新项目添加 Storybook,则 @storybook/addon-interactions 已经安装并配置好了。如果你从 Storybook 的早期版本迁移,则需要手动安装它。

运行以下命令以安装交互插件和相关的依赖项。

npm install @storybook/test @storybook/addon-interactions --save-dev

接下来,将 .storybook/main.js|ts 更新为以下内容

.storybook/main.ts
// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
import type { StorybookConfig } from '@storybook/your-framework';
 
const config: StorybookConfig = {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    // Other Storybook addons
    '@storybook/addon-interactions', // 👈 Register the addon
  ],
};
 
export default config;

确保将 @storybook/addon-interactions 放在 @storybook/addon-essentials 插件(或如果你单独安装了 @storybook/addon-actions)**之后**。

现在,当你运行 Storybook 时,交互插件将被启用。

Storybook Interactions installed and registered

编写组件测试

交互作为故事的 play 函数的一部分运行。我们依靠 Testing Library 来完成繁重的工作。

确保通过 @storybook/test 导入 Vitest 和 Testing Library 的 Storybook 包装器,而不是直接导入原始包。

Form.stories.ts|tsx
// Replace your-framework with the name of your framework
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { userEvent, waitFor, within, expect, fn } from '@storybook/test';
 
import { Form } from './Form';
 
const meta: Meta<typeof Form> = {
  component: Form,
  args: {
    // 👇 Use `fn` to spy on the onSubmit arg
    onSubmit: fn(),
  },
};
 
export default meta;
type Story = StoryObj<typeof Form>;
 
/*
 * See https://storybook.org.cn/docs/writing-stories/play-function#working-with-the-canvas
 * to learn more about using the canvasElement to query the DOM
 */
export const Submitted: Story = {
  play: async ({ args, canvasElement, step }) => {
    const canvas = within(canvasElement);
 
    await step('Enter credentials', async () => {
      await userEvent.type(canvas.getByTestId('email'), '[email protected]');
      await userEvent.type(canvas.getByTestId('password'), 'supersecret');
    });
 
    await step('Submit form', async () => {
      await userEvent.click(canvas.getByRole('button'));
    });
 
    // 👇 Now we can assert that the onSubmit arg was called
    await waitFor(() => expect(args.onSubmit).toHaveBeenCalled());
  },
};

上面的示例使用 canvasElement 将你的元素查询范围限定到当前故事。如果你希望你的 play 函数最终与 Storybook 文档兼容,这至关重要,因为文档在同一页面上渲染多个组件。此外,step 函数可用于创建交互的带标签的组。

虽然你可以参考 Testing Library 文档 以了解如何使用它的详细信息,但在使用 Storybook 包装器时有一个重要的细节有所不同:**方法调用必须使用 await**。它允许你使用调试器在交互中来回切换。

任何已标记为操作的 args(使用 argTypes 注解argTypesRegex),都将自动转换为 Jest 模拟函数(间谍)。这允许你对对这些函数的调用进行断言。

要在你的 Storybook 故事中模拟函数以进行可靠且隔离的组件测试,请使用来自 @storybook/test 的命名 fn 导入。

API

参数

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

disable

类型: boolean

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

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