# Drawer Component

A slide-out panel component that overlays content from the side (left/right), top, or bottom of the viewport. Provides a modal-like experience for displaying supplementary content, navigation menus, or forms without navigating away from the current page.

## Import path
```tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';
```

## Purpose

The Drawer component provides a consistent way to create slide-out panels. It renders a fixed-position container that slides in from any edge of the viewport with smooth animations. The component automatically manages body scroll locking, keyboard interactions (ESC to close), and accessibility attributes. Use this component when you need to display additional content without losing the current page context, such as navigation menus, filters, settings panels, or detailed forms.

## Component Structure

```
Drawer/
├── index.tsx                       # Main component with render logic
├── types.ts                        # TypeScript type definitions
├── hooks/
│   └── index.tsx                   # useDrawer hook for state management
├── index.module.scss               # CSS module with animations
└── stories/
    ├── Drawer.stories.tsx          # Component usage examples
    ├── DrawerDecorator.tsx         # Storybook decorator
    └── DrawerDecorator.module.css  # Decorator styles
```

## Components

### Drawer (Main Component)

**Purpose:** Renders a fixed-position panel that slides in from any viewport edge with overlay support and body scroll locking.

**Props:**
```tsx
{
  open: boolean;                                        // Controls drawer visibility (required)
  direction: 'left' | 'right' | 'top' | 'bottom';       // Edge from which drawer slides (required)
  variant?: 'primary' | 'secondary' | 'tertiary';       // Visual style variant (default: 'primary')
  className?: string;                                   // Additional CSS classes
  style?: React.CSSProperties;                          // Inline styles
  children?: React.ReactNode;                           // Drawer content
  showOverlay?: boolean;                                // Show dark backdrop overlay (default: true)
  onOverlayClick?: () => void;                          // Callback when overlay is clicked
  fullScreen?: boolean;                                 // Make drawer fill entire viewport (default: false)
  size?: string;                                        // Drawer width/height in CSS units (default: '300px')
  'aria-label'?: string;                                // Accessible label for the drawer
  'aria-labelledby'?: string;                           // ID of element that labels the drawer
}
```

### useDrawer Hook

**Purpose:** Provides drawer state management with open, close, and toggle functions.

**Return Type:**
```tsx
{
  isOpen: boolean;      // Current open/closed state
  open: () => void;     // Opens the drawer
  close: () => void;    // Closes the drawer
  toggle: () => void;   // Toggles drawer state
}
```

## Usage Examples

### Island Usage

The Drawer component requires JavaScript for interactive state management and must be used within an Island component.

#### Island Component

```tsx
// islands/DrawerIsland.tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';

export default function DrawerIsland({ menuItems, direction = 'right', size = '320px' }) {
  const { isOpen, open, close } = useDrawer();

  return (
    <>
      <button onClick={open}>Open Menu</button>
      <Drawer
        open={isOpen}
        direction={direction}
        size={size}
        onOverlayClick={close}
        aria-label="Navigation menu"
      >
        <nav style={{ padding: '24px' }}>
          <h2>Menu</h2>
          <ul>
            {menuItems?.map((item, index) => (
              <li key={index}>
                <a href={item.url}>{item.label}</a>
              </li>
            ))}
          </ul>
          <button onClick={close}>Close</button>
        </nav>
      </Drawer>
    </>
  );
}
```

#### Module File

```tsx
import { Island } from '@hubspot/cms-components';
import DrawerIsland from './islands/DrawerIsland?island';

export const Component = ({ fieldValues }) => {
  return (
    <Island
      module={DrawerIsland}
      menuItems={fieldValues.menuItems}
      direction={fieldValues.direction}
      size={fieldValues.size}
    />
  );
};
```

### Basic Drawer (Right Side)

```tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';

export default function MyComponent() {
  const { isOpen, open, close } = useDrawer();

  return (
    <>
      <button onClick={open}>Open Menu</button>
      <Drawer
        open={isOpen}
        direction="right"
        onOverlayClick={close}
        aria-label="Navigation menu"
      >
        <div style={{ padding: '24px' }}>
          <h2>Menu</h2>
          <nav>
            {/* Navigation content */}
          </nav>
          <button onClick={close}>Close</button>
        </div>
      </Drawer>
    </>
  );
}
```

### Left Side Drawer

```tsx
<Drawer
  open={isOpen}
  direction="left"
  onOverlayClick={close}
  aria-label="Side navigation"
>
  <div style={{ padding: '24px' }}>
    <h2>Navigation</h2>
    {/* Drawer content */}
  </div>
</Drawer>
```

### Top Drawer

```tsx
<Drawer
  open={isOpen}
  direction="top"
  size="400px"
  onOverlayClick={close}
  aria-label="Details panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Details</h2>
    {/* Content */}
  </div>
</Drawer>
```

### Bottom Drawer

```tsx
<Drawer
  open={isOpen}
  direction="bottom"
  size="50vh"
  onOverlayClick={close}
  aria-label="Details panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Details</h2>
    {/* Content */}
  </div>
</Drawer>
```

### Custom Size Drawer

```tsx
<Drawer
  open={isOpen}
  direction="right"
  size="500px"
  onOverlayClick={close}
  aria-label="Details panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Details</h2>
    {/* Content */}
  </div>
</Drawer>
```

### Full Screen Drawer

```tsx
<Drawer
  open={isOpen}
  direction="right"
  fullScreen
  onOverlayClick={close}
  aria-label="Full screen content"
>
  <div style={{ padding: '48px' }}>
    <h1>Full Screen Content</h1>
    <p>This drawer takes up the entire viewport.</p>
    <button onClick={close}>Close</button>
  </div>
</Drawer>
```

### Without Overlay

