文件
Storybook 文件

互動

Storybook 中的 play 函數讓您可以在 story 渲染後模擬使用者互動。透過 Interactions 擴充功能,您可以視覺化並偵錯這些互動。

互動的 Play 函數

Stories 以結構化的方式隔離並捕捉組件狀態。在開發組件時,您可以快速瀏覽 stories 以驗證外觀和風格。每個 story 都指定了重現特定狀態所需的所有輸入。您甚至可以模擬上下文和 API 呼叫,讓您能夠處理組件的大部分使用情境。但是,對於需要使用者互動的狀態呢?

例如,點擊按鈕以開啟/關閉對話框、拖曳列表項目以重新排序,或填寫表單以檢查驗證錯誤。為了測試這些行為,您必須像使用者一樣與組件互動。互動式 stories 讓您可以使用 play 函數自動化這些互動。它們是一小段程式碼,會在 story 渲染完成後執行,模擬使用者與組件互動時會採取的確切步驟。

由 Testing Library 和 Vitest 驅動

互動是使用名為 @storybook/test 的套件編寫的。它提供了 Storybook 儀器化的 Testing LibraryVitest 版本。這為您提供了熟悉的開發人員友善語法,可以與 DOM 互動並進行斷言,同時還提供額外的遙測功能來協助偵錯。

設定 Interactions 擴充功能

預設情況下,如果您為新專案新增 Storybook,則 @storybook/addon-interactions 已安裝並設定完成。如果您是從舊版 Storybook 遷移,則需要手動安裝。

執行以下命令以安裝 Interactions 擴充功能和相關依賴項。

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 時,Interactions 擴充功能將會啟用。

Storybook Interactions installed and registered

撰寫組件測試

互動作為您的 stories 的 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.dev.org.tw/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'), 'hi@example.com');
      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 將您的元素查詢範圍限定在目前的 story。如果您希望您的 play 函數最終與 Storybook Docs 相容,這非常重要,因為 Storybook Docs 會在同一頁面上渲染多個組件。此外,step 函數可用於建立已標記的互動群組。

雖然您可以參考 Testing Library 文件 以了解如何使用它,但在使用 Storybook 包裝器時,有一個重要的不同之處:方法調用必須使用 `await`。這讓您可以使用偵錯工具在您的互動中來回逐步執行。

任何已標記為 Action 的 args,無論是使用 argTypes 註解argTypesRegex,都會自動轉換為 Jest mock function(spy)。這讓您可以針對這些函數的呼叫進行斷言。

為了在您的 Storybook stories 中模擬函數以進行可靠且隔離的組件測試,請使用從 @storybook/test 匯入的具名 fn