多個元件的故事
撰寫一次渲染兩個或多個元件的故事很有用,如果這些元件被設計為協同工作。 例如,ButtonGroup
、List
和 Page
元件。
子元件
當您要記錄的元件具有父子關係時,您可以使用 subcomponents
屬性將它們一起記錄。 當子元件不應單獨使用,而僅作為父元件的一部分時,這特別有用。
這是一個使用 List
和 ListItem
元件的範例
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { List } from './List';
import { ListItem } from './ListItem';
const meta: Meta<typeof List> = {
component: List,
subcomponents: { ListItem }, //👈 Adds the ListItem component as a subcomponent
};
export default meta;
type Story = StoryObj<typeof List>;
export const Empty: Story = {};
export const OneItem: Story = {
render: (args) => (
<List {...args}>
<ListItem />
</List>
),
};
請注意,透過將 subcomponents
屬性新增至預設匯出,我們會在 ArgTypes 和 Controls 表格上獲得一個額外的面板,列出 ListItem
的 props
子元件僅用於文件目的,並且有一些限制
- 子元件的 argTypes 是 推斷的 (對於支援該功能的渲染器),並且無法手動定義或覆寫。
- 每個已記錄子元件的表格都不包含 controls 來變更 props 的值,因為 controls 始終適用於主要元件的 args。
讓我們來談談您可以用來減輕上述情況的一些技術,這些技術在更複雜的情況下特別有用。
重複使用故事定義
我們還可以透過重複使用故事定義來減少故事中的重複。 在這裡,我們可以在 List
的故事中重複使用 ListItem
故事的 args
import type { Meta, StoryObj } from '@storybook/react';
import { List } from './List';
import { ListItem } from './ListItem';
//👇 We're importing the necessary stories from ListItem
import { Selected, Unselected } from './ListItem.stories';
const meta: Meta<typeof List> = {
component: List,
};
export default meta;
type Story = StoryObj<typeof List>;
export const ManyItems: Story = {
render: (args) => (
<List {...args}>
<ListItem {...Selected.args} />
<ListItem {...Unselected.args} />
<ListItem {...Unselected.args} />
</List>
),
};
透過使用其 args 渲染 Unchecked
故事,我們能夠在 List
中重複使用來自 ListItem
故事的輸入資料。
但是,我們仍然沒有使用 args 來控制 ListItem
故事,這表示我們無法使用 controls 變更它們,也無法在其他更複雜的元件故事中重複使用它們。
使用 children 作為 arg
我們改善這種情況的一種方法是將渲染的子元件拉出到 children
arg 中
import type { Meta, StoryObj } from '@storybook/react';
import { List } from './List';
//👇 Instead of importing ListItem, we import the stories
import { Unchecked } from './ListItem.stories';
const meta: Meta<typeof List> = {
/* 👇 The title prop is optional.
* See https://storybook.dev.org.tw/docs/configure/#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'List',
component: List,
};
export default meta;
type Story = StoryObj<typeof List>;
export const OneItem: Story = {
args: {
children: <Unchecked {...Unchecked.args} />,
},
};
現在 children
是一個 arg,我們可能會在另一個故事中重複使用它。
但是,使用這種方法時,您應該注意一些注意事項。
children
arg,就像所有 args 一樣,需要是 JSON 可序列化的。 為了避免 Storybook 發生錯誤,您應該
我們目前正在努力改善 children arg 的整體體驗,並允許您在 control 中編輯 children arg,並允許您在不久的將來使用其他類型的元件。 但就目前而言,在實作您的故事時,您需要考慮到這個注意事項。
建立範本元件
另一個更基於「資料」的選項是建立一個特殊的「故事產生」範本元件
import type { Meta, StoryObj } from '@storybook/react';
import { List } from './List';
import { ListItem } from './ListItem';
//👇 Imports a specific story from ListItem stories
import { Unchecked } from './ListItem.stories';
const meta: Meta<typeof List> = {
/* 👇 The title prop is optional.
* Seehttps://storybook.dev.org.tw/docs/configure/#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'List',
component: List,
};
export default meta;
type Story = StoryObj<typeof List>;
//👇 The ListTemplate construct will be spread to the existing stories.
const ListTemplate: Story = {
render: ({ items, ...args }) => {
return (
<List>
{items.map((item) => (
<ListItem {...item} />
))}
</List>
);
},
};
export const Empty = {
...ListTemplate,
args: {
items: [],
},
};
export const OneItem = {
...ListTemplate,
args: {
items: [{ ...Unchecked.args }],
},
};
這種方法設定起來稍微複雜一些,但這表示您可以更輕鬆地在複合元件中重複使用每個故事的 args
。 這也表示您可以使用 Controls 插件來變更元件的 args。