返回部落格

Storybook Args 介紹

新一代動態元件範例

loading
Michael Shilman
@mshilman
上次更新

Storybook 是世界上最受歡迎的 UI 元件工作坊。它被 Airbnb、Slack、Lyft、IBM、Shopify 以及業界數千個頂尖團隊所使用。

在其核心,Storybook 是一個故事的目錄:小的 Javascript 函數,捕捉隔離的 UI 元件狀態,使其對於互動式開發、測試和文件編寫非常有用。

現在在 Storybook 6.0 中,我們很高興推出 Storybook Args,這是編寫故事的一個基礎性改進。Args 允許故事接收動態數據作為輸入參數,解鎖故事易用性可移植性重複使用性的新層次。

Args 與您現有的故事相容,但解鎖了新功能

  • 🗜 減少 故事的大小和複雜性
  • 🚚 重複使用 跨故事的 fixture 數據
  • ♻️ 在更廣泛的工具中重複利用 故事

它們還為強大的下一代 Storybook 插件打開了大門,我們將在一系列後續文章中介紹這些插件。


💡 為什麼需要動態數據?

Storybook 的 元件故事格式 (CSF) 是元件範例的新興標準,在 Storybook 5.2 中引入。CSF 檔案是 ES6 模組,沒有任何 Storybook 特定的擴展。因此,它們簡單、可移植且面向未來

export const Basic = () => (
  <Button label='hello' />
);

編寫 CSF 故事很容易。您在想要顯示的狀態下實例化您的元件,將其包裹在一個函數中,並命名它。

但它可以被改進。Tom Coleman 和我在研究了 CSF 在 45,000 多個 Github 倉庫中的使用方式後,有了一個洞察:如果故事接受動態數據作為輸入,它將解鎖無數新的用例。

考慮我們之前範例的以下迭代

export const Basic = (args) => <Button {...args} />;
Basic.args = { label: 'hello' };

這個新版本從「環境」接收一個 Args 物件,並宣告它想要在該物件中接收的初始數據。

這種間接的技巧可能看起來是一件小事,但它是一個強大的抽象概念,支撐著下一代 Storybook 功能。在這篇文章中,我們將重點介紹 Args 使編寫故事更有效率的不同方式。

動態數據使您能夠即時編輯元件

🗜 減少故事樣板程式碼

Args 透過在故事的數據顯示邏輯之間提供更清晰的分隔,減少了使用者需要為每個故事編寫和維護的程式碼。

考慮以下 CSF v1 故事

// Button.stories.js

export const Text = () => (
  <Button label="hello" background="#ff0" />
);

export const Emoji = () => (
  <Button label="😀 😎 👍 💯" background="#ff0" />
);

現在考慮使用 Args 的相同故事

// Button.stories.js

const Template = (args) => <Button {...args} />;

export const Text = Template.bind({});
Text.args = { label: 'hello', background: '#ff0' };

export const Emoji = Template.bind({});
Emoji.args = { ...Text.args, label: '😀 😎 👍 💯' };

故事函數樣板程式碼在故事之間重複使用:Template.bind({}) 複製了該函數,減少了程式碼重複

同樣地,...Text.args 複製了數據,減少了數據重複

當為其他框架編寫故事時,好處變得更加明顯。考慮 Vue 的等效範例

// Button.stories.js

const Template = (args) => ({
  props: Object.keys(args),
  components: { MyButton },
  template: `
    <my-button :background="background">
     {{label}}
    </my-button>
  `
});

export const Text = Template.bind({});
Text.args = { label: 'hello', background: '#ff0' };

export const Emoji = Template.bind({});
Emoji.args = { ...Text.args, label: '😀 😎 👍 💯' };

Args 故事的這種模式與早期的 CSF 版本有所不同,但我們認為您會喜歡它。如果您喜歡以「舊」方式編寫故事,或逐步採用,Storybook 完全向後相容。

🚚 重複使用跨故事的 fixture 數據

Args 的第二個主要使用者好處是,它提供了一種風格化的方式來組織和分享元件的 fixture 數據。

您可能熟悉程式設計中的 DRY 原則:不要重複自己。但在實踐中,我們在故事中看到了很多重複。

考慮一個用於假設任務列表的複合元件

// TaskList.stories.js

const owner = { login: 'shilman', avatar: ... };
export const Basic = () => (
  <TaskList items={[
    { title: 'checked item', checked: true, owner },
    { title: 'checked item', checked: true, owner },
    { title: 'unchecked item', checked: false, owner },
    { title: 'unassigned item', checked: false,  },
  ]}/>
);

現在考慮 Args 的等效範例

// TaskList.stories.js

import { Checked, Unchecked, Unassigned } from '../Task.stories';

const Template = (args) => <TaskList {...arg} />;

export const Basic = Template.bind({});
Basic.args = { 
  items: [Checked.args, Checked.args, Unchecked.args, Unassigned.args]
};

在 Args 範例中,當 Task 元件的簽名變更時,您只需要變更 Tasks 故事以反映新的架構。由於您的 Args 故事重複使用 fixture 數據,因此架構變更將向上傳播到故事層次結構中,從而大幅降低維護成本。

當為大型應用程式建構 Storybook 時,這種數據重複使用特別有用,在這些應用程式中,元件是按層次結構組織的。

