Storybook 基础使用 #

界面概览 #

Storybook 启动后,你将看到以下界面布局:

text
┌─────────────────────────────────────────────────────────────────────────┐
│  Storybook                                    🔍 搜索    ⚙️ 设置        │
├────────────────┬────────────────────────────────────────────────────────┤
│                │                                                        │
│   侧边栏        │                    Canvas 画布                        │
│   (Sidebar)    │                                                        │
│                │         ┌─────────────────────────┐                   │
│  📁 Components │         │                         │                   │
│    📄 Button   │         │    [组件预览区域]        │                   │
│      Primary   │         │                         │                   │
│      Secondary │         │    Primary Button       │                   │
│      Disabled  │         │                         │                   │
│    📄 Input    │         └─────────────────────────┘                   │
│      Default   │                                                        │
│      WithIcon  │                                                        │
│                │                                                        │
│  📁 Pages      │                                                        │
│    📄 Home     │                                                        │
│                │                                                        │
├────────────────┴────────────────────────────────────────────────────────┤
│  Addons 面板 (Controls | Actions | Interactions | ...)                  │
│  ┌────────────────────────────────────────────────────────────────────┐ │
│  │ variant: [primary ▼]  size: [medium ▼]  disabled: [ ]             │ │
│  └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘

侧边栏(Sidebar) #

导航结构 #

侧边栏按照层级组织 Stories:

text
📁 Components          ← 目录(由 title 生成)
  📄 Button            ← 组件(Story 文件)
    Primary            ← Story(具体状态)
    Secondary
    Disabled
  📄 Input
    Default
    WithIcon
    Error

📁 Pages
  📄 Home
    LoggedIn
    LoggedOut

搜索功能 #

使用搜索框快速定位组件:

text
┌─────────────────────────────────────┐
│ 🔍 搜索组件或 Story...               │
└─────────────────────────────────────┘

支持:
- 组件名称搜索
- Story 名称搜索
- 模糊匹配
- 快捷键:/ 或 Ctrl/Cmd + K

折叠与展开 #

text
点击目录或组件可以折叠/展开:

▶ 📁 Components     ← 折叠状态
▼ 📁 Components     ← 展开状态
  📄 Button
    Primary
    Secondary

Canvas 画布 #

组件预览区域 #

Canvas 是组件的主要展示区域:

text
┌─────────────────────────────────────────────────────────────┐
│  Canvas                                                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                    ┌─────────────┐                         │
│                    │   Button    │  ← 组件渲染区域          │
│                    └─────────────┘                         │
│                                                             │
│  工具栏:                                                   │
│  🔲  📐  🎨  📱  🔗                                        │
│  │   │   │   │   │                                         │
│  │   │   │   │   └─ 在新窗口打开                           │
│  │   │   │   └───── 视口切换                               │
│  │   │   └───────── 背景切换                               │
│  │   └───────────── 测量/轮廓                              │
│  └───────────────── 全屏                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

工具栏功能 #

图标 功能 说明
🔲 全屏 切换全屏模式
📐 测量 显示组件尺寸信息
🎨 背景 切换背景颜色
📱 视口 切换设备视口
🔗 新窗口 在新窗口打开 Story

视口切换 #

text
┌─────────────────────────────────────┐
│ 📱 视口选择                          │
├─────────────────────────────────────┤
│                                     │
│  ┌─────────┐                       │
│  │ Mobile  │  375 x 812            │
│  └─────────┘                       │
│  ┌─────────┐                       │
│  │ Tablet  │  768 x 1024           │
│  └─────────┘                       │
│  ┌─────────┐                       │
│  │ Desktop │  1280 x 800           │
│  └─────────┘                       │
│                                     │
└─────────────────────────────────────┘

Docs 文档页面 #

文档视图 #

点击 Docs 标签查看组件文档:

text
┌─────────────────────────────────────────────────────────────┐
│  Canvas | Docs                                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Button                                                     │
│  ==========                                                 │
│                                                             │
│  按钮组件用于触发操作。                                      │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Controls                                             │   │
│  │ variant: [primary ▼]                                │   │
│  │ size: [medium ▼]                                    │   │
│  │ disabled: [ ]                                       │   │
│  │ children: "Button"                                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Stories                                                    │
│  -------                                                    │
│                                                             │
│  Primary                                                    │
│  ┌─────────────┐                                           │
│  │   Button    │                                           │
│  └─────────────┘                                           │
│                                                             │
│  Secondary                                                  │
│  ┌─────────────┐                                           │
│  │   Button    │                                           │
│  └─────────────┘                                           │
│                                                             │
│  Args Table                                                 │
│  ----------                                                 │
│  | Name | Description | Type | Default |                   │
│  |------|-------------|------|---------|                   │
│  | variant | 按钮类型 | string | 'primary' |               │
│  | size | 按钮尺寸 | string | 'medium' |                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

