AgentSkillsCN

creating-stories

为原子组件和示例页面创建并修改 Storybook 故事。采用 MSW 进行 API 模拟,遵循 UI 工具包的编写规范。当您需要创建故事、添加组件示例、构建示例页面、排查 MSW 模拟污染问题,或当用户提及 Storybook、故事、可视化测试,或引用故事的端到端测试时,均可使用此功能。

SKILL.md
--- frontmatter
name: creating-stories
description: Creates and modifies Storybook stories for Atomic components and sample pages. Uses MSW for API mocking, follows ui-kit conventions. Use when creating stories, adding component examples, building sample pages, debugging MSW mock pollution, or when user mentions Storybook, stories, visual testing, or e2e tests that reference stories.
license: Apache-2.0
metadata:
  author: coveo
  version: "1.0"
  package: atomic

Creating Stories

Process

Step 1: Determine Story Type

Answer these questions:

  1. Is this a single component or a full page?
  2. Which interface type? (Search, Commerce, Insight, IPX, Recommendations)
  3. Is this a result template component?
  4. What API mocking is needed?

Locations:

  • Components: packages/atomic/src/components/<category>/<name>/<name>.new.stories.tsx
  • Pages: packages/atomic/storybook-pages/<use-case>/<name>.new.stories.tsx

Step 2: Generate Template

This skill includes a small generator that renders Handlebars templates from .claude/skills/creating-stories/assets/.

bash
# Component story
node .claude/skills/creating-stories/scripts/generate-story-template.mjs \
  atomic-component-name --category search

# Result template component
node .claude/skills/creating-stories/scripts/generate-story-template.mjs \
  atomic-result-field --category search --result

# Sample page
node .claude/skills/creating-stories/scripts/generate-story-template.mjs \
  page-name --type page --category commerce

Allowed values:

  • --type: component (default), page
  • --category: search (default), commerce, insight, ipx, recommendations

Notes:

  • --result is only valid for --type component (using it with --type page is an error).

Step 3: Complete the Story

  1. Add API mocking - Configure MSW harness for expected responses
  2. Create story variants - Add stories for different states (empty, error, selected)
  3. Add custom props - Set component-specific arguments
  4. Test interactions - Verify story renders and behaves correctly

For EndpointHarness overview and methods, see endpoint-harness-reference.md. For advanced API mocking patterns, see msw-patterns.md. For creating a new API mock domain, see creating-new-api-mock.md. For component examples, see component-examples.md. For page examples, see sample-page-examples.md.

Step 4: Validate

bash
# Validate the story file
node .claude/skills/creating-stories/scripts/validate_story.mjs packages/atomic/src/components/.../component.new.stories.tsx

# Run Storybook to verify visually
cd packages/atomic && pnpm storybook

Story Structure

Component Story Anatomy

typescript
// 1. Create API harness at top level
const searchApiHarness = new MockSearchApi();

// 2. Get interface wrapper
const {decorator, play} = wrapInSearchInterface();

// 3. Get component helpers
const {events, args, argTypes, template} = getStorybookHelpers('atomic-name');

// 4. Configure meta
const meta: Meta = {
  component: 'atomic-name',
  decorators: [decorator],
  parameters: {
    msw: {handlers: [...searchApiHarness.handlers]},
  },
  beforeEach: () => {
    searchApiHarness.searchEndpoint.clear();
  },
  play,
};

// 5. Export stories
export const Default: Story = {};

API Mocking Patterns

Default response:

typescript
// Uses base response automatically

Modify for all stories:

typescript
searchApiHarness.searchEndpoint.mock((response) => ({
  ...response,
  results: response.results.slice(0, 10),
}));

Story-specific response:

typescript
export const NoResults: Story = {
  beforeEach: () => {
    searchApiHarness.searchEndpoint.mockOnce((response) => ({
      ...response,
      results: [],
      totalCount: 0,
    }));
  },
};

Available API Mocks

MockImportUse Case
MockSearchApi@/storybook-utils/api/search/mockSearch interface
MockCommerceApi@/storybook-utils/api/commerce/mockCommerce interface
MockInsightApi@/storybook-utils/api/insight/mockInsight interface
MockAnswerApi@/storybook-utils/api/answer/mockAnswer/RGA
MockRecommendationApi@/storybook-utils/api/recommendation/mockRecommendations
MockMachineLearningApi@/storybook-utils/api/machinelearning/mockML/User Actions

Interface Wrappers

WrapperImportOptions
wrapInSearchInterface@/storybook-utils/search/search-interface-wrapperskipFirstSearch, includeCodeRoot
wrapInCommerceInterface@/storybook-utils/commerce/commerce-interface-wrapperskipFirstSearch, includeCodeRoot
wrapInInsightInterface@/storybook-utils/insight/insight-interface-wrapperskipFirstSearch
wrapInResultTemplate@/storybook-utils/search/result-template-wrapperautoLoad

Reference Documentation

ReferenceWhen to Load
endpoint-harness-reference.mdEndpointHarness overview, methods, type safety
msw-patterns.mdAdvanced MSW techniques, pagination, errors
creating-new-api-mock.mdAdd a new mock domain when needed
component-examples.mdFacets, search box, pager, result components
sample-page-examples.mdFull page patterns for all interfaces

Scripts

ScriptPurpose
generate-story-template.mjsGenerate story boilerplate from templates
validate_story.mjsValidate created story files for correctness

Templates

Templates in assets/ directory:

  • component.new.stories.tsx.hbs - Standard component story
  • result-component.new.stories.tsx.hbs - Result template component
  • page.new.stories.tsx.hbs - Sample page story

Validation Checklist

Before completing:

  • Story file named <component-name>.new.stories.tsx
  • MSW handlers included in parameters
  • beforeEach clears mocked responses
  • At least Default story exported
  • Component imports use path aliases (@/storybook-utils/...)
  • For pages: initialization function and play handler included
  • Story follows patterns from similar components

Common Edge Cases

Forgetting to clear endpoints

Always clear in meta-level beforeEach, not afterEach:

typescript
beforeEach: () => {
  harness.searchEndpoint.clear();
}

Stories show wrong data in docs mode

Every story needs story-level beforeEach queuing mockOnce(). Create helper:

typescript
const mockDefault = () => harness.endpoint.mockOnce(r => r);

Call in ALL stories' beforeEach, even for default responses.

State persists between stories

Cause: Missing endpoint clear in meta beforeEach
Fix: Add harness.endpoint.clear() to meta-level beforeEach

Not spreading base response

Always spread to maintain all required fields:

typescript
mockOnce((response) => ({
  ...response,
  results: [], // Modify only what you need
}))

Wrong import paths

Use @/storybook-utils/... path aliases, not relative paths

Missing MSW handlers

Include all harness handlers in parameters:

typescript
parameters: {
  msw: {handlers: [...harness.handlers]},
}

Wrong decorator order

Result templates require specific decorator order—check similar components for the pattern

API calls return default responses

Check:

  • Endpoint path in harness matches actual API call
  • HTTP method (GET/POST) is correct
  • Handlers included: msw: {handlers: [...harness.handlers]}

Using mock() instead of mockOnce()

Use mockOnce() for story-specific responses to prevent pollution