
Component Story Format 3.0
告别样板代码,迎接脚本化交互!

Storybook 基于一个核心概念:故事 (story)。每个使用 Storybook 的人都为其组件示例编写故事。Component Story Format 是一种富有表现力、平台无关的格式,已被从 Netflix 到 Shopify 及更广泛的行业所采用。
我很高兴地宣布 Component Story Format 3.0。这是 CSF 的下一个主要迭代版本,它利用了一年来社区的反馈,大幅减少了样板代码,让您专注于故事的本质。
- ♻️ 可扩展的故事对象,便于重用
- 🌈 默认渲染函数,力求简洁
- 📓 自动标题,方便快捷
- ▶️ Play 函数,用于脚本化交互
- ✅ 100% 向后兼容 CSF 2.0
CSF 3.0 今天即可在实验模式下使用。我们希望您能提供反馈,以帮助我们在正式发布前使其变得更好。让我们深入了解以下细节。
CSF 到底有什么优点呢?
Component Story Format (CSF) 是一种基于 ES6 模块的简单文件格式,用于组件示例。自两年前推出以来,它已大获成功。
在底层,CSF 是标准的 JavaScript,与 Storybook 的 API 解耦。没有工具锁定。这意味着您可以将故事导入您最喜欢的 JS 库。
去年,我们推出了 CSF Args:动态故事输入,开启了新的用例,例如依赖注入和自动生成的控件。
Args 已成为 Storybook 及其插件的强大构建块,也成为了 Testing Library 和 Cypress 等第三方集成的重要组成部分。
更重要的是,由于 CSF 是平台无关的,因此它现在得到了整个生态系统中令人难以置信的项目的支持,例如 RedwoodJS、React Styleguidist 和 UXPin。

CSF 3.0 中的新功能
Component Story Format 是一种基于 ES6 模块导出的文件格式:默认导出包含有关示例的元数据,每个命名导出都是一个示例。
在 CSF 2.0 中,命名导出始终是实例化组件的函数,并且这些函数可以使用配置选项进行注释。例如:
// CSF 2.0
export default {
title: 'components/Button',
component: Button,
};
export const Primary = (args) => <Button {...args} />;
Primary.args = { primary: true };
这为 Button
声明了一个 Primary
故事,该故事通过将 {primary: true}
扩展到组件中来渲染自身。default.title
元数据说明了故事在导航层次结构中的位置。
这是 CSF 3.0 的等效代码:
// CSF 3.0
export default { component: Button };
export const Primary = { args: { primary: true } };
它更简洁了!让我们逐个了解这些更改,以了解发生了什么。
可扩展的故事对象
您可能注意到的第一件事是,在 CSF 3.0 中,命名导出是对象,而不是函数。这使我们能够使用 JS 扩展运算符更有效地重用故事。
考虑以下添加到介绍示例中的内容,它创建了一个 PrimaryOnDark
故事,该故事在深色背景下渲染:

这是 CSF 2.0 的实现:
// CSF 2.0
export const PrimaryOnDark = Primary.bind({});
PrimaryOnDark.args = Primary.args;
PrimaryOnDark.parameters = { background: { default: 'dark' } };
Primary.bind({})
复制了故事函数,但它不会复制挂在函数上的注释,因此我们必须添加 PrimaryOnDark.args = Primary.args
以继承 args。
在 CSF 3.0 中,我们可以扩展 Primary
对象以继承其所有注释:
// CSF 3.0
export const PrimaryOnDark = {
...Primary,
parameters: { background: { default: 'dark' } },
};
这似乎是一件小事,但我们可以用它做很多巧妙的事情,我们将在下面看到。
默认渲染函数
您可能注意到的下一件事是,在初始示例中,根本没有函数!这是怎么回事?
在 CSF 3.0 中,故事是对象,因此您指定故事渲染方式的方式是通过渲染函数。我们可以通过以下步骤将 CSF 2.0 示例重写为 CSF 3.0。
让我们从一个简单的 CSF 2.0 故事函数开始:
// CSF 2.0
export default {
title: 'components/Button',
component: Button,
};
export const Default = (args) => <Button {...args} />;
现在,让我们在 CSF 3.0 中将其重写为故事对象,并使用显式渲染函数来告诉故事如何渲染自身。与 CSF 2.0 一样,这使我们可以完全控制如何渲染组件,甚至渲染组件集合。
// CSF 3.0 - explicit render function
export const Default = {
render: (args) => <Button {...args} />
};
但在 CSF 2.0 中,许多 story
函数都是相同的:获取默认导出中指定的组件并将 args 扩展到其中。这些故事的有趣之处不是函数,而是传递给函数的 args。
因此,在 CSF 3.0 中,我们为每个框架提供默认渲染函数。如果您所做的只是将 args 扩展到您的组件中(这是最常见的情况),则您根本不需要指定任何渲染函数:
// CSF 3.0 - default render function
export const Default = {};
没有比这更简单的了。告别样板代码!
自动生成标题
CSF 3.0 中另一个巨大的便利是自动标题生成。
// CSF 2.0
export default { title: 'components/Button', component: Button }
// CSF 3.0
export default { component: Button }
您仍然可以像在 CSF 2.0 中那样指定标题,但如果您不指定标题,则可以从故事在磁盘上的路径推断出来。
这由故事的配置方式控制。考虑旧式配置:
// .storybook/main.js
module.exports = {
stories: ['../src/**/*.stories.*']
};
现在考虑一下 Storybook 6.4 中可用的新式配置:
// .storybook/main.js
module.exports = {
stories: ['../src']
};
给定此配置,故事文件 ../src/components/Button.stories.tsx
将获得标题 components/Button
。
需要更多控制?以下内容将匹配自定义文件模式,并为生成的标题添加自定义前缀:
module.exports = {
stories: [
{ directory: '../src', files: '*.story.tsx', titlePrefix: 'foo' }
]
};
Play 函数
最后但并非最不重要的一点,我们为 CSF 3.0 带来了一个全新的功能:play 函数。Play 函数是在故事渲染后执行的小段代码。
虽然其他 CSF 3.0 功能优化了现有的故事结构,但 play 函数启用了以前不可能实现的场景。
考虑一下验证表单的场景:

及其 CSF 3.0 实现:
// CSF 3.0
import userEvent from '@testing-library/user-event';
export default { component: AccountForm }
export const Empty = {};
export const EmptyError = {
...Empty,
play: () => userEvent.click(screen.getByText('Submit'));
}
export const Filled = {
...Empty,
play: () => {
userEvent.type(screen.getById('user'), 'shilman@example.com');
userEvent.type(screen.getById('password'), 'blahblahblah');
}
}
export const FilledSuccess = {
...Filled,
play: () => {
Filled.play();
EmptyError.play();
}
}
Empty
故事渲染空表单。
EmptyError
通过在故事渲染后立即发出用户事件以使用 Testing Library 单击提交按钮来模拟验证错误。感谢可扩展的故事对象,此故事继承了 Empty
故事的所有 args 和参数。
Filled
故事使用 Testing Library 填写表单。
最后,FilledSuccess
故事通过首先重用 Filled
故事的 play
函数,然后单击提交按钮来显示提交状态。
我们甚至可以在 CSF 3.0 中为整个序列添加动画:
此场景也激发了 CSF 3.0 的可扩展对象语法。我们现在可以方便地复制故事的整个配置,并且只修改我们关心的部分。
希望得到反馈
CSF 3.0 作为实验版本发布。我们希望您能提供反馈。
如果您使用的是 Storybook 6.3 或 SB 6.4 预发布版,请在您的 .storybook/main.js
配置中启用 previewCsf3
:
// .storybook/main.js
module.exports = {
features: {
previewCsfV3: true,
}
};
👉 对于专门的自动标题生成,您必须使用 SB 6.4,并且您必须将 .storybook/main.js
中的 stories
配置更新为上面概述的新格式——官方文档即将发布。
为了您的方便,我们提供了一个 codemod 来升级您的故事
$ npx sb@next migrate csf-2-to-3 --glob="**/*.stories.js"
有关反馈,请访问 GitHub 讨论 和 Storybook Discord 中的 #component-story-format
频道。查看 Github 上 标记为 csf3
的已知未解决问题。
我们计划在整个 SB6.4 发布周期中迭代 CSF,并在准备就绪后删除功能标志。CSF 3.0 向后兼容,因此所有 CSF 2.0 功能仍然可用。您可以试用它,而无需更改任何现有故事。
参与进来
Component Story Format 3.0 由 Michael Shilman(我!)、Tom Coleman、Gert Hengeveld 和 Pavan Sunkara 开发,并得到了整个 Storybook 社区的测试和反馈。
Storybook 是超过 1320 位社区贡献者 的成果,并由顶尖维护者的 指导委员会 组织。
如果 Storybook 使您的 UI 开发工作流程更轻松,请帮助 Storybook 变得更好。您可以贡献新功能、修复错误或改进文档。在 Discord 上加入我们,在 Open Collective 上支持我们,或者直接参与 Github。
Component Story Format 3.0 来了(alpha 版)
— Storybook (@storybookjs) July 7, 2021
♻️ 可扩展的故事对象
🌈 默认渲染函数
📓 自动标题
▶️ Play 函数,用于脚本化交互
✅ 100% 向后兼容
所有这些都减少了大量样板代码! https://#/ky1vC6FcEL pic.twitter.com/CB7bQxNkiy