自动文档 #

Storybook 自动从组件生成文档:

javascript
// Button.jsx
import React from 'react';
import PropTypes from 'prop-types';

/**
 * 按钮组件用于触发操作
 * @param {Object} props - 组件属性
 * @param {'primary'|'secondary'} props.variant - 按钮变体类型
 * @param {'small'|'medium'|'large'} props.size - 按钮尺寸
 * @param {boolean} props.disabled - 是否禁用
 */
const Button = ({ variant, size, disabled, children, onClick }) => {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

Button.propTypes = {
  variant: PropTypes.oneOf(['primary', 'secondary']),
  size: PropTypes.oneOf(['small', 'medium', 'large']),
  disabled: PropTypes.bool,
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func,
};

Button.defaultProps = {
  variant: 'primary',
  size: 'medium',
  disabled: false,
};

export default Button;

Controls 控件面板 #

控件类型 #

Controls 面板允许动态修改组件参数:

text
┌─────────────────────────────────────────────────────────────┐
│  Controls                                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  variant                                                    │
│  ┌─────────────────────────────────────────┐               │
│  │ primary ▼                               │  ← select      │
│  └─────────────────────────────────────────┘               │
│                                                             │
│  size                                                       │
│  ○ small  ● medium  ○ large                 ← radio        │
│                                                             │
│  disabled                                                   │
│  ☐ disabled                                 ← boolean      │
│                                                             │
│  children                                                   │
│  ┌─────────────────────────────────────────┐               │
│  │ Button                                  │  ← text        │
│  └─────────────────────────────────────────┘               │
│                                                             │
│  count                                                      │
│  ┌──┬───────────────────────────────────┬──┐              │
│  │ -│███████████████████████████████████│+ │  ← range      │
│  └──┴───────────────────────────────────┴──┘              │
│  5                                                          │
│                                                             │
│  color                                                      │
│  ┌─────────────────────────────────────────┐               │
│  │ #1890ff                                 │  ← color      │
│  └─────────────────────────────────────────┘               │
│                                                             │
│  onClick                                                    │
│  ┌─────────────────────────────────────────┐               │
│  │ () => alert('clicked')                  │  ← object     │
│  └─────────────────────────────────────────┘               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

控件配置 #

javascript
// Button.stories.js
export default {
  title: 'Components/Button',
  component: Button,
  argTypes: {
    // 下拉选择
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger'],
      description: '按钮变体类型',
      table: {
        defaultValue: { summary: 'primary' },
      },
    },
    // 单选按钮
    size: {
      control: 'radio',
      options: ['small', 'medium', 'large'],
    },
    // 开关
    disabled: {
      control: 'boolean',
    },
    // 文本输入
    children: {
      control: 'text',
    },
    // 数字范围
    count: {
      control: { type: 'range', min: 0, max: 10, step: 1 },
    },
    // 颜色选择
    color: {
      control: 'color',
    },
    // 对象编辑
    style: {
      control: 'object',
    },
    // 隐藏控件
    className: {
      control: false,
    },
  },
};

控件类型一览 #

控件类型 适用数据类型 示例
text string 文本输入框
boolean boolean 开关
number number 数字输入
range number 滑块
color string 颜色选择器
select string/number 下拉选择
radio string/number 单选按钮
inline-radio string/number 行内单选
check string[] 复选框
inline-check string[] 行内复选
object object JSON 编辑器
file string 文件选择
date Date 日期选择

Actions 操作面板 #

事件日志 #

Actions 面板记录组件触发的事件:

text
┌─────────────────────────────────────────────────────────────┐
│  Actions                                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  📋 onClick                                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ {                                                    │   │
│  │   "type": "click",                                   │   │
│  │   "target": "button",                                │   │
│  │   "timestamp": 1640000000000                         │   │
│  │ }                                                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  📋 onChange                                                │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ "new value"                                          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  清除日志                                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

配置 Actions #

javascript
// .storybook/preview.js
const preview = {
  parameters: {
    actions: {
      // 自动捕获以 on 开头的事件处理函数
      argTypesRegex: '^on[A-Z].*',
    },
  },
};