Args 的許多設計靈感來自 Chromatic 的 storybook,其中包含數千個故事,範圍從 原子化 元件一直到完整的應用程式頁面。

Args 將這些學習成果編碼到故事語言本身中,融入了多年來作為頂尖實踐者開發和使用 Storybook 而來之不易的最佳實踐。

♻️ 在其他工具中重複利用故事

最後一個好處是 Args 故事比以前更具可移植性。

元件故事格式的一個設計目標是創建高度可移植的元件範例。在我們的公告文章中,我們展示了如何在 Jest 單元測試中重複利用 CSF 故事,並描繪了前端工具空間中互操作性的更大願景。

隨著 CSF 在整個生態系統中被採用,這個願景現在正在成為現實。它內建於熱門的新 React 框架 RedwoodJS 中。它為 webcomponents.dev 提供支持,這是一個強大的線上 IDE。並且截至撰寫本文時,它已被納入至少一個流行的設計工具中。

Args 透過從您的故事中移除插件依賴性,將互操作性更進一步。考慮以下 CSF v1 故事,它使用了 actionsknobs,這是 Storybook 最受歡迎的兩個插件

import { action } from '@storybook/addon-actions';
import { text } from '@storybook/addon-knobs';

export default { title: 'Button' };

export const Text = () => (
  <Button
    onClick={action('onClick')}
    label={text('label', 'Hello')}
  />
);

這個 ES6 模組是可移植的,但它依賴於 @storybook/addon-actions@storybook/addon-knobs,它們都嚴重依賴 Storybook 的運行時。

現在考慮 Args 的等效範例

const Template = (args) => <Button {...args} />;

export const Text = Template.bind({});
Text.args = { label: 'Hello' }

請注意,這個故事不再依賴 @storybook/* 套件。Args 由「環境」提供,因此即使沒有直接的故事依賴性,我們也可以保留 actions 和 knobs 風格的功能!我們將在後續文章中介紹自動生成的控制項動作時,更詳細地解釋這是如何運作的。

移除這些 Storybook 依賴性提高了可移植性,因此故事在外部設計、原型設計、開發和測試工具中更容易使用。

考慮如何在上文中提到的 Args 故事在 Jest 中被重複利用

import { render, fireEvent } from '@testing-library/react';
import { Text } from './Button.stories';

it('should respond to click events', () => {
  const handleClick = jest.fn();
  
  const instance = render(
    <Text {...Text.args} onClick={handleClick} />
  );
  
  fireEvent.click(instance.container.firstChild);
  expect(handleClick).toHaveBeenCalled();
});

使用 knobs 和 actions 為原始 CSF 故事編寫相同的測試將需要複雜的模擬。不再需要了!我們很高興在前端生態系統中推廣基於 Args 的元件範例。

⚡ 1 分鐘安裝

在使用 args 的過去幾個月中,我們認為所有使用者都應該立即升級。立即在 Storybook 6.0 中試用!

升級現有的 Storybook 專案

npx sb upgrade

或將 Storybook 啟動到現有的應用程式中

npx sb init

要創建您的第一個 Args 故事,請創建一個函數,該函數將 Args 物件作為其第一個輸入,然後使用您想要接收的數據註釋該函數

import { Button } from '@storybook/react/demo';

export default { title: 'Button/Args', component: Button };

export const Basic = (args) => <Button {...args} />;
Basic.args = { children: 'hello' };

Voilà!您已經編寫了您的第一個 Args 故事。但這只是冰山一角。Args 也為插件增強了功能,並為 Storybook 帶來了許多新功能,我們將在未來幾週內對此進行擴展。

在下方訂閱以了解 Args 如何啟用

  • 🎛 自動生成 控制項 用於元件屬性,
  • 📢 自動生成動作 用於事件記錄,
  • 🛠 自訂工具列 用於主題、國際化和上下文。

參與其中

Args 由 Michael Shilman(我!)和 Tom Coleman 開發,靈感來自 addon-docs/knobs/context 貢獻者,包括 Norbert de LangenFilipp RiabchunAtanas StoyanovLeo Y. Li

Storybook 由 1000 多名開源貢獻者維護,並由頂級維護者的指導委員會指導。如果您有興趣貢獻,請查看 GitHub 上的 Storybook,創建 issue,或提交 pull request。在 Open Collective 上捐款。在 Discord 中與我們聊天 — 通常會有維護者在線上。

加入 Storybook 電郵列表

獲取最新的新聞、更新和發布

6,730位開發人員及持續增加中

我們正在招聘!

加入 Storybook 和 Chromatic 背後的團隊。構建被數十萬開發人員在生產環境中使用的工具。遠程優先。

查看職位

熱門文章

Storybook Controls

無需程式碼即可即時編輯 UI 元件
loading
Michael Shilman

零配置 Storybook

簡單設定,即時生產力
loading
Michael Shilman

Storybook 5.3

更快地構建生產設計系統
loading
Michael Shilman
加入社群
6,730位開發人員及持續增加中
為何為何選擇 Storybook元件驅動的 UI
文件指南教程變更日誌Telemetry
社群插件參與其中部落格
展示探索專案元件詞彙表
開源軟體
Storybook - Storybook 繁體中文

特別感謝 Netlify CircleCI