多個組件的故事
如果這些組件的設計目的是協同工作,那麼同時渲染兩個或多個組件的 story 會很有用。例如,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 會推斷 (對於支援該功能的渲染器),且無法手動定義或覆寫。
- 每個已記錄子組件的表格都不包含可變更 props 值的控制項,因為控制項始終會套用至主要組件的 args。
讓我們來談談一些可以用來減輕上述情況的技巧,這些技巧在更複雜的情況下特別有用。
重複使用 story 定義
我們也可以透過重複使用 story 定義來減少 story 中的重複。在這裡,我們可以在 List
的 story 中重複使用 ListItem
stories 的 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
story,我們能夠在 List
中重複使用 ListItem
stories 的輸入資料。
但是,我們仍然沒有使用 args 來控制 ListItem
stories,這表示我們無法使用控制項變更它們,也無法在其他更複雜的組件 story 中重複使用它們。
將 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,我們就可以在另一個 story 中重複使用它。
但是,當您使用這種方法時,有一些注意事項應該要注意。
與所有 args 一樣,children
arg 需要可序列化為 JSON。為了避免您的 Storybook 發生錯誤,您應該
我們目前正在努力改善 children arg 的整體體驗,並讓您在控制項中編輯 children arg,並讓您在不久的將來使用其他類型的組件。但是現在,您在實作您的 story 時需要考慮這個注意事項。
建立範本組件
另一個更「以資料」為基礎的選項是建立一個特殊的「story 產生」範本組件
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 }],
},
};
此方法的設定比較複雜,但這表示您可以更輕鬆地在複合組件中重複使用每個 story 的 args
。這也表示您可以使用控制項外掛變更組件的 args。