// 或在 Story 中手动配置
export const WithClick = {
  args: {
    onClick: action('button-clicked'),
    onChange: action('value-changed'),
  },
};

Interactions 交互面板 #

交互测试 #

Interactions 面板显示交互测试步骤:

text
┌─────────────────────────────────────────────────────────────┐
│  Interactions                                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✅ Play function                                           │
│                                                             │
│  ✅ 1. Find button                                          │
│     await screen.findByRole('button')                       │
│                                                             │
│  ✅ 2. Click button                                         │
│     await userEvent.click(button)                           │
│                                                             │
│  ✅ 3. Verify click handler                                 │
│     expect(onClick).toHaveBeenCalled()                      │
│                                                             │
│  ▶️ 重新运行                                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

快捷键 #

常用快捷键 #

快捷键 功能
/Ctrl/Cmd + K 搜索
S 聚焦侧边栏
D 切换 Canvas/Docs
A 切换 Addons 面板
F 全屏
上一个/下一个 Story
导航 Stories 列表
Enter 选择 Story
Escape 关闭搜索/退出全屏

第一个 Story #

创建组件 #

jsx
// src/components/Button.jsx
import React from 'react';
import './Button.css';

const Button = ({
  variant = 'primary',
  size = 'medium',
  disabled = false,
  children,
  onClick,
}) => {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

export default Button;

创建 Story #

javascript
// src/components/Button.stories.jsx
import Button from './Button';

export default {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger'],
    },
    size: {
      control: 'radio',
      options: ['small', 'medium', 'large'],
    },
  },
};

export const Primary = {
  args: {
    variant: 'primary',
    children: 'Primary Button',
  },
};

export const Secondary = {
  args: {
    variant: 'secondary',
    children: 'Secondary Button',
  },
};

export const Large = {
  args: {
    size: 'large',
    children: 'Large Button',
  },
};

export const Disabled = {
  args: {
    disabled: true,
    children: 'Disabled Button',
  },
};

查看结果 #

启动 Storybook 后:

bash
npm run storybook

你将看到:

text
📁 Components
  📄 Button
    Primary      ← 主要按钮
    Secondary    ← 次要按钮
    Large        ← 大尺寸按钮
    Disabled     ← 禁用按钮

组织 Stories #

目录结构 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Story 组织方式                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  方式一:按类型分组                                          │
│                                                             │
│  title: 'Components/Button'                                │
│  title: 'Components/Input'                                 │
│  title: 'Layouts/Grid'                                     │
│  title: 'Pages/Home'                                       │
│                                                             │
│  方式二:按功能分组                                          │
│                                                             │
│  title: 'UI/Buttons/Primary'                               │
│  title: 'UI/Forms/Input'                                   │
│  title: 'UI/Forms/Select'                                  │
│                                                             │
│  方式三:按模块分组                                          │
│                                                             │
│  title: 'Auth/LoginForm'                                   │
│  title: 'Auth/RegisterForm'                                │
│  title: 'Dashboard/Stats'                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

使用斜杠分隔 #

javascript
// 使用 / 分隔层级
export default {
  title: 'Design System/Atoms/Button',
  component: Button,
};

// 结果:
// 📁 Design System
//   📁 Atoms
//     📄 Button
//       Primary
//       Secondary

最佳实践 #

1. 命名规范 #

javascript
// ✅ 好的命名
export const PrimaryButton = {};
export const WithIcon = {};
export const LoadingState = {};
export const ErrorState = {};

// ❌ 不好的命名
export const Story1 = {};
export const Test = {};
export const Default = {};  // 除非真的是默认状态

2. Story 组织 #

javascript
export default {
  title: 'Components/Button',
  component: Button,
  // 按逻辑分组
  subcomponents: { ButtonGroup },
};

// 基础状态
export const Primary = {};
export const Secondary = {};

// 尺寸变体
export const Small = {};
export const Medium = {};
export const Large = {};

// 状态变体
export const Loading = {};
export const Disabled = {};

// 组合示例
export const WithIcon = {};
export const WithBadge = {};

3. 文档描述 #

javascript
export default {
  title: 'Components/Button',
  component: Button,
  parameters: {
    docs: {
      description: {
        component: `
按钮组件用于触发操作。

## 使用指南

- 使用 \`primary\` 变体作为主要操作
- 使用 \`secondary\` 变体作为次要操作
- 禁用状态用于不可用操作
        `,
      },
    },
  },
};

下一步 #

现在你已经掌握了 Storybook 的基础使用方法,接下来学习 编写 Stories 深入了解 Story 的编写技巧!

最后更新:2026-03-29