```tsx
<Drawer
  open={isOpen}
  direction="right"
  showOverlay={false}
  aria-label="Side panel"
>
  <div style={{ padding: '24px' }}>
    <h2>Panel</h2>
    <p>Background remains visible and interactive.</p>
    <button onClick={close}>Close</button>
  </div>
</Drawer>
```

### With Toggle Control

```tsx
const { isOpen, toggle } = useDrawer();

<>
  <button onClick={toggle}>
    {isOpen ? 'Close' : 'Open'} Drawer
  </button>
  <Drawer
    open={isOpen}
    direction="right"
    showOverlay={false}
    aria-label="Toggleable drawer"
  >
    <div style={{ padding: '24px' }}>
      {/* Content */}
    </div>
  </Drawer>
</>
```

## HubSpot CMS Integration

### Field Definitions

The Drawer component is primarily interactive and does not provide pre-built field definitions. Since drawers are typically controlled programmatically (open/close state, callbacks), the component focuses on behavior rather than static content configuration. You can define your own fields based on the content you want to display inside the drawer (such as menu items, settings options, or form data).

### Module Usage Example

When integrating Drawer into a HubSpot module, create an Island component that manages drawer state and pass any necessary data from field values.

```tsx
import { Island } from '@hubspot/cms-components';
import DrawerIsland from './islands/DrawerIsland?island';

export default function NavigationModule({ fieldValues }) {
  return (
    <Island
      module={DrawerIsland}
      menuItems={fieldValues.menuItems}
      direction={fieldValues.direction || 'left'}
      size={fieldValues.size || '320px'}
    />
  );
}
```

## Styling

### CSS Variables

The Drawer component uses CSS variables for theming and customization:

**Base Styles:**
- `--hscl-drawer-backgroundColor`: Drawer background color (default: white)
- `--hscl-drawer-boxShadow`: Shadow effect for depth (default: 0 2px 8px rgba(0, 0, 0, 0.15))
- `--hscl-drawer-zIndex`: Drawer z-index (default: 1000)
- `--hscl-drawer-size`: Drawer width (for left/right) or height (for top/bottom)

**Overlay Styles:**
- `--hscl-drawer-overlay-backgroundColor`: Overlay background (default: rgba(0, 0, 0, 0.5))
- `--hscl-drawer-overlay-backdropFilter`: Backdrop blur effect (default: blur(3px))
- `--hscl-drawer-overlay-zIndex`: Overlay z-index (default: 999)

### Custom Styling Example

```tsx
<Drawer
  open={isOpen}
  direction="right"
  onOverlayClick={close}
  style={{
    '--hscl-drawer-backgroundColor': '#f5f5f5',
    '--hscl-drawer-boxShadow': '0 4px 16px rgba(0, 0, 0, 0.2)',
  } as React.CSSProperties}
>
  {/* Content */}
</Drawer>
```

## Accessibility

The Drawer component follows accessibility best practices:

- **Semantic HTML**: Uses `role="dialog"` and `aria-modal="true"` for proper semantics
- **Keyboard Navigation**:
  - `ESC` key closes the drawer
  - Focus management is handled automatically
- **Screen Reader Support**:
  - `aria-label` or `aria-labelledby` should be provided to describe drawer purpose
  - `aria-hidden` attribute toggles based on open state
  - Proper ARIA roles announce drawer as a modal dialog
- **Body Scroll Locking**: Background content scroll is disabled when drawer is open
- **Focus Trap**: Overlay click closes drawer, preventing accidental interactions with background
- **Visual Indicators**: Overlay provides clear visual feedback that drawer is modal
- **Reduced Motion**: Respects `prefers-reduced-motion` for users with motion sensitivity

## Best Practices

- **Use Islands for interactivity**: The Drawer component is interactive and requires JavaScript, so it must be used within an Island component in modules
- **Provide aria-label**: Always include `aria-label` or `aria-labelledby` to describe the drawer's purpose
- **Choose appropriate direction**:
  - `left` or `right`: Best for navigation menus, settings panels, or supplementary content
  - `top`: Good for notification panels or collapsible headers
  - `bottom`: Ideal for mobile-style action sheets or details panels
- **Size considerations**:
  - Consider `fullScreen` for complex workflows or extensive forms
- **Handle close actions**: Always provide a way to close the drawer (overlay click, close button)
- **Overlay usage**: Keep `showOverlay={true}` (default) for modal behavior, use `false` only for persistent sidebars
- **Body scroll locking**: The component automatically prevents background scrolling when open
- **ESC key support**: The drawer closes automatically when ESC is pressed (built-in)
- **Content structure**: Include a clear heading and close button within drawer content for usability
- **Avoid nested drawers**: Don't open multiple non-full-screen drawers simultaneously as it creates confusing UX
- **State management**: Use the `useDrawer` hook for consistent state management
- **Loading states**: Consider showing loading indicators within drawer content during async operations

## Common Patterns

### Navigation Menu Drawer

```tsx
import Drawer, { useDrawer } from '@hubspot/cms-component-library/Drawer';

export default function NavigationDrawer({ menuItems }) {
  const { isOpen, open, close } = useDrawer();

  return (
    <>
      <button onClick={open}>Menu</button>
      <Drawer
        open={isOpen}
        direction="left"
        size="320px"
        onOverlayClick={close}
        aria-label="Navigation menu"
      >
        <nav style={{ padding: '24px' }}>
          <h2>Navigation</h2>
          <ul>
            {menuItems.map((item, i) => (
              <li key={i}>
                <a href={item.url}>{item.label}</a>
              </li>
            ))}
          </ul>
          <button onClick={close}>Close</button>
        </nav>
      </Drawer>
    </>
  );
}
```


## Related Components

- **useDrawer**: Hook for managing drawer open/close state. Always use this hook rather than managing state manually.
