storybook

djimontyp's avatarfrom djimontyp

Storybook stories, CSF3 format, autodocs. Use for component documentation.

⭐0stars🔀0forks📁View on GitHub🕐Updated Dec 14, 2025

When & Why to Use This Skill

This Claude skill streamlines UI component documentation and testing by automating the creation of Storybook stories in CSF3 format. It enforces standardized naming conventions, architectural tiers, and mandatory documentation rules, ensuring a consistent and high-quality Design System. By integrating autodocs and interaction testing via play functions, it helps developers maintain a robust, well-tested component library with minimal manual overhead.

Use Cases

  • Automating the generation of CSF3-compliant stories for React components to ensure 100% documentation coverage with autodocs.
  • Enforcing design system consistency by applying specific naming conventions and story requirements for shared UI, patterns, and feature-based components.
  • Implementing automated interaction tests using Play functions to validate user behaviors like form submissions, modal toggles, and keyboard navigation within Storybook.
  • Configuring complex story decorators to mock environment providers such as ThemeProvider, React Router, and TanStack Query for isolated component development.
namestorybook
descriptionStorybook stories, CSF3 format, autodocs. Use for component documentation.

Storybook Skill

Atomic Rule

Компонент в shared/ → story обов'язково в тому ж коміті.

F1 creates component → F1 creates story → Same commit

Before Creating UI

  1. Перевір Storybook на існуючі компоненти
  2. Використовуй існуючі замість створення нових
  3. Дотримуйся patterns з Design System
just storybook  # http://localhost:6006

Story Requirements

CSF3 Format

import type { Meta, StoryObj } from '@storybook/react-vite';
import { ComponentName } from './ComponentName';

const meta: Meta<typeof ComponentName> = {
  title: 'Category/ComponentName',  // See naming below
  component: ComponentName,
  tags: ['autodocs'],               // REQUIRED!
  parameters: {
    docs: {
      description: {
        component: 'Brief description of the component purpose.',
      },
    },
  },
};

export default meta;
type Story = StoryObj<typeof ComponentName>;

export const Default: Story = {
  args: {
    // Default props
  },
};

Naming Convention

Location Title Prefix Example
shared/ui/ UI/ UI/Button, UI/Card
shared/patterns/ Design System/Patterns/ Design System/Patterns/CardWithStatus
shared/components/ Components/ Components/DataTable
features/*/components/ Features/{Domain}/ Features/Analysis/RunCard

Minimum Stories by Tier

Tier Location Minimum Stories
1 shared/ui/ 4-6 (Default, Variants, States, Sizes)
2 shared/patterns/ 5-8 (All statuses, compositions, use cases)
3 features/ 2-4 (Default, Empty, Loading, Error)

Required Story Types

For Shared UI (Tier 1)

// REQUIRED
export const Default: Story = { args: { ... } };

// If component has variants
export const Primary: Story = { args: { variant: 'primary' } };
export const Secondary: Story = { args: { variant: 'secondary' } };
export const Destructive: Story = { args: { variant: 'destructive' } };

// If component has sizes
export const Small: Story = { args: { size: 'sm' } };
export const Large: Story = { args: { size: 'lg' } };

// If component has states
export const Disabled: Story = { args: { disabled: true } };
export const Loading: Story = { args: { loading: true } };

For Patterns (Tier 2)

// REQUIRED - All status states
export const Connected: Story = { args: { status: 'connected' } };
export const Validating: Story = { args: { status: 'validating' } };
export const Pending: Story = { args: { status: 'pending' } };
export const Error: Story = { args: { status: 'error' } };

// REQUIRED - Compositions
export const WithFooter: Story = { args: { footer: <Button>Action</Button> } };
export const WithContent: Story = { args: { children: <div>...</div> } };

// REQUIRED - Layout examples
export const CardGrid: Story = {
  render: () => (
    <div className="grid grid-cols-3 gap-4">
      <Component ... />
      <Component ... />
      <Component ... />
    </div>
  ),
};

For Feature Components (Tier 3)

// REQUIRED
export const Default: Story = { args: { ... } };
export const Empty: Story = { args: { items: [] } };
export const Loading: Story = { args: { isLoading: true } };
export const Error: Story = { args: { error: new Error('Failed to load') } };

Providers for Stories

Hook Used Required Provider
useTheme ThemeProvider
useLocation, Link MemoryRouter
useQuery QueryClientProvider
useSidebar SidebarProvider

Decorator Template

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MemoryRouter } from 'react-router-dom';
import { ThemeProvider } from '@/shared/components/ThemeProvider';

const queryClient = new QueryClient();

const meta: Meta<typeof Component> = {
  component: Component,
  decorators: [
    (Story) => (
      <QueryClientProvider client={queryClient}>
        <ThemeProvider>
          <MemoryRouter>
            <Story />
          </MemoryRouter>
        </ThemeProvider>
      </QueryClientProvider>
    ),
  ],
};

When Story Is Mandatory

Location Mandatory? Notes
shared/ui/ ✅ Yes All UI primitives
shared/patterns/ ✅ Yes All patterns
shared/components/ ✅ Yes If exported publicly
features/*/components/ ⚠️ Conditional If >50 LOC or reused
pages/ ❌ No Use E2E tests

