name: storybook-configuration user-invocable: false description: 用于在项目中设置或配置Storybook。覆盖主配置、添加物、构建器和框架特定设置。 allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
Storybook - 配置
配置Storybook以实现最佳开发体验,包括正确的添加物、构建器和框架集成。
关键概念
主配置
.storybook/main.ts 是主配置文件:
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
};
export default config;
预览配置
.storybook/preview.ts 配置故事如何渲染:
import type { Preview } from '@storybook/react';
import '../src/index.css';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
globalTypes: {
theme: {
description: '组件的全局主题',
defaultValue: 'light',
toolbar: {
title: '主题',
icon: 'circlehollow',
items: ['light', 'dark'],
dynamicTitle: true,
},
},
},
};
export default preview;
添加物
添加物扩展Storybook功能:
- @storybook/addon-essentials - 核心添加物包
- @storybook/addon-interactions - 交互测试
- @storybook/addon-a11y - 可访问性测试
- @storybook/addon-links - 故事导航
- @storybook/addon-coverage - 代码覆盖率
- storybook-dark-mode - 深色模式切换
最佳实践
1. 使用TypeScript配置
类型安全配置防止错误:
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
viteConfigPath: './vite.config.ts',
},
},
},
};
export default config;
2. 配置故事模式
指定故事位置:
const config: StorybookConfig = {
stories: [
'../src/**/*.mdx',
'../src/**/*.stories.@(js|jsx|ts|tsx)',
// 包含特定目录
'../components/**/*.stories.tsx',
'../features/**/*.stories.tsx',
// 排除模式
'!../src/**/*.test.stories.tsx',
],
};
3. 启用自动文档
自动生成文档:
const config: StorybookConfig = {
docs: {
autodocs: 'tag', // 为带有'autodocs'标签的故事生成自动文档
// 或
autodocs: true, // 为所有故事生成自动文档
},
};
4. 配置核心添加物
在预览中自定义添加物行为:
import type { Preview } from '@storybook/react';
const preview: Preview = {
parameters: {
// 操作添加物
actions: { argTypesRegex: '^on[A-Z].*' },
// 控制添加物
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
exclude: ['className', 'style'],
},
// 视口添加物
viewport: {
viewports: {
mobile: {
name: '移动端',
styles: { width: '375px', height: '667px' },
},
tablet: {
name: '平板',
styles: { width: '768px', height: '1024px' },
},
},
},
// 背景添加物
backgrounds: {
default: 'light',
values: [
{ name: '浅色', value: '#ffffff' },
{ name: '深色', value: '#1a1a1a' },
],
},
},
};
5. 添加全局装饰器
用提供者包装所有故事:
import { Preview } from '@storybook/react';
import { ThemeProvider } from '../src/theme';
const preview: Preview = {
decorators: [
(Story) => (
<ThemeProvider>
<div style={{ padding: '2rem' }}>
<Story />
</div>
</ThemeProvider>
),
],
};
常见模式
Next.js 配置
import type { StorybookConfig } from '@storybook/nextjs';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/nextjs',
options: {
nextConfigPath: '../next.config.js',
},
},
staticDirs: ['../public'],
typescript: {
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
},
},
};
Vite 配置
import type { StorybookConfig } from '@storybook/react-vite';
import { mergeConfig } from 'vite';
const config: StorybookConfig = {
framework: {
name: '@storybook/react-vite',
options: {},
},
async viteFinal(config) {
return mergeConfig(config, {
resolve: {
alias: {
'@': '/src',
},
},
define: {
'process.env.STORYBOOK': JSON.stringify(true),
},
});
},
};
Webpack 配置
import type { StorybookConfig } from '@storybook/react-webpack5';
import path from 'path';
const config: StorybookConfig = {
framework: {
name: '@storybook/react-webpack5',
options: {},
},
webpackFinal: async (config) => {
config.resolve = {
...config.resolve,
alias: {
...config.resolve?.alias,
'@': path.resolve(__dirname, '../src'),
},
};
return config;
},
};
可访问性测试
import type { Preview } from '@storybook/react';
const preview: Preview = {
parameters: {
a11y: {
config: {
rules: [
{
id: 'color-contrast',
enabled: true,
},
{
id: 'aria-required-attr',
enabled: true,
},
],
},
options: {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa'],
},
},
},
},
};
主题切换
import { Preview } from '@storybook/react';
import { useDarkMode } from 'storybook-dark-mode';
const preview: Preview = {
decorators: [
(Story) => {
const isDark = useDarkMode();
return (
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<Story />
</ThemeProvider>
);
},
],
};
代码覆盖率
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
addons: [
'@storybook/addon-coverage',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
};
// 在 .storybook/test-runner.ts 中
import type { TestRunnerConfig } from '@storybook/test-runner';
const config: TestRunnerConfig = {
async postRender(page, context) {
// 添加覆盖率收集
await page.coverage.startJSCoverage();
},
};
export default config;
高级模式
多框架设置
// .storybook/main.react.ts
const config: StorybookConfig = {
stories: ['../src/react/**/*.stories.tsx'],
framework: '@storybook/react-vite',
};
// .storybook/main.vue.ts
const config: StorybookConfig = {
stories: ['../src/vue/**/*.stories.ts'],
framework: '@storybook/vue3-vite',
};
自定义添加物开发
// .storybook/addons/custom-addon/register.tsx
import { addons, types } from '@storybook/manager-api';
import { AddonPanel } from '@storybook/components';
import React from 'react';
addons.register('custom-addon', () => {
addons.add('custom-addon/panel', {
type: types.PANEL,
title: '自定义面板',
render: ({ active, key }) => (
<AddonPanel active={active} key={key}>
<div>自定义添加物内容</div>
</AddonPanel>
),
});
});
// .storybook/main.ts
const config: StorybookConfig = {
addons: ['./addons/custom-addon/register'],
};
管理器配置
// .storybook/manager.ts
import { addons } from '@storybook/manager-api';
import { themes } from '@storybook/theming';
addons.setConfig({
theme: themes.dark,
panelPosition: 'right',
showPanel: true,
selectedPanel: 'storybook/actions/panel',
initialActive: 'sidebar',
sidebar: {
showRoots: true,
collapsedRoots: ['other'],
},
});
反模式
❌ 不要跳过框架类型
// 错误 - 无类型安全
const config = {
framework: '@storybook/react-vite',
};
// 正确 - 类型安全
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
framework: {
name: '@storybook/react-vite',
options: {},
},
};
❌ 不要硬编码路径
// 错误
const config: StorybookConfig = {
stories: [
'/Users/username/project/src/**/*.stories.tsx',
],
};
// 正确
const config: StorybookConfig = {
stories: [
'../src/**/*.stories.tsx',
],
};
❌ 不要包含不必要的添加物
// 错误 - 过多添加物减慢Storybook速度
const config: StorybookConfig = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-actions', // 包含在essentials中
'@storybook/addon-controls', // 包含在essentials中
'@storybook/addon-backgrounds', // 包含在essentials中
],
};
// 正确 - 仅核心添加物覆盖大多数需求
const config: StorybookConfig = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y', // 额外专业添加物
],
};
相关技能
- storybook-story-writing: 为已配置的Storybook编写故事
- storybook-component-documentation: 使用文档添加物
- storybook-play-functions: 设置交互测试添加物