# Storybook Patterns

This document outlines Storybook configuration, story types, and utilities for componentLibrary.

## Directory Structure

Each component with stories should have a `stories/` directory containing:

```
ComponentName/
└── stories/
    ├── ComponentName.stories.tsx        # Main story file
    ├── ComponentNameDecorator.tsx       # Decorators for CSS variables (optional)
    └── ComponentNameDecorator.module.scss  # Decorator styles (optional)
```

**For components with multiple variants** (like Button):
```
Button/
└── stories/
    ├── Button.AsButton.stories.tsx      # Variant 1 stories
    ├── Button.AsLink.stories.tsx        # Variant 2 stories
    ├── ButtonDecorator.tsx              # Shared decorators
    └── ButtonDecorator.module.scss      # Decorator styles
```

## Meta Configuration

Configure the story metadata with proper title, layout, and documentation:

```typescript
import type { Meta, StoryObj } from '@storybook/react';
import Component from '../index.js';
import { ComponentProps } from '../types.js';
import { withComponentStyles } from './ComponentDecorator.js';

const meta: Meta<ComponentProps> = {
  title: 'Component Library/ComponentName',  // or 'Component Library/ComponentName/Variant' for variants
  component: Component,
  parameters: {
    layout: 'centered',  // Options: 'centered', 'fullscreen', 'padded'
    docs: {
      description: {
        component: `Description of the component...`,
      },
    },
  },
  decorators: [withComponentStyles],  // Apply at meta level for all stories
};

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

## Story Types & Patterns

Essential stories to include for comprehensive component documentation:

### 1. Default Story (Playground)
Basic usage with all args present - acts as an interactive playground:

```typescript
export const Default: Story = {
  args: {
    variant: 'primary',
    children: 'Default content',
    // Include all component-specific args with sensible defaults
  },
};
```

### 2. States Story
Demonstrates different component states:

```typescript
export const States: Story = {
  name: 'Component States',
  render: () => (
    <SBContainer flex direction="column" gap="16px">
      <SBContainer addBackground>
        <h4>Default State</h4>
        <Component>Default</Component>
      </SBContainer>

      <SBContainer addBackground>
        <h4>Hover State</h4>
        <p>Hover over the component to see the effect</p>
        <Component>Hover me</Component>
      </SBContainer>

      <SBContainer addBackground>
        <h4>Disabled State</h4>
        <Component disabled>Disabled</Component>
      </SBContainer>
    </SBContainer>
  ),
};
```

### 3. Edge Cases Story
Shows behavior with edge case content:

```typescript
export const EdgeCases: Story = {
  name: 'Edge Cases',
  render: () => (
    <SBContainer flex direction="column" gap="16px">
      <SBContainer addBackground>
        <h4>Long Text</h4>
        <Component>
          This is a very long text that demonstrates how the component handles
          overflow and wrapping of content across multiple lines...
        </Component>
      </SBContainer>

      <SBContainer addBackground>
        <h4>Empty Content</h4>
        <Component />
      </SBContainer>

      <SBContainer addBackground>
        <h4>Special Characters</h4>
        <Component>!@#$%^&*()_+-=[]{}|;':",.<>?/</Component>
      </SBContainer>
    </SBContainer>
  ),
};
```

### 4. Bespoke Stories
Create component-specific stories that showcase unique features (no need to go overboard):

```typescript
export const WithIcon: Story = {
  args: {
    children: 'Button with icon',
    showIcon: true,
    iconFieldPath: 'icon',
    iconPosition: 'right',
  },
};
```

**Common categories:**
- `'Basic'` - className and fundamental props
- `'Component Specific'` - Component-unique props
- `'Content'` - Content and children props
- `'Icon'` - Icon-related props
- `'Events'` - Event handlers
- `'Typography'` - Font and text styling
- `'Layout'` - Display and sizing
- `'Visual'` - Colors and borders
- `'Spacing'` - Margins and padding

## Decorator Pattern

Create decorators to apply default CSS variables for stories:

```typescript
import type { Decorator } from '@storybook/react';
import type { CSSVariables } from '../../utils/types.js';

const defaultComponentStyles: CSSVariables = {
  '--hscl-component-property': 'value',
};