Output Format

When creating component + story:

✅ Component implemented

Component: [Name]
Files:
- [component path]
- [story path]
Story: [Tier] / [# of stories]
Verify: npm run build && npx tsc --noEmit

Interaction Testing

Use play functions to test user interactions directly in stories.

Basic Interaction Test

import { expect, within, userEvent } from '@storybook/test';

export const ClickTest: Story = {
  args: {
    onClick: fn(),  // Mock function from @storybook/test
  },
  play: async ({ canvasElement, args }) => {
    const canvas = within(canvasElement);

    // Find and click button
    const button = canvas.getByRole('button');
    await userEvent.click(button);

    // Assert callback was called
    await expect(args.onClick).toHaveBeenCalled();
  },
};

Form Interaction Test

export const FormSubmit: Story = {
  args: {
    onSubmit: fn(),
  },
  play: async ({ canvasElement, args }) => {
    const canvas = within(canvasElement);

    // Fill form
    await userEvent.type(canvas.getByLabelText('Email'), 'test@example.com');
    await userEvent.type(canvas.getByLabelText('Password'), 'password123');

    // Submit
    await userEvent.click(canvas.getByRole('button', { name: /submit/i }));

    // Assert
    await expect(args.onSubmit).toHaveBeenCalledWith({
      email: 'test@example.com',
      password: 'password123',
    });
  },
};

Dialog/Modal Test

export const DialogOpen: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // Open dialog
    await userEvent.click(canvas.getByRole('button', { name: /open/i }));

    // Wait for dialog
    await expect(canvas.findByRole('dialog')).resolves.toBeInTheDocument();

    // Close with ESC
    await userEvent.keyboard('{Escape}');
    await expect(canvas.queryByRole('dialog')).not.toBeInTheDocument();
  },
};

Keyboard Navigation Test

export const KeyboardNav: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);

    // Tab through elements
    await userEvent.tab();
    await expect(canvas.getByRole('button', { name: /first/i })).toHaveFocus();

    await userEvent.tab();
    await expect(canvas.getByRole('button', { name: /second/i })).toHaveFocus();

    // Activate with Enter
    await userEvent.keyboard('{Enter}');
  },
};

When to Add Interaction Tests

Component Type Interaction Test Required?
Buttons with onClick ✅ Yes
Forms ✅ Yes
Dialogs/Modals ✅ Yes
Dropdowns ✅ Yes
Static display ❌ No
Layout components ❌ No

Running Interaction Tests

# In Storybook UI - click "Interactions" tab
just storybook

# CLI (requires Storybook running)
just storybook-test

# CI mode
just storybook-test-ci

Templates

Use templates from @templates/ directory:

  • shared-ui.template.tsx — for shared/ui/ components
  • pattern.template.tsx — for shared/patterns/ components
  • feature.template.tsx — for features/*/ components

References