Storybook 组件文档 — 隔离开发、测试和记录 UI 组件的工作台¶
一句话说明¶
Storybook 是一个独立的 UI 组件开发环境,让你在不启动整个应用的情况下开发和预览每个组件的各种状态,同时自动生成交互式文档,当前版本 v8.x。
安装与配置¶
# 在现有项目中初始化 Storybook(自动检测框架)
npx storybook@latest init
# 手动指定框架
npx storybook@latest init --type react
# 启动 Storybook 开发服务器
npm run storybook # 默认端口 6006
# 构建静态文档站点
npm run build-storybook
# 查看版本
npx storybook --version
// .storybook/main.ts — Storybook 主配置文件
import type { StorybookConfig } from '@storybook/react-vite'; // React+Vite 类型
const config: StorybookConfig = {
stories: [
'../src/**/*.mdx', // 支持 MDX 格式的文档文件
'../src/**/*.stories.@(js|jsx|mjs|ts|tsx)', // 匹配所有 story 文件
],
addons: [
'@storybook/addon-links', // 页面间导航
'@storybook/addon-essentials', // 必备插件包(controls, docs, actions 等)
'@storybook/addon-interactions', // 测试交互
'@storybook/addon-a11y', // 无障碍检查
],
framework: {
name: '@storybook/react-vite', // 使用 React + Vite 框架
options: {},
},
docs: {
autodocs: 'tag', // 有 tags: ['autodocs'] 时自动生成文档
},
};
export default config;
核心用法¶
// src/components/Button/Button.stories.ts — 组件 Story 文件
import type { Meta, StoryObj } from '@storybook/react'; // 类型导入
import { Button } from './Button'; // 导入组件
// ── Meta:整个文件的元信息 ──
const meta: Meta<typeof Button> = {
title: 'UI/Button', // 在 Storybook 侧边栏中的路径
component: Button, // 关联的组件
parameters: {
layout: 'centered', // 居中显示
},
tags: ['autodocs'], // 自动生成文档页面
argTypes: { // 控制面板配置
variant: {
control: 'select', // 用下拉框控制
options: ['primary', 'secondary', 'danger'],
},
size: {
control: 'radio', // 用单选框控制
options: ['sm', 'md', 'lg'],
},
onClick: { action: 'clicked' }, // 点击时在 Actions 面板显示
},
};
export default meta; // 必须导出 meta 作为默认导出
type Story = StoryObj<typeof meta>; // Story 类型
// ── Story:组件的一个具体状态 ──
export const Primary: Story = {
args: {
variant: 'primary', // props 值
children: '提交', // 按钮文字
size: 'md',
},
};
export const Danger: Story = {
args: {
variant: 'danger',
children: '删除',
size: 'md',
},
};
// ── 带交互测试的 Story ──
export const WithInteraction: Story = {
args: { children: '点击我' },
play: async ({ canvasElement, args }) => { // play 函数实现交互测试
const { within, userEvent, expect } = await import('@storybook/test');
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
await userEvent.click(button); // 模拟点击
expect(args.onClick).toHaveBeenCalled(); // 验证点击事件触发
},
};
实战案例¶
// ── 生信工具 UI 组件:基因表达量热力图控件 ──
// src/components/HeatmapControls/HeatmapControls.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { HeatmapControls } from './HeatmapControls';
const meta: Meta<typeof HeatmapControls> = {
title: '生信组件/热力图控件', // 在侧边栏分组显示
component: HeatmapControls,
tags: ['autodocs'],
argTypes: {
colorScheme: {
control: 'select',
options: ['Blues', 'Reds', 'Viridis', 'RdBu'], // 颜色方案选项
description: '热力图颜色映射方案',
},
minValue: {
control: { type: 'range', min: -5, max: 0, step: 0.5 }, // 滑块控制
description: '颜色映射最小值',
},
maxValue: {
control: { type: 'range', min: 0, max: 10, step: 0.5 },
description: '颜色映射最大值',
},
showDendrogram: {
control: 'boolean', // 开关控制
description: '是否显示聚类树',
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
// 默认状态
export const Default: Story = {
args: {
colorScheme: 'RdBu',
minValue: -3,
maxValue: 3,
showDendrogram: true,
},
};
// 无聚类树状态
export const NoCluster: Story = {
args: { ...Default.args, showDendrogram: false },
};
// 自定义颜色状态
export const BlueScheme: Story = {
args: { ...Default.args, colorScheme: 'Blues', minValue: 0 },
};
// .storybook/preview.ts — 全局装饰器配置
import type { Preview } from '@storybook/react';
import '../src/styles/global.css'; // 引入全局样式,让组件预览有正确样式
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i, // 包含 color 的 prop 自动用颜色选择器
date: /Date$/i, // 包含 Date 的 prop 自动用日期选择器
},
},
},
decorators: [
(Story) => ( // 全局包装器,给所有 Story 加上容器
<div style={{ padding: '24px' }}>
<Story />
</div>
),
],
};
export default preview;
常见报错与解决¶
| 报错 | 原因 | 解决方法 |
|---|---|---|
Cannot find module '@storybook/xxx' | 插件未安装 | 运行 npx storybook@latest upgrade |
| Story 不显示组件 | export default 缺失 | 确保文件有 export default meta |
| Controls 面板为空 | argTypes 未配置 | 添加 argTypes 或用 autodocs |
| 样式丢失 | 全局 CSS 未引入 | 在 .storybook/preview.ts 中 import 样式 |
| HMR 不生效 | Vite 缓存问题 | 重启 Storybook 或清除 .storybook-static |
速查表¶
# 命令
npm run storybook # 启动开发服务器(默认 :6006)
npm run build-storybook # 构建静态站点到 storybook-static/
npx storybook@latest upgrade # 升级 Storybook 版本
# Story 文件命名约定
Button.stories.ts # TypeScript Story 文件
Button.stories.tsx # 带 JSX 的 Story 文件
Button.mdx # Markdown + Story 混合文档
# 常用 Addons
addon-essentials # controls, docs, actions, backgrounds, viewport
addon-a11y # 无障碍检查
addon-interactions # play 函数交互测试
addon-coverage # 测试覆盖率
# 部署文档站
npx chromatic # 发布到 Chromatic(Storybook 官方云服务)
# 官方文档:https://storybook.js.org/docs