Skip to main content

Storybook

Maybern uses Storybook for component documentation, visual testing, and development in isolation.

Running Storybook

# Start Storybook dev server
pnpm storybook

# Build static Storybook
pnpm build-storybook
Storybook runs at http://localhost:6006.

Writing Stories

Basic Story

// stories/Button.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import { Button } from "@/shared/components";

const meta: Meta<typeof Button> = {
  title: "Components/Button",
  component: Button,
  tags: ["autodocs"],
};

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

export const Primary: Story = {
  args: {
    children: "Click me",
    colorScheme: "brand",
  },
};

export const Secondary: Story = {
  args: {
    children: "Secondary",
    variant: "outline",
  },
};

export const Loading: Story = {
  args: {
    children: "Loading",
    isLoading: true,
  },
};

With Controls

const meta: Meta<typeof Input> = {
  title: "Components/Input",
  component: Input,
  argTypes: {
    size: {
      control: "select",
      options: ["xs", "sm", "md", "lg"],
    },
    isDisabled: {
      control: "boolean",
    },
  },
};

export const Configurable: Story = {
  args: {
    placeholder: "Enter text...",
    size: "md",
    isDisabled: false,
  },
};

Interactive Stories

export const Interactive: Story = {
  render: () => {
    const [value, setValue] = useState("");
    
    return (
      <Input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Type something..."
      />
    );
  },
};

Story Organization

stories/
├── Introduction.mdx           # Storybook intro
├── components/
│   ├── Button.stories.tsx
│   ├── Input.stories.tsx
│   └── Card.stories.tsx
├── forms/
│   ├── FormInput.stories.tsx
│   └── FormSelect.stories.tsx
└── patterns/
    ├── DataTable.stories.tsx
    └── Modal.stories.tsx

Decorators

Theme Decorator

// .storybook/preview.tsx
import { ChakraProvider } from "@chakra-ui/react";
import { theme } from "@/theme";

const preview: Preview = {
  decorators: [
    (Story) => (
      <ChakraProvider theme={theme}>
        <Story />
      </ChakraProvider>
    ),
  ],
};

Story-Level Decorator

export const WithBackground: Story = {
  decorators: [
    (Story) => (
      <Box bg="gray.100" p={8}>
        <Story />
      </Box>
    ),
  ],
};

MDX Documentation

{/* stories/Button.mdx */}
import { Meta, Canvas, Story, ArgsTable } from "@storybook/blocks";
import * as ButtonStories from "./Button.stories";

<Meta of={ButtonStories} />

# Button

Buttons are used for triggering actions.

## Usage

<Canvas of={ButtonStories.Primary} />

## Props

<ArgsTable of={ButtonStories} />

Component Development

Isolation Benefits

  • Develop components without full app
  • Test different states easily
  • Document component API
  • Visual regression testing

Workflow

  1. Create story file alongside component
  2. Define variants and states
  3. Document props with argTypes
  4. Test interactions
  5. Build storybook for sharing

Best Practices

Create separate stories for each meaningful state:
  • Default
  • Loading
  • Error
  • Empty
  • Disabled
Define component props via args for controls panel to work:
export const Story: Story = {
  args: {
    prop1: "value",
    prop2: true,
  },
};
Include MDX documentation for complex components explaining usage patterns.
Update stories when components change. Broken stories indicate documentation drift.