export const withComponentStyles: Decorator = (Story) => (
  <div style={defaultComponentStyles}>
    <Story />
  </div>
);
```

## Storybook Utilities

### SBContainer Helper Component
Use `SBContainer` for consistent layout and spacing in stories:

**Location:** `.storybook/utils/SBContainer.tsx`
**Import:** `import { SBContainer } from '@sb-utils/SBContainer.js';`

**Props:**
- `children`: ReactNode - Content to render
- `className?: string` - Additional CSS classes
- `addBackground?: boolean` - Adds light gray background (default: false)
- `flex?: boolean` - Enables flexbox layout (default: false)
- `direction?: 'row' | 'column' | 'row-reverse' | 'column-reverse'` - Flex direction (default: 'column')
- `gap?: 'none' | 'small' | 'medium' | 'large' | 'extralarge'` - Gap between flex items (default: 'none')
- `padding?: 'none' | 'small' | 'medium' | 'large' | 'extralarge'` - Padding inside container (default: 'medium')
- `alignItems?: CSSProperties['alignItems']` - Flex align-items (default: 'stretch')
- `justifyContent?: CSSProperties['justifyContent']` - Flex justify-content (default: 'flex-start')
- `minWidth?: string` - Minimum width (default: 'auto')
- `maxWidth?: string` - Maximum width (default: 'auto')
- `width?: string` - Fixed width
- `height?: string` - Fixed height
- `style?: CSSProperties` - Additional inline styles

**Size Values:**
- `none`: 0
- `small`: 8px
- `medium`: 16px
- `large`: 24px
- `extralarge`: 32px

**Example Usage:**
```typescript
import { SBContainer } from '@sb-utils/SBContainer.js';

export const ExampleStory: Story = {
  render: () => (
    <SBContainer flex direction="column" gap="large">
      <SBContainer addBackground padding="large">
        <h4>Example Title</h4>
        <Component prop="value">Content</Component>
      </SBContainer>

      <SBContainer addBackground padding="medium">
        <h4>Another Example</h4>
        <Component prop="different">More content</Component>
      </SBContainer>
    </SBContainer>
  ),
};
```

### Creating New Storybook Utilities
If you need additional helper components for stories:

1. Create the component in `.storybook/utils/`
2. Export it from `.storybook/utils/index.ts`
3. Import using the `@sb-utils/` alias

**Example:**
```typescript
// .storybook/utils/MyHelper.tsx
export const MyHelper = ({ children }: { children: ReactNode }) => {
  return <div className="my-helper">{children}</div>;
};

// .storybook/utils/index.ts
export { SBContainer } from './SBContainer.js';
export { MyHelper } from './MyHelper.js';

// In your story file
import { MyHelper } from '@sb-utils/MyHelper.js';
```

## Story Isolation and Component Usage

**Stories should focus on showcasing the component being tested, not other component library components.**

### When to Use Component Library Components vs. Inline HTML

**Use lightweight inline HTML or Storybook mocks when:**
- Testing primitive components (headings, buttons, cards, etc.)
- You need simple content like dividers, placeholder text, or basic structure
- The goal is to isolate and showcase the specific component's functionality

**Example - Card stories with inline HTML:**
```typescript
export const Default: Story = {
  render: args => (
    <Card {...args}>
      <div style={{ padding: '16px' }}>
        <h3 style={{ margin: '0 0 16px 0' }}>Card Title</h3>
        <hr style={{ margin: '16px 0', border: 'none', borderTop: '1px solid #e0e0e0' }} />
        <p style={{ margin: 0 }}>This is the card content area.</p>
      </div>
    </Card>
  ),
};
```

**Use real component library components when:**
- Testing composition-heavy components that are meant to contain other components
- The component's primary purpose is to compose other library components
- Real usage patterns require combining multiple library components

**Rationale:**
- Keeps stories focused on the component being tested
- Avoids coupling between component stories
- Makes it easier to identify issues with specific components
- Reduces maintenance burden when components change

## Mock Components

### Icon Mock
For components that use `@hubspot/cms-components` Icon, a mock is available:

**Location:** `.storybook/mocks/cms-components.tsx`
**Usage:** Automatically used in Storybook environment

**Key Points:**
- Mock Icon component renders a generic SVG icon
- For `iconFieldPath` prop, **any text string will work** to display the icon

### Creating New Mocks
If you need to mock other components:

1. Create or update the mock in `.storybook/mocks/`
2. Export the mock component with the same name as the real component
