介绍
Storybook是一个UI组件库开发环境,它使得我们可以通过开发环境来快速地浏览、查看和测试组件,并对它们进行交互。在Storybook中可以方便地组织、展示和测试UI组件。它支持多种框架(React, Vue, Angular等)以及跨平台共享组件库。Storybook 也允许开发人员在开发 UI 组件时重点关注组件的展示和交互,而不必在意业务相关的逻辑,从而提高了开发效率。
使用
我们将需要通过几个步骤设置环境。 首先,我们需要使用Create React App (CRA) 来设置我们的构建系统,并开启 Storybook 以及 Jest 在我们创建的应用程序中进行测试。 让我们运行以下命令:
# Create our application:
npx create-react-app storyboocase
cd storyboocase
# Add Storybook:
npx storybook init
在已有的项目中使用
使用Storybook CLI在单个命令中安装它。在现有项目的根目录中运行以下命令:
//最新版本
npx storybook@latest init
检查应用程序环境是否正常运行
//在终端中运行测试运行器(Jest):
yarn test --watchAll
//在端口6006上启动组件资源管理器:
yarn storybook
//在端口3000上运行前端应用程序
yarn start
什么是故事书
- 故事捕获 UI 组件的呈现状态,开发人员为每个组件编写多个故事,描述组件可以支持的所有有趣的状态
- CLI 创建的示例组件演示了可以使用
Storybook构建的组件类型:按钮、页眉和页面 - 每个示例组件都有一组故事,显示它支持的状态。您可以在UI中浏览故事,并在以
.stories.js或.stories.ts结尾的文件中查看它们背后的代码。这些故事是用组件故事格式(CSF)编写的,CSF是一种基于ES6模块的标准,用于编写组件示例。
创建一个 Button 组件,故事是描述如何呈现所讨论的组件的函数。以下是如何在“主要”状态下渲染Button并导出名为Primary的故事。
在哪里放组件描述
组件的故事在与组件文件共存的故事文件中定义。故事文件仅用于开发,不会包含在生产包中
Button.js | ts | jsx | tsx | vue | svelte
Button.stories.js | ts | jsx | tsx
默认导出
默认的导出元数据控制Storybook如何列出您的故事并提供插件使用的信息。例如,以下是文章文件Button.stories.js的默认导出:
// Button.stories.ts|tsx
import type { Meta } from '@storybook/react';
import { Button } from './Button';
const meta = {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Button',
component: Button,
} satisfies Meta<typeof Button>;
export default meta;
定义故事
story book 参数
如果您正在使用 Storybook 库,并且要了解所有可用参数,可以查阅官方文档,这里提供一个基本的参数列表:
--config-dir:指定 Storybook 配置文件的目录。默认为.storybook。--port:指定 Storybook 运行的端口号。默认为 6006。--host:指定 Storybook 运行的主机名。默认为 localhost。--static-dir:指定用于存储静态文件的目录,例如图像和 CSS 文件。默认为public。--output-dir:指定 Storybook 打包构建生成的任务存放的文件夹。默认为.out。--https:通过 HTTPS 运行 Storybook。--ssl-ca:指定证书颁发机构的证书文件路径。--ssl-cert:指定 SSL 证书的路径。--ssl-key:指定 SSL private key 的路径。--quiet:关闭所有日志输出。--no-watch:禁止监听文件更改并重新构建 Storybook 样式。--no-dll:指定不使用 DllPlugin 加速构建时间。--ci:在 CI 环境下运行 Storybook。 这里只列举了部分可用参数,更详细的参数列表可以在官方文档中查看。 使用CSF文件的命名导出定义组件的故事。
// 引入Meta和StoryObj组件
import type { Meta, StoryObj } from ‘@storybook/react’;
// 引入Button组件
import { Button } from ‘./Button’;
// 定义meta对象
const meta = {
title: 'Button', // 标题
component: Button, // 对应的组件
} satisfies Meta<typeof Button>; // 继承自Meta
// 导出meta对象
export default meta;
// 定义Story类型
type Story = StoryObj<typeof meta>;
// 导出Primary变量,它是一个包含render方法的对象,类型为Story
export const Primary: Story = {
render: () =>,
};
/** 本段代码定义了一个名为Primary的story,它展示了 Button 组件在 primary 模式下的状态。
- 通过调用Button组件,并传递label和primary属性来渲染button组件。
- 其中,primary属性的值为true,表示button处于primary模式下。
- 代码中通过使用StoryObj类型的对象来确保story对象符合Story类型的约束。
*/
使用 React Hooks
React Hooks是使用更简化的方法创建组件的方便助手方法。如果需要,可以在创建组件的故事时使用它们,尽管您应该将它们视为高级用例。我们建议在写自己的故事时尽可能多地使用args。作为一个例子,这里有一个使用React Hooks来更改按钮状态的故事:
// 引入React和useState
import React, { useState } from 'react';
// 引入Meta和StoryObj组件
import { Meta, StoryObj } from '@storybook/react';
// 引入Button组件
import { Button } from './Button';
// 定义meta对象
const meta = {
title: 'Button', // 标题
component: Button, // 对应的组件
} satisfies Meta<typeof Button>; // 继承自Meta
// 导出meta对象
export default meta;
// 定义Story类型
type Story = StoryObj<typeof meta>;
// 声明一个新的Button组件,使用useState维护状态
const ButtonWithHooks = () => {
// 定义value状态,并设置默认值为'Secondary'
const [value, setValue] = useState('Secondary');
// 定义isPrimary状态,并设置默认值为false
const [isPrimary, setIsPrimary] = useState(false);
// 定义一个处理onChange事件的函数
const handleOnChange = () => {
// 如果isPrimary为false
if (!isPrimary) {
// 将isPrimary设置为true
setIsPrimary(true);
// 将value更新成'Primary'
setValue('Primary');
}
};
// 返回Button组件,并传递状态
return <Button primary={isPrimary} onClick={handleOnChange} label={value} />;
};
// 导出Primary变量,它是一个包含render方法的对象
export const Primary = {
render: () => <ButtonWithHooks />,
} satisfies Story; // 继承自StoryObj,用于指示它符合Story类型约束。
重命名文章
您可以重命名任何您需要的特定故事。例如,给予它一个更准确的名字。以下是如何更改Primary文章的名称:
// 引入Meta和StoryObj组件
import type { Meta, StoryObj } from ‘@storybook/react’;
// 引入Button组件
import { Button } from ‘./Button’;
// 定义meta对象
const meta = {
/* 👇 title属性是可选的。
- 参见https://storybook.js.org/docs/react/configure/overview#configure-story-loading
- 以了解如何生成自动标题。
*/
title: ‘Button’,
component: Button,
} satisfies Meta<typeof Button>;
// 导出meta对象
export default meta;
// 定义Story类型
type Story = StoryObj<typeof meta>;
/*
*👇 Render函数是一个框架特有的功能,它允许您控制组件的呈现方式。
- 参见https://storybook.js.org/docs/react/api/csf
- 以了解如何使用 render 函数。
*/
// 导出Primary变量,它是一个包含render方法的对象,类型为Story
export const Primary: Story = {
name: ‘I am the primary’, // 起一个名字方便后续查找
render: () =>, // 调用Button组件,并传递primary和label属性
};
/**
- 本段代码中定义了一个名为Primary的story。
- 它展示了Button组件在primary模式下的状态。
- 使用render方法来渲染Button组件,并传递label和primary属性。
- Story对象有一个额外的name属性,用于标识story对象的名称。
- meta对象和Story对象都继承自对应的类型,以确保类型安全。
*/
如何写故事
故事是描述如何呈现组件的函数。每个组件可以有多个故事,创建故事的最简单方法是多次呈现具有不同参数的组件。
// Button.stories.ts|tsx
// 引入 Meta 和 StoryObj 类型,用于 storybook 配置
import type { Meta, StoryObj } from '@storybook/react';
// 引入自定义组件 Button
import { Button } from './Button';
// 创建 meta 对象,用于 storybook 配置。其中 title 和 component 是可选的。
const meta = {
title: 'Button', // 组件在 storybook 中的 title
component: Button, // 组件对象
} satisfies Meta<typeof Button>; // 指定类型
// 将 meta 对象作为默认导出
export default meta;
// 创建 Story 参数类型
type Story = StoryObj<typeof meta>;
/*
*👇 Render functions 是框架的特性之一,可以控制组件的渲染方式。
* 详见 https://storybook.js.org/docs/react/api/csf
* 了解如何使用 Render functions。
*/
// 创建多个 Story,每个 Story 包括了一个 render 函数和一些自定义参数。
export const Primary: Story = {
render: () => <Button backgroundColor="#ff0" label="Button" />, // 定义组件的渲染方式
};
export const Secondary: Story = {
render: () => <Button backgroundColor="#ff0" label="😄👍😍💯" />,
};
export const Tertiary: Story = {
render: () => <Button backgroundColor="#ff0" label="📚📕📈🤓" />,
};
配置文件
main.tsx
在 Storybook 中,main.ts 是一个配置文件,用于指定 Storybook 构建和启动应用程序时的选项和配置。它是在 Storybook 6.x 版本中引入的,用于取代之前版本中的 .storybook/config.js 文件。
main.ts 文件的主要目的是为 Storybook 提供一个工作空间和执行环境,并定义了 Storybook 的一些默认设置。在 main.ts 中,可以指定 Storybook 的各种配置,例如:
addons: Storybook 插件的注册,用于扩展 Storybook 的各种功能。stories: Storybook 要加载的故事文件(或故事目录)的列表。webpackFinal: Storybook 使用的 webpack 配置信息。frameworks: 在 Storybook 中使用的框架(例如 React、Angular、Vue 等)。
以下是一个示例 main.ts 文件的代码片段:
module.exports = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-actions',
'@storybook/addon-links',
'@storybook/addon-knobs',
'@storybook/preset-create-react-app',
],
};
在这个示例中,我们通过 module.exports 导出了一个对象,其中包含 Storybook 的一些配置选项。stories 数组用于指定 Storybook 要加载的故事文件的路径,这里是 ../src/**/*.stories.@(js|jsx|ts|tsx),使用了通配符匹配所有文件名,我们在 src 目录下,使用任意子目录和任意 .stories.js 或 .stories.jsx 或 .stories.ts 或 .stories.tsx 后缀的文件作为故事文件。
addons 数组指定了要加载的插件列表,例如 @storybook/addon-actions、@storybook/addon-links、@storybook/addon-knobs 等。preset-create-react-app 插件提供了一个默认的 webpack 配置,使得 Storybook 可以运行 React 应用程序并进行预览。
除此之外,还可以在 main.ts 文件中指定更多的选项和配置,以满足具体的开发需求。
总之,main.ts 是一个非常重要的文件,它定义了 Storybook 的基本配置和选项。使用 main.ts 文件,可以轻松地扩展 Storybook 的功能,并定制您自己的故事应用程序。
preview
在 Storybook 中,preview.ts 是一个配置文件,用于配置 Storybook 中故事预览面板的选项和设置。它是在 Storybook 6.x 版本中引入的,用于取代之前版本中的 .storybook/preview.js 文件。
preview.ts 文件的主要目的是为了提供 Storybook 预览面板上的默认选项和设置,并且可以在文件中设置一些全局的附加行为。在 preview.ts 中,可以设置一些暴露给所有故事的参数和配置选项,例如:
parameters: 故事参数的配置,可以传递基于组件的配置选项。decorators: 故事装饰器函数,可以允许为所有故事应用相同的修饰效果。globalTypes: 全局类型需要在 knob 中使用的存储。
以下是一个示例 preview.ts 文件的代码片段:
import { addParameters } from '@storybook/react';
import { withKnobs } from '@storybook/addon-knobs';
addParameters({
options: {
showRoots: true,
}
});
const globalDecorator = (Story) => (
<div style={{ margin: '3em' }}>
<Story />
</div>
);
export const decorators = [
globalDecorator,
withKnobs,
];
在这个示例中,我们在 preview.ts 文件中导入了 addParameters 函数和 withKnobs 函数。然后,我们调用 addParameters 函数并传递一个对象作为参数,来设置 options 属性,在这里我们设置了 showRoots 属性为 true,这个设置可以让故事列表中更好地显示文件系统结构。
接下来,我们定义了一个名为 globalDecorator 的函数,这个函数作为所有故事的修饰器,并且将所以故事减少上下左右的边距。最后,将装饰器数组导出的方式将其设置为全局“修饰器”的一个列表,这里包括以上定义和 withKnobs 修饰器,以便在所有故事里面使用。
总之,在 preview.ts 文件中,可以使用 Storybook 插件和工具,来定制化预览面板的行为和选项,同时,还可以将全局参数和装饰器应用于所有故事中。 preview.ts 可以让预览面板变得更加易用和实用,具有更加灵活的配置能力。
@storybook/react 中导出的模块对象
import { ComponentMeta, ComponentStory, ComponentStoryFn, ComponentStoryObj, Decorator, DecoratorFn, Loader, Meta, Preview, ReactRenderer, Story, StoryContext, StoryFn, StoryObj, composeStories, composeStory, configure, forceReRender, raw, setGlobalConfig, setProjectAnnotations, storiesOf } from '@storybook/react';
ComponentMate:Storybook 组件元数据类型,用于描述组件的相关信息; (新版本中已移除)ComponentMeta:Storybook 组件元数据类型,用于描述组件的相关信息;(新版本中已移除)ComponentStory:Storybook 组件故事类型;(新版本中已移除)ComponentStoryObj:Storybook 组件故事对象类型;(新版本中已移除)Decorator:Storybook 装饰器类型;DecoratorFn:Storybook 装饰器函数类型;(新版本中已移除)Loader:用于加载 Storybook 组件的函数;Meta:Storybook 组件元数据类型;Preview:Storybook 组件预览类型;ReactRenderer:Storybook React 渲染器类型,用于渲染 React 组件;Story:Storybook 组件故事类型;(新版本中已移除)StoryContext:Storybook 故事上下文类型;StoryFn:Storybook 故事函数类型;StoryObj:Storybook 故事对象类型;composeStories:Storybook 故事合并函数;composeStory:Storybook 单一故事合并函数;configure:用于配置 Storybook 的函数;forceReRender:Storybook 强制重新渲染函数;raw:Storybook 预览渲染原始文本的函数;setGlobalConfig:Storybook 设置全局配置函数;(新版本中已移除)setProjectAnnotations:Storybook 设置项目注解的函数;storiesOf:Storybook 创建故事对象的函数。 这些模块成员提供了 Storybook 开发组件和故事所需的基本类型、API 和工具函数。它们是 Storybook 中经常使用的一部分模块成员,熟悉它们可以帮助 Storybook 开发更加顺利。
Storybook 装饰器类型Decorator
在 Storybook 中,Decorator 是一种高阶组件(HOC)、函数或对象,可以用于修饰一个组件,增加额外的功能或行为。Decorator 可以应用于 Storybook 的预览和故事中,用于装饰预览环境或故事,使其更具有可定制性和复用性。
Decorator可以在 Storybook 中实现多种功能,例如:- 更改预览区的大小、背景样式等;
- 注入全局样式,主题等;
- 给组件添加相关的说明文件,API 文档等;
- 对组件进行自动化测试等;
Decorator可以使一个函数,示例如下: 在 Storybook 中,Decorator是一种高阶组件(HOC)、函数或对象,可以用于修饰一个组件,增加额外的功能或行为。Decorator 可以应用于 Storybook 的预览和故事中,用于装饰预览环境或故事,使其更具有可定制性和复用性。
Decorator 可以在 Storybook 中实现多种功能,例如:
- 更改预览区的大小、背景样式等;
- 注入全局样式、主题等;
- 给组件添加相关联的说明文件、API 文档等;
- 对组件进行自动化测试等。
Decorator 可以是一个函数,示例如下:
import { withKnobs } from '@storybook/addon-knobs';
export default {
title: 'Example/Button',
component: Button,
decorators: [withKnobs],
};
export const Primary = () => <Button>Primary Button</Button>;
这个例子中,withKnobs 是一个 Storybook 官方提供的装饰器,用于在 Storybook 预览页中启用「Knobs」插件,可以方便地修改组件的属性和状态。decorators 属性就是用于注册这个装饰器的。
当然,Decorator 也可以是一个对象,示例如下:
import { ThemeProvider } from 'styled-components';
const ThemeDecorator = (storyFn) => (
<ThemeProvider theme={appTheme}>
{storyFn()}
</ThemeProvider>
);
export default {
title: 'Example/Button',
component: Button,
decorators: [ThemeDecorator],
};
export const Primary = () => <Button>Primary Button</Button>;
这个例子中,ThemeDecorator 是一个用于注入 styled-components 主题到组件中的装饰器。
可以看到,Decorator 可以以多种形式出现,都是为了修饰组件。在 Storybook 中,Decorator 是很重要的一个工具,可以帮助我们更轻松、高效地开发和维护组件。
用于加载 Storybook 组件的函数 Loader
在 Storybook 中,Loader 组件用于在 Storybook UI 加载过程中显示加载器或者占位符,直到 Storybook UI 完成加载,并渲染完整个 Storybook。
Loader 组件通常会自动加载,包括加载所有的组件、故事和 addon 等。加载完成之后,Storybook 将这些内容渲染到页面上。在加载过程中,Loader 组件也可以指定展示一些自定义的样式或动画。
Storybook 默认提供了一个名为 loading 的参数组,其中包含了一个加载组件 PreviewLoading,用于在 Storybook 加载过程中展示加载器或占位符。在 Storybook 6.3 及以上版本中,还可以自定义 Loader 组件。
以下是一个示例,展示如何自定义 Loader 组件:
export const CustomLoader: Story = () => (
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
backgroundColor: 'gray',
color: 'white',
fontSize: '2em'
}}
>
<span>Loading Storybook</span>
<span className="loading-icon" style={{ marginLeft: '10px' }}>
{/* 加载动画 */}
</span>
</div>
);
export const parameters = {
loading: CustomLoader,
};
在这个例子中,我们定义了一个名为 CustomLoader 的 Story 组件,可以通过自定义样式和内容来展示自定义的加载动画或占位符。
然后,我们把 CustomLoader 组件传递给 parameters 对象的 loading 属性,以自定义 Storybook 的加载动画。在这个例子中,我们将背景设置为灰色,垂直和水平居中,同时在 Loading 文字后面加上了一个加载动画。
在运行 Storybook 时,就会使用自定义的加载器组件,来代替默认的加载器组件,展示您自定义的加载效果。
Storybook 组件元数据类型 Meta
在 Storybook 中,Meta 是一个对象,用于描述一个组件或模块的信息,包括标题、版本号、文档、参数等等。Meta 对象是导出 Storybook 组件时作为默认导出模块的一个类型,用于展示组件在 Storybook 中的相关信息。
Meta 对象包含以下属性:
title: 组件的标题component: 对应的组件parameters: 其他的参数配置,如 controls、docs、actions 等decorators: 组件装饰器argTypes: 用于 Storybook 向组件传递 props 时对 prop 进行描述
下面是 Meta 对象的基本例子:
import { Meta } from '@storybook/react';
export default {
title: 'Button',
component: Button,
argTypes: {
backgroundColor: { control: 'color' },
},
} as Meta;
在这个例子中,Meta 对象包含了组件的标题和组件本身,以及对应组件使用到的参数类型等信息。可以在 Storybook 中通过 Meta 对象来展示组件的信息,如演示及用法等。
通过定义 Meta 对象,可以让组件的信息更加规范化,同时也可以确保在 Storybook 中以同样的方式使用组件,提高了代码的可读性和可维护性。
Storybook 组件预览类型Preview
在 Storybook 中,Preview 是用于展示组件的预览和故事的代码区域,它是 Storybook 的核心之一。Preview 组件是和 Story 组件一起使用,用于呈现组件及其属性和交互方式。
Preview 组件有以下一些作用:
- 展示组件的视图及组件所使用到的 props 和状态;
- 提供组件交互式的编辑界面,包括 knobs、actions、events、argTypes 等;
- 展示组件的故事列表,可以在列表中切换故事查看效果;
- 提供对代码区域的搜索、筛选等功能。
Preview 组件也支持定制化,开发者可以自定义 Preview 组件,实现完全定制化的展示效果。
通常,Preview 组件由 Storybook 的默认主题提供,不需要开发人员对其进行过多干预。但是,如果需要自定义组件的展示效果,可以使用 addDecorator 函数来添加自定义的装饰器和样式以及控制面板,或者使用 addParameters 函数添加自定义配置项,以确保组件在预览中拥有理想的展示效果。
以下是一个基本示例,展示如何使用 Preview 组件和多个 Story 组件来展示组件及其属性和状态:
import { Meta, Story } from '@storybook/react';
import { Button, ButtonProps } from './Button';
export default {
title: 'Example/Button',
component: Button,
} as Meta;
const Template: Story<ButtonProps> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
在这个例子中,使用 Meta 对象定义了组件的信息,包括标题和组件本身。然后,定义了 Template 组件作为 Story 组件的基础模板,并定义了 Primary 和 Secondary 两个故事,这两个故事都应用 Template 组件,并分别添加了不同的 props。
最后,可以使用 Preview 组件来展示这些故事和组件,如下所示:
import { Primary, Secondary } from './Button.stories';
// 以组件为单位(storybook的单位)
export default {
title: 'Example/Preview',
};
export const ButtonPreview = () => (
<>
<h1>Previewing the Button Component</h1>
<Preview>
<Primary />
<Secondary />
</Preview>
</>
);
在渲染 Preview 组件时,将 Primary 和 Secondary 故事录入 Preview 中即可展示组件及其相关属性和状态。
Storybook React 渲染器类型,用于渲染 React 组件 ReactRenderer
在 Storybook 中,ReactRenderer 是将 React 组件和 Storybook UI 集成在一起的组件。它是 Storybook UI 渲染 React 组件的关键组件之一。
ReactRenderer 组件以插件的形式集成到 Storybook,提供了诸如渲染、交互、UI 状态、事件绑定和错误处理等功能。接口使用不到,一般不需直接使用。
以下是一个在 Storybook 中使用 ReactRenderer 的示例:
import { Button } from './Button';
const Template = (args) => <Button {...args} />;
export default {
title: 'Example/Button',
component: Button,
parameters: {
react: {
component: Template,
// 定义Props类型
props: {
primary: true,
label: 'Button',
},
},
},
};
在这个例子中,定义了一个 Button 组件,以及一个 Template 组件作为 Button 故事的基础模板。然后,使用 parameters 属性中的 react 对象来配置 ReactRenderer 组件。react 对象中包含了 component 和 props 属性,其中,component 属性指定渲染的组件,props 属性定义了组件的 props。
进行这种配置之后,在 Storybook UI 中就可以看到渲染出的 Button 组件和模板中定义的默认 props。可以通过交互式的控件来修改 props,并实时预览组件的变化。
需要注意的是,ReactRenderer 组件应该由 Storybook 自动集成和加载,开发者一般不需要手动指定或调用它。如果需要自定义 ReactRenderer 组件的配置参数,可以直接在参数配置中指定即可。
Storybook 故事上下文类型StoryContext
在 Storybook 中,StoryContext 是一个 React 上下文,它提供了 Storybook UI 所需的组件和故事内容,可以在 Storybook 插件和组件中使用。
使用 useContext 函数可以获取 StoryContext 实例,通过 StoryContext 实例可以获取到当前正在预览的组件中,每个故事的信息,例如组件名称、故事名称、故事 ID 和其他元数据等等。
以下是一个示例,展示了如何使用 useContext 函数在 Storybook 中获取当前正在预览的组件和故事的信息:
import React, { useContext } from 'react';
import { StoryContext } from '@storybook/addons';
export const MyAddon = () => {
const context = useContext(StoryContext);
const story = context?.storyId;
const component = context?.componentId;
return (
<div>
<div>Currently viewing the story {story} in the component {component}</div>
</div>
);
};
在这个例子中,我们使用了 useContext 函数来获取 StoryContext 实例,并从实例中提取出当前故事的 ID 和组件的 ID。这里将这些信息呈现在 UI 中,以便开发者了解当前所处的故事和组件。
需要注意的是,StoryContext 只能在 Storybook 插件或组件中使用,不能在 React 组件中使用。如果需要使用 StoryContext 中的信息更新 React 组件的状态或者渲染页面,可以考虑传递相关的参数或者方法到 React 组件中,以便实现组件的状态或展示效果的重载。
Storybook 故事函数类型 StoryFn
在 Storybook 中,StoryFn 是一个函数,用于返回一个 React 组件作为 Storybook UI 预览中显示的内容。StoryFn 函数可以在 Storybook 插件中使用,也可以在故事描述(Story)中使用。
在 Storybook 插件中使用 StoryFn 函数的示例:
import { StoryFn } from '@storybook/addons';
import MyButton from './MyButton';
export const myAddon: StoryFn = (context) => (
<div>
<MyButton>Hello, I'm a button!</MyButton>
</div>
);
在上面的示例中,我们定义了一个名为 myAddon 的 Storybook 插件,通过 StoryFn 函数返回一个包含 MyButton 组件的 div 元素。该组件将在 Sto
rybook UI 中展示,用于呈现自定义内容。
在故事描述(Story)中使用 StoryFn 函数的示例:
import { StoryFn } from '@storybook/addons';
import MyButton from './MyButton';
export const Basic: StoryFn = () => <MyButton>Hello, I'm a button!</MyButton>;
在上面的示例中,我们定义了一个名为 Basic 的故事描述,通过 StoryFn 函数返回一个包含 MyButton 组件的 JSX 元素。在 Storybook UI 中,可以展开该故事,查看和交互该组件。
通过使用 StoryFn 函数,可以实现在 Storybook 中显示自定义内容和逻辑。
Storybook 故事对象类型 StoryObj
在 Storybook 中,StoryObj 是一个包含组件故事相关信息的对象,可以在 Storybook 插件和故事描述(Story)中使用。它包含以下属性:
id: string:故事 IDkind: string:故事分类(组件名称)name: string:故事名称storyFn: function:返回 React 元素的函数parameters: object:包含故事元数据和参数的对象
可以通过 addDecorator 函数和 configure 函数来访问 StoryObj 对象。
以下是一个示例,展示了如何使用 addDecorator 函数和 StoryObj 对象来在 Storybook 中为所有故事添加相同的装饰器:
import { addDecorator } from '@storybook/react';
import { StoryObj } from '@storybook/addons';
const withMyDecorator = (storyFn: StoryFn, context: StoryObj) => {
return (
<div>
<h1>This is my custom decorator!</h1>
{storyFn(context)}
</div>
);
};
addDecorator(withMyDecorator);
在这个例子中,我们定义了一个名为 withMyDecorator 的函数,它接收一个 StoryFn 和一个 StoryObj 对象作为参数,返回一个包含自定义装饰器和故事元素的 JSX 元素。在 addDecorator 函数中,我们将 withMyDecorator 函数作为装饰器函数传递给 Storybook,以便为所有故事添加相同的装饰器。
需要注意的是,StoryObj 对象可以在 Storybook 插件和故事描述中访问。例如,在故事描述中,可以通过 kinds、name 和 parameters 等属性来获取和操作当前故事的信息和设置。
此外,由于 TypeScript 的类型推断能力,可以在代码编辑器中查看 StoryObj 类型的属性和方法,以方便编写正确的代码。
Storybook 故事合并函数composeStories
在 Storybook 中,composeStories 是一个函数,用于将多个故事合成一个组件,并返回一个撰写好的故事集。通常在编写 Storybook 组件时使用。
以下是一个示例,展示了如何使用 composeStories 函数将多个故事合成一个组件:
import { composeStories } from '@storybook/testing-react';
import * as PrimaryButtonStories from '../Button/PrimaryButton.stories';
import * as SecondaryButtonStories from '../Button/SecondaryButton.stories';
const { Primary, LargePrimary, SmallPrimary } = composeStories(PrimaryButtonStories);
const { Secondary, LargeSecondary, SmallSecondary } = composeStories(SecondaryButtonStories);
export default {
title: 'Button/CombinedButton',
};
export const Combined = () => (
<div>
<Primary />
<Secondary />
<LargePrimary />
<LargeSecondary />
<SmallPrimary />
<SmallSecondary />
</div>
);
在这个例子中,我们导入两个 Button 组件故事,分别是 PrimaryButtonStories 和 SecondaryButtonStories。然后调用 composeStories 函数,将每个故事中的 Template 组件合成为新的组件。接着使用 Combined 故事描述,将新组件渲染到 Storybook UI 中。
composeStories 的参数是一个包含故事的对象,每个故事应包含 Template 组件,并按照约定的格式进行导出,例如:
export const Basic = () => <MyButton>Hello, I'm a button!</MyButton>;
Basic.storyName = 'Basic';
需要注意的是,composeStories 函数返回的对象中包含了拆分并且以相同方式导出的所有部分。这使得内部部件可以在上层组件中进行定位选取。
使用 composeStories 函数可以有效简化对多个故事的编写、管理和组合,提高工作流程的效率。
Storybook 单一故事合并函数composeStories
在 Storybook 中,composeStory 是一个函数,用于将多个故事合成一个故事,并返回一个撰写好的故事。通常在编写 Storybook 组件时使用。
以下是一个示例,展示了如何使用 composeStory 函数将多个故事合成一个故事:
import { composeStory } from '@storybook/testing-react';
import * as PrimaryButtonStories from '../Button/PrimaryButton.stories';
const largePrimary = composeStory(PrimaryButtonStories.Large, {
args: {
label: 'Click me!',
},
});
export default {
title: 'Button/LargePrimaryButton',
argTypes: {
label: { control: 'text' },
},
};
export const LargePrimary = largePrimary.bind({});
LargePrimary.storyName = 'Large Primary Button';
在这个例子中,我们导入 PrimaryButtonStories 组件故事,并使用 composeStory 函数将 Large 故事和自定义参数组合为一个故事。然后使用 bind 函数绑定新的参数,将其捆绑到 LargePrimary 故事中,并将其命名为 Large Primary Button。
composeStory 的第一个参数是要合成的故事,它应包含 Template 组件,并按照约定的格式进行导出。第二个参数是一个对象,包含用于重新定义和设置故事参数值的设置。
需要注意的是,composeStory 函数返回的故事还需要使用 bind 函数将新的参数值与故事绑定,这样才能在 Storybook UI 中展开和呈现。
使用 composeStory 函数可以有效简化对多个故事的编写、管理和组合,提高工作流程的效率。同时,它还可以为开发者提供更灵活和高效的途径来管理和设置故事参数。
用于配置 Storybook 的函数 configure
configure用于配置 Storybook 的各种选项和扩展。在项目中启动 Storybook 之前,我们需要使用 configure 函数对其进行配置,以确保 Storybook 能够正常运行、渲染和加载所有相关组件和文件。
以下是一个使用 configure 函数配置 Storybook 的示例:
import { configure } from '@storybook/react';
function loadStories() {
require('../src/stories');
}
configure(loadStories, module);
在这个示例中,我们首先从 @storybook/react 中导入 configure 函数。然后,在 loadStories 函数中,我们引入了 src/stories 目录中所有的 Story 文件,以便 Storybook 能够加载它们。最后,我们调用 configure 函数,将 loadStories 和 module 作为参数传递给它。
除了 loadStories 和 module,configure 函数还可以接受其他可选参数,包括 addDecorator、addParameters、storiesOf等,这些参数允许我们进行更多的配置,例如添加装饰器、修改主题、设置故事类型等。
使用 configure 函数不仅可以帮助我们更好地配置和管理 Storybook,还可以帮助我们在开发过程中更加高效地测试和验证我们的组件。
Storybook 强制重新渲染函数forceReRender
forceReRender 是 React 测试工具中的一个函数,用于强制重新渲染一个组件。该函数接受一个无参函数作为参数,在这个函数中对组件状态进行修改,然后再调用 forceReRender 函数,就可以触发组件重新渲染。
这个函数通常用于测试组件在不同状态下的渲染结果。例如,当修改组件状态时,需要确保组件在状态改变后能够正确地重新渲染,而不是仅仅重新渲染旧状态。
以下是一个示例:
import { render, fireEvent } from '@testing-library/react';
import App from './App';
test('App renders correctly with new props', () => {
const { container } = render(<App />);
// 点击按钮触发状态改变
const button = container.querySelector('button');
fireEvent.click(button);
// 在新状态下重新渲染组件
render(<App />);
// 确保渲染结果正确
expect(container.textContent).toContain('new state');
});
在这个示例中,我们首先渲染 App 组件,并找到包含按钮元素的容器。然后,我们模拟点击按钮,使用 forceReRender 函数触发重新渲染组件,并把容器再次赋值给 container 变量。最后,我们使用 expect 函数来验证新渲染结果是否正确。
Storybook 预览渲染原始文本的函数raw
在 Storybook 中,raw 是一种特殊的故事(story)格式,用于在 Storybook 中显示原始的HTML、CSS或JS代码。可以使用 addDecorator 和 addParameters 函数来配置 raw 故事,并控制其在 Storybook 中的外观和行为。
以下是一个使用 raw 故事的示例:
import { addDecorator, addParameters } from '@storybook/react';
import { withHTML } from '@whitespace/storybook-addon-html/react';
const htmlSource = `
<div class="my-component">
<h1>Hello, world!</h1>
</div>
`;
addDecorator(withHTML);
addParameters({
html: {
// 将 HTML 编辑器的选项配置为开启行号,自动换行等
prettier: {
tabWidth: 2,
useTabs: true,
htmlWhitespaceSensitivity: 'ignore',
},
// 允许在 HTML 编辑器中将编辑器内容传递到其他地方
copy: true,
// 定义要显示的 HTML 代码字符串
source: {
html: htmlSource,
},
},
});
export default {
title: 'Raw Example',
};
export const Default = () => htmlSource;
在这个示例中,我们首先从 @storybook/react 和 @whitespace/storybook-addon-html/react 中导入 addDecorator 和 addParameters 函数。然后,我们创建了一个包含原始代码的字符串 htmlSource,该代码包括一个 div 元素和一个 h1 标题。接着,我们使用 withHTML 装饰器将 raw 故事包装在一个HTML编辑器中,并使用 addParameters 函数为 HTML 编辑器传递选项,配置项等信息。我们还定义了自定义的 source.html 对象,将我们的 htmlSource 字符串传递给该对象。最后,我们定义了一个 Default 组件,该组件返回 htmlSource 字符串本身,将其显示为 raw 故事。
通过使用 raw 故事,我们可以方便地在 Storybook 中显示 HTML、CSS、JS或其他类型的代码,以向项目的其他开发人员展示和共享我们的代码。同时,可以使用 HTML 编辑器设置选项对代码进行格式化和调整。
Storybook 设置项目注解的函数setProjectAnnotations
setProjectAnnotations 是 Storybook 中的一个 API,用于为整个 Storybook 项目添加全局注解(annotations)。注解是一种特殊的 Storybook 功能,允许开发人员在 Storybook 页面中添加自定义文本、图像或可视化元素,以对某个部分或组件进行说明或展示。
使用 setProjectAnnotations 函数,可以设置整个 Storybook 项目的全局注解,并将它们添加到 Storybook UI 左侧“Storybook”菜单下的“Annotations”面板中。这些注解可以是文本、HTML、SVG等形式,并且支持MDX格式的文档。
以下是一个示例:
import { setProjectAnnotations } from '@storybook/addon-docs/blocks';
setProjectAnnotations([
{
title: 'The storybook project',
text: 'This is a demo storybook project',
},
{
title: 'Useful links',
mdx: '#### [React](https://reactjs.org/)\n#### [Storybook](https://storybook.js.org/)',
},
{
title: 'Logo',
image: 'https://via.placeholder.com/150',
},
]);
在这个示例中,我们首先从 @storybook/addon-docs/blocks 中导入 setProjectAnnotations 函数。然后,我们创建了一个包含三个注解对象的数组,每个注解对象都包含一个标题和不同类型的内容(文本、MDX文档和图像)。最后,我们调用 setProjectAnnotations 函数,将注解添加到 Storybook 项目中。
在 Storybook UI 中,可以通过展开左侧菜单栏中的“Storybook”菜单,点击“Annotations”面板来查看添加的注解。
需要注意的是,注解只在 Storybook UI 中显示,并不影响实际的组件渲染。此外,这些全局注解可以在单个或多个 Story 文件中添加自定义注解,以向用户提供更详细的信息。
Storybook 创建故事对象的函数storiesOf
storiesOf 是 Storybook 中用于创建故事部分的函数之一。它接受两个参数:故事的名称和故事所属的模块。使用 storiesOf 函数,可以创建包含一个或多个故事的故事集部分,分别测试和呈现组件的各个方面。
以下是一个示例:
import { storiesOf } from '@storybook/react';
import Button from './Button';
storiesOf('Button', module)
.add('with text', () => (
<Button>Hello Button</Button>
))
.add('with emoji', () => (
<Button>
<span role="img" aria-label="so cool">
😎👍💯
</span>
</Button>
));
在这个示例中,我们首先通过 import 导入了 Button 组件。接着,我们使用 storiesOf 函数创建了一个名为 'Button' 的故事集部分,并将其添加到模块中。然后,我们通过 add 方法向部分中添加了两个故事:一个带有文本内容的故事和一个带有表情符号的故事。在每个故事中,我们呈现了 Button 组件,并分别提供了不同的属性和内容。
通过使用 storiesOf 函数,可以方便地创建和管理故事集部分,以测试和演示组件的各个方面。同时,可以通过一些其他的API如:
addDecorator()来添加装饰器addParameters():来设置自定义参数add()来创建故事
例如,如下代码示例演示如何使用以上函数和方法来创建、配置和渲染故事:
import { storiesOf } from '@storybook/react';
import { withKnobs, text, boolean } from '@storybook/addon-knobs';
import Button from './Button';
storiesOf('Button', module)
.addDecorator(withKnobs)
.addParameters({
docs: {
description: {
component: 'A simple button component.',
},
},
})
.add('simple', () => (
<Button
primary={boolean('Primary', false)}
disabled={boolean('Disabled', false)}
>
{text('Label', 'Button')}
</Button>
));
在这个示例中,我们首先使用 storiesOf 函数创建了一个名为 'Button' 的故事集部分,并将其添加到模块中。然后,我们使用 addDecorator 方法添加了 withKnobs 装饰器,以启用故事的交互式 Props 控件。接着,我们使用 addParameters 方法设置了一个文档描述参数,以指定组件的描述信息。最后,我们使用 add 方法创建了一个名为 'simple' 的故事,并提供了一些属性和值,这些属性和值由 withKnobs 控件提供,并使我们可以在故事中实时修改它们。
通过使用这些函数和方法,我们可以在 Storybook 中方便地创建和配置各种类型的故事,并测试和演示组件的各个方面。 本文参考storybook.js.org/tutorials/i…