storybook-setup

安装量: 77
排名: #10074

安装

npx skills add https://github.com/patricio0312rev/skills --skill storybook-setup

Storybook Setup

Configure Storybook for comprehensive component documentation and testing.

Core Workflow Initialize Storybook: Setup with framework Configure addons: Controls, actions, a11y Write stories: Document components Add documentation: MDX pages Setup testing: Visual regression Deploy docs: Static hosting Installation

Initialize Storybook

npx storybook@latest init

Or with specific framework

npx storybook@latest init --type react npx storybook@latest init --type nextjs npx storybook@latest init --type vue3

Configuration Main Configuration // .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-onboarding', '@storybook/addon-links', '@storybook/addon-essentials', '@chromatic-com/storybook', '@storybook/addon-interactions', '@storybook/addon-a11y', '@storybook/addon-designs', '@storybook/addon-coverage', ],

framework: { name: '@storybook/react-vite', options: {}, },

docs: { autodocs: 'tag', },

staticDirs: ['../public'],

typescript: { reactDocgen: 'react-docgen-typescript', reactDocgenTypescriptOptions: { shouldExtractLiteralValuesFromEnum: true, shouldRemoveUndefinedFromOptional: true, propFilter: (prop) => prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, }, },

viteFinal: async (config) => { // Customize Vite config return config; }, };

export default config;

Preview Configuration // .storybook/preview.ts import type { Preview } from '@storybook/react'; import { themes } from '@storybook/theming'; import '../src/styles/globals.css';

const preview: Preview = { parameters: { controls: { matchers: { color: /(background|color)$/i, date: /Date$/i, }, }, backgrounds: { default: 'light', values: [ { name: 'light', value: '#ffffff' }, { name: 'dark', value: '#1a1a1a' }, { name: 'gray', value: '#f5f5f5' }, ], }, layout: 'centered', docs: { theme: themes.light, }, a11y: { config: { rules: [ { id: 'color-contrast', enabled: true }, { id: 'label', enabled: true }, ], }, }, viewport: { viewports: { mobile: { name: 'Mobile', styles: { width: '375px', height: '667px' }, }, tablet: { name: 'Tablet', styles: { width: '768px', height: '1024px' }, }, desktop: { name: 'Desktop', styles: { width: '1440px', height: '900px' }, }, }, }, }, globalTypes: { theme: { description: 'Global theme', defaultValue: 'light', toolbar: { title: 'Theme', icon: 'circlehollow', items: ['light', 'dark'], dynamicTitle: true, }, }, }, decorators: [ (Story, context) => { const theme = context.globals.theme; return (

); }, ], };

export default preview;

Writing Stories Component Story Format (CSF3) // src/components/Button/Button.stories.tsx import type { Meta, StoryObj } from '@storybook/react'; import { fn } from '@storybook/test'; import { Button } from './Button';

const meta = { title: 'Components/Button', component: Button, parameters: { layout: 'centered', docs: { description: { component: 'A versatile button component with multiple variants and sizes.', }, }, }, tags: ['autodocs'], argTypes: { variant: { control: 'select', options: ['primary', 'secondary', 'outline', 'ghost'], description: 'Visual style variant', table: { type: { summary: 'string' }, defaultValue: { summary: 'primary' }, }, }, size: { control: 'radio', options: ['sm', 'md', 'lg'], description: 'Button size', }, disabled: { control: 'boolean', description: 'Disable the button', }, loading: { control: 'boolean', description: 'Show loading state', }, children: { control: 'text', description: 'Button content', }, }, args: { onClick: fn(), children: 'Button', }, } satisfies Meta;

export default meta; type Story = StoryObj;

// Basic stories export const Primary: Story = { args: { variant: 'primary', }, };

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

export const Outline: Story = { args: { variant: 'outline', }, };

export const Ghost: Story = { args: { variant: 'ghost', }, };

// Size variants export const Small: Story = { args: { size: 'sm', }, };

export const Large: Story = { args: { size: 'lg', }, };

// States export const Disabled: Story = { args: { disabled: true, }, };

export const Loading: Story = { args: { loading: true, }, };

// With icons export const WithIcon: Story = { args: { children: ( <> Add Item </> ), }, };

// All variants showcase export const AllVariants: Story = { render: () => (

), };

Interactive Stories // src/components/Form/Form.stories.tsx import type { Meta, StoryObj } from '@storybook/react'; import { within, userEvent, expect, fn } from '@storybook/test'; import { Form } from './Form';

const meta: Meta = { title: 'Components/Form', component: Form, args: { onSubmit: fn(), }, };

export default meta; type Story = StoryObj;

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

// Fill out the form
const emailInput = canvas.getByLabelText('Email');
await userEvent.type(emailInput, 'test@example.com', { delay: 50 });

const passwordInput = canvas.getByLabelText('Password');
await userEvent.type(passwordInput, 'password123', { delay: 50 });

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

// Assert the form was submitted
await expect(args.onSubmit).toHaveBeenCalled();

}, };

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

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

// Check for error messages
await expect(canvas.getByText('Email is required')).toBeInTheDocument();

}, };

MDX Documentation {/ src/components/Button/Button.mdx /} import { Meta, Story, Canvas, Controls, ArgTypes } from '@storybook/blocks'; import * as ButtonStories from './Button.stories'; import { Button } from './Button';

<Meta of={ButtonStories} />

Button

The Button component is used to trigger actions or navigation.

Import

```tsx import { Button } from '@/components/Button';

Usage Variants

Buttons come in four variants to communicate different levels of emphasis.

Primary

Use for primary actions that are the main call to action on a page.

Secondary

Use for secondary actions that complement the primary action.

Outline

Use for tertiary actions or when you want less visual emphasis.

Ghost

Use for navigation or very subtle actions.

Sizes States Disabled Loading Props Accessibility Buttons use the native

返回排行榜