story-naming
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseStory Naming Conventions
Storybook 故事命名规范
Name Storybook stories to describe what the user sees or does, not how the story works internally.
为Storybook故事命名时,应描述用户所见或操作的内容,而非故事的内部工作原理。
Quick Decision Guide
快速决策指南
Before naming a story, ask:
- Does this describe what the user sees or does?
- Would a non-technical stakeholder understand this name?
- Is this name still accurate without the function?
play
If yes to all three, it's a good name.
为故事命名前,先问自己三个问题:
- 这个名称是否描述了用户所见或操作的内容?
- 非技术相关的利益相关者能否理解这个名称?
- 即使移除函数,这个名称是否依然准确?
play
如果三个问题的答案都是肯定的,那这就是一个好的名称。
Naming Rules
命名规则
Describe User Scenarios or Component States
描述用户场景或组件状态
tsx
// User scenarios (typically with play functions)
export const SelectAndApply: Story = {};
export const SearchByKeyword: Story = {};
export const ClearAllSelections: Story = {};
// Component states (typically static/visual)
export const DisabledState: Story = {};
export const WithPreselectedItems: Story = {};
export const EmptyState: Story = {};tsx
// User scenarios (typically with play functions)
export const SelectAndApply: Story = {};
export const SearchByKeyword: Story = {};
export const ClearAllSelections: Story = {};
// Component states (typically static/visual)
export const DisabledState: Story = {};
export const WithPreselectedItems: Story = {};
export const EmptyState: Story = {};Never Expose Implementation Details
切勿暴露实现细节
tsx
// Bad - exposes implementation
export const InteractiveDefault: Story = {};
export const InteractiveWithValidation: Story = {};
export const TestSearchFunctionality: Story = {};
export const PlayFunctionForSearch: Story = {};
// Good - describes user scenario
export const Default: Story = {};
export const WithValidation: Story = {};
export const SearchByKeyword: Story = {};
export const SearchAndFilter: Story = {};Rationale: The presence of a function already indicates interactivity. Story names should remain accurate even if the function is removed.
playplaytsx
// Bad - exposes implementation
export const InteractiveDefault: Story = {};
export const InteractiveWithValidation: Story = {};
export const TestSearchFunctionality: Story = {};
export const PlayFunctionForSearch: Story = {};
// Good - describes user scenario
export const Default: Story = {};
export const WithValidation: Story = {};
export const SearchByKeyword: Story = {};
export const SearchAndFilter: Story = {};理由: 函数的存在已经表明了交互性。即使移除函数,故事名称也应保持准确。
playplayStory Title Organization
故事标题组织
Use hierarchical paths for sidebar organization:
titletsx
const meta = {
title: 'Components/StructuredFilter/SelectOptions',
component: SelectOptions,
} satisfies Meta<typeof SelectOptions>;Patterns:
| Pattern | Use Case |
|---|---|
| Single component stories |
| Visual state variations |
| User interaction flows |
| Feature-level compositions |
使用层级化的路径来组织侧边栏:
titletsx
const meta = {
title: 'Components/StructuredFilter/SelectOptions',
component: SelectOptions,
} satisfies Meta<typeof SelectOptions>;模式:
| 模式 | 使用场景 |
|---|---|
| 单一组件的故事 |
| 视觉状态变体 |
| 用户交互流程 |
| 功能级别的组合组件 |
Examples
示例
Example 1: Create a new story file
示例1:创建新的故事文件
User: "Create stories for the UserProfile component"
Action:
tsx
import type { Meta, StoryObj } from '@storybook/react';
import { UserProfile } from './UserProfile';
const meta = {
title: 'Components/UserProfile',
component: UserProfile,
} satisfies Meta<typeof UserProfile>;
export default meta;
type Story = StoryObj<typeof UserProfile>;
export const Default: Story = {};
export const WithLongName: Story = {
args: { name: 'Alexandria Bartholomew Richardson III' },
};
export const EmptyState: Story = {
args: { user: null },
};
export const LoadingState: Story = {
args: { isLoading: true },
};用户: "为UserProfile组件创建故事"
操作:
tsx
import type { Meta, StoryObj } from '@storybook/react';
import { UserProfile } from './UserProfile';
const meta = {
title: 'Components/UserProfile',
component: UserProfile,
} satisfies Meta<typeof UserProfile>;
export default meta;
type Story = StoryObj<typeof UserProfile>;
export const Default: Story = {};
export const WithLongName: Story = {
args: { name: 'Alexandria Bartholomew Richardson III' },
};
export const EmptyState: Story = {
args: { user: null },
};
export const LoadingState: Story = {
args: { isLoading: true },
};Example 2: Name an interactive story
示例2:为交互式故事命名
User: "Add a story where the user fills out and submits the form"
Action:
tsx
// Good - describes the user action
export const FillAndSubmit: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText('Email'), 'user@example.com');
await userEvent.click(canvas.getByRole('button', { name: 'Submit' }));
},
};Not:
tsx
// Bad - exposes implementation detail
export const InteractiveFormSubmission: Story = { ... };
export const PlayFunctionSubmit: Story = { ... };
export const TestFormValidation: Story = { ... };用户: "添加一个用户填写并提交表单的故事"
操作:
tsx
// Good - describes the user action
export const FillAndSubmit: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByLabelText('Email'), 'user@example.com');
await userEvent.click(canvas.getByRole('button', { name: 'Submit' }));
},
};错误示例:
tsx
// Bad - exposes implementation detail
export const InteractiveFormSubmission: Story = { ... };
export const PlayFunctionSubmit: Story = { ... };
export const TestFormValidation: Story = { ... };Example 3: Organize complex component stories
示例3:整理复杂组件的故事
User: "Set up stories for the DataGrid with states and interactions"
Action:
tsx
// States file: Components/DataGrid/States
export const Empty: Story = {};
export const WithSingleRow: Story = {};
export const WithPagination: Story = {};
export const DisabledState: Story = {};
// Flows file: Components/DataGrid/Flows
export const SortByColumn: Story = {};
export const FilterAndSearch: Story = {};
export const SelectMultipleRows: Story = {};
export const EditInlineCell: Story = {};用户: "为DataGrid组件设置包含状态和交互的故事"
操作:
tsx
// States file: Components/DataGrid/States
export const Empty: Story = {};
export const WithSingleRow: Story = {};
export const WithPagination: Story = {};
export const DisabledState: Story = {};
// Flows file: Components/DataGrid/Flows
export const SortByColumn: Story = {};
export const FilterAndSearch: Story = {};
export const SelectMultipleRows: Story = {};
export const EditInlineCell: Story = {};Example 4: Review story names for convention compliance
示例4:审核故事名称是否符合规范
User: "Review my story names"
Action: Check each export name against the three questions:
| Current Name | Issue | Suggested Name |
|---|---|---|
| Exposes implementation ( | |
| Exposes implementation ( | |
| Exposes implementation ( | |
| Describes state | |
| Describes state | |
用户: "审核我的故事名称"
操作: 根据三个问题检查每个导出名称:
| 当前名称 | 问题 | 建议名称 |
|---|---|---|
| 暴露实现细节( | |
| 暴露实现细节( | |
| 暴露实现细节( | |
| 描述状态 | |
| 描述状态 | |
Common Anti-Patterns
常见反模式
| Anti-Pattern | Why It's Bad | Fix |
|---|---|---|
| Play function already signals interactivity | Remove prefix |
| Stories aren't tests, they're demonstrations | Describe the scenario |
| Implementation detail | Describe what happens |
| All stories render | Describe what's rendered |
| Technical jargon in names | Non-technical stakeholders can't understand | Use plain language |
| 反模式 | 弊端 | 修复方案 |
|---|---|---|
| Play函数已经表明了交互性 | 移除前缀 |
| 故事不是测试,而是演示 | 描述具体场景 |
| 属于实现细节 | 描述实际发生的操作 |
| 所有故事都会渲染 | 描述渲染的内容 |
| 名称中包含技术术语 | 非技术利益相关者无法理解 | 使用通俗易懂的语言 |
More Information
更多信息
See REFERENCE.md for detailed documentation including:
- Complete naming patterns with examples
- Story title hierarchy best practices
- Code review checklist for story naming
- Migration guide for renaming existing stories
详见REFERENCE.md中的详细文档,包括:
- 完整的命名模式及示例
- 故事标题层级最佳实践
- 故事命名的代码审核清单
- 现有故事重命名的迁移指南