# List Component

A flexible list component that renders ordered (`<ol>`) or unordered (`<ul>`) lists with customizable spacing, variants, and marker visibility.

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

## Purpose

The List component provides a consistent way to render lists in HubSpot CMS projects. It combines the semantic correctness of native HTML list elements with the flexibility of the Flex component for layout control. The component supports both ordered (numbered) and unordered (bulleted) lists, with configurable marker visibility.

## Compositional Component Pattern

The List component follows a **parent/child compositional pattern**, consisting of two separate but related components:

| Component | Role | Renders |
|-----------|------|---------|
| `List` | Container | `<ol>` or `<ul>` |
| `ListItem` | Child | `<li>` |

**Why this pattern?**
- **Separation of concerns**: List handles container semantics (ordered vs unordered, gap, variant, marker visibility), while ListItem handles individual item presentation
- **Flexibility**: Modules can pass any content as children to ListItem, including custom icons
- **HubSpot CMS integration**: Each component has its own field definitions, allowing RepeatedGroup usage for dynamic lists
- **Type safety**: Props are scoped to their respective components, preventing invalid configurations

**Key relationships:**
- ListItem should **always** be used inside a List component
- List passes layout context (gap, variant styling, marker visibility) to children via CSS
- ListItem renders children directly in the `<li>` element

This pattern is distinct from the "Compound Component Pattern" (e.g., `Button.ContentFields`) which attaches field definitions—List uses *both* patterns together.

## Component Structure

```
List/
├── index.tsx                     # Main List component with render logic
├── types.ts                      # TypeScript type definitions
├── ContentFields.tsx             # HubSpot field definitions for list type
├── StyleFields.tsx               # HubSpot field definitions for variant and marker
├── index.module.scss             # CSS module for list styling
├── ListItem/
│   ├── index.tsx                 # ListItem component
│   ├── types.ts                  # ListItem TypeScript types
│   ├── ContentFields.tsx         # HubSpot field definitions for list item text
│   └── index.module.scss         # CSS module for list item styling
└── stories/
    ├── List.stories.tsx          # Storybook examples
    ├── ListDecorator.tsx         # Storybook decorator
    └── ListDecorator.module.css  # Decorator styles
```

## Components

### List (Main Component)

**Purpose:** Container component that renders either an `<ol>` or `<ul>` element based on the `listType` prop, with Flex-based layout control.

**Props:**
```tsx
{
  listType?: 'ordered' | 'unordered';                     // List type - 'ordered' renders <ol>, 'unordered' renders <ul>
  variant?: 'primary' | 'secondary' | 'tertiary';         // Visual style variant
  gap?: string;                                           // Space between list items (any valid CSS length value, e.g., '16px', '1rem', '0')
  showMarker?: boolean;                                   // Show list markers (bullets/numbers) - default: true
  className?: string;                                     // Additional CSS classes
  style?: CSSVariables;                                   // Inline styles (including CSS custom properties)
  children?: React.ReactNode;                             // ListItem components
}
```

### ListItem (Child Component)

**Purpose:** Individual list item component that renders an `<li>` element. Children are rendered directly inside the `<li>`.

**Props:**
```tsx
{
  children?: React.ReactNode;                 // List item content (text, elements, icons, etc.)
  className?: string;                         // Additional CSS classes
  style?: React.CSSProperties;                // Inline styles
}
```

## Usage Examples

### Basic Unordered List

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

<List listType="unordered" variant="primary">
  <ListItem>First item</ListItem>
  <ListItem>Second item</ListItem>
  <ListItem>Third item</ListItem>
</List>
```

### Basic Ordered List

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

<List listType="ordered" variant="primary">
  <ListItem>Step one</ListItem>
  <ListItem>Step two</ListItem>
  <ListItem>Step three</ListItem>
</List>
```

### List without Markers

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

<List listType="unordered" variant="primary" showMarker={false}>
  <ListItem>Item without bullet</ListItem>
  <ListItem>Another item without bullet</ListItem>
  <ListItem>Third item without bullet</ListItem>
</List>
```

### List with Custom Gap

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

<List listType="unordered" variant="primary" gap="1rem">
  <ListItem>Spaced item one</ListItem>
  <ListItem>Spaced item two</ListItem>
  <ListItem>Spaced item three</ListItem>
</List>
```

### List Items with Custom Content (Icons)

When you need icons or custom content in list items, set `showMarker={false}` to hide bullets/numbers, then handle your own layout:

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';
import Icon from '@hubspot/cms-component-library/Icon';

<List listType="unordered" variant="primary" showMarker={false} gap="0.5rem">
  <ListItem style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}>
    <Icon name="check" size={16} />
    <span>Item with custom icon</span>
  </ListItem>
  <ListItem style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}>
    <Icon name="check" size={16} />
    <span>Another item with icon</span>
  </ListItem>
</List>
```

### With Custom Styling

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

<List
  listType="unordered"
  variant="primary"
  gap="8px"
  className="custom-list-class"
  style={{ maxWidth: '500px' }}
>
  <ListItem>Custom styled item</ListItem>
</List>
```

## HubSpot CMS Integration

### Field Definitions

The List component provides field definitions for easy integration with HubSpot CMS modules.

#### List.ContentFields

Configurable props for list type selection:

```tsx
<List.ContentFields
  listTypeName="listType"
  listTypeLabel="List type"
  listTypeDefault="unordered"
/>
```

**Fields:**
- `listType`: ChoiceField for selecting ordered or unordered list

#### List.StyleFields

Configurable props for variant and marker selection:

```tsx
<List.StyleFields
  variantName="variant"
  variantLabel="Variant"
  variantDefault="primary"
  showMarkerName="showMarker"
  showMarkerLabel="Show list markers"
  showMarkerDefault={true}
/>
```

**Fields:**
- `variant`: ChoiceField for selecting visual style (primary, secondary, tertiary)
- `showMarker`: BooleanField for toggling marker visibility

**Note:** Gap must be set directly via the `gap` prop on the List component, as there is no field definition for it in StyleFields.

#### ListItem.ContentFields

Configurable props for list item content:

```tsx
<ListItem.ContentFields
  textName="text"
  textLabel="Item text"
  textDefault="Add a list item here."
/>
```

**Fields:**
- `text`: TextField for list item text content

### Module Usage Example

```tsx
import { ModuleMeta } from '../types/modules.js';
import List, { ListItem } from '@hubspot/cms-component-library/List';
import { GapValue } from '@hubspot/cms-component-library/List/types';

type FeatureListModuleProps = {
  listType?: 'ordered' | 'unordered';
  style?: {
    variant?: 'primary' | 'secondary' | 'tertiary';
    gap?: GapValue;
    showMarker?: boolean;
  };
  listItems?: Array<{
    text?: string;
  }>;
};

export const Component = ({
  listType = 'unordered',
  style,
  listItems = [],
}: FeatureListModuleProps) => {
  const variant = style?.variant ?? 'primary';
  const gap = style?.gap ?? '0px';
  const showMarker = style?.showMarker ?? true;

  return (
    <List
      listType={listType}
      variant={variant}
      gap={gap}
      showMarker={showMarker}
    >
      {listItems.map(({ text }, index) => (
        <ListItem key={index}>{text}</ListItem>
      ))}
    </List>
  );
};

export const meta: ModuleMeta = {
  label: 'Feature List',
  content_types: [],
  icon: '',
  categories: ['text'],
};
```

### Module Fields Example

```tsx
import {
  FieldGroup,
  ModuleFields,
  RepeatedFieldGroup,
  TextField,
} from '@hubspot/cms-components/fields';
import List, { ListItem } from '@hubspot/cms-component-library/List';

const defaultItem = {
  text: 'Add a list item here.',
};

export const fields = (
  <ModuleFields>
    <List.ContentFields />
    <RepeatedFieldGroup
      label="List items"
      name="listItems"
      occurrence={{ min: 1, max: 20, default: 4 }}
      default={[defaultItem, defaultItem, defaultItem, defaultItem]}
    >
      <ListItem.ContentFields />
    </RepeatedFieldGroup>
    <FieldGroup label="Style" name="style" tab="STYLE">
      <List.StyleFields
        variantName="variant"
        variantLabel="Variant"
        variantDefault="primary"
      />
      <TextField
        label="Gap"
        name="gap"
        default="0px"
        helpText="Space between list items (e.g., '0', '0.5rem', '16px')"
      />
    </FieldGroup>
  </ModuleFields>
);
```

## Styling

### CSS Variables

The List component uses CSS variables for theming and customization:

**List Item Text:**
- `--hscl-list-item-text-color`: Text color for list items (set via variant)
- `--hscl-listItem-textColor-primary`: Primary variant text color
- `--hscl-listItem-textColor-secondary`: Secondary variant text color
- `--hscl-listItem-textColor-tertiary`: Tertiary variant text color

### Custom Styling Example

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

<List
  listType="unordered"
  variant="primary"
  style={{
    '--hscl-list-item-text-color': '#333333',
  }}
>
  <ListItem>Custom colored item</ListItem>
</List>
```

### CSS Module Classes

**List (`index.module.scss`):**
- `.list`: Base list styles (removes default padding/margin, positions bullets inside)
- `.withMarker`: Applies `display: list-item` to children (shows bullets/numbers)
- `.noMarker`: Applies `list-style: none` (hides markers—modules handle their own internal layout)
- Nested `.list`: Indented styling for nested lists

**ListItem (`ListItem/index.module.scss`):**
- `.listItem`: Base list item styles with text color

## Accessibility

The List component follows accessibility best practices:

- **Semantic HTML**: Renders appropriate elements (`<ol>` for ordered, `<ul>` for unordered)
- **List Semantics**: Proper `<li>` elements maintain screen reader list navigation
- **Keyboard Navigation**: Native list keyboard navigation is preserved
- **Content Structure**: Lists help screen reader users understand grouped content

## Best Practices

- **Choose the right list type**: Use `ordered` for sequential steps or rankings, `unordered` for non-sequential items
- **Marker visibility**: Use `showMarker={true}` (default) for standard lists, `showMarker={false}` when providing custom markers or icons as children
- **Consistent variants**: Use the same variant across related lists for visual consistency
- **Gap selection**: Use any valid CSS length value (e.g., '0', '0.5rem', '1rem', '16px') for spacing between items
- **Dynamic rendering**: Always provide unique `key` props when mapping arrays to ListItems
- **CSS Variables**: Override design tokens using CSS variables rather than hardcoding values
- **Custom icons**: When using custom icons, set `showMarker={false}` and pass icons as part of ListItem children
- **Custom content layout**: When using `showMarker={false}`, markers are hidden but no layout is imposed. Modules are responsible for their own internal layout—use `style={{ display: 'flex', gap: '8px', alignItems: 'flex-start' }}` on ListItem or wrap content in a styled container

## Common Patterns

### Feature List with Custom Icons

```tsx
import List, { ListItem } from '@hubspot/cms-component-library/List';
import Icon from '@hubspot/cms-component-library/Icon';

const itemStyle = { display: 'flex', gap: '8px', alignItems: 'flex-start' };

const FeatureList = ({ features, variant = 'primary' }) => (
  <List listType="unordered" variant={variant} showMarker={false} gap="0.5rem">
    {features.map((feature, index) => (
      <ListItem key={index} style={itemStyle}>
        <Icon name="check" size={20} fill={`var(--hscl-listItem-iconColor-${variant})`} />
        <span>{feature.text}</span>
      </ListItem>
    ))}
  </List>
);
```

### Step-by-Step Instructions

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

const StepList = ({ steps, variant = 'primary' }) => (
  <List listType="ordered" variant={variant} gap="1rem">
    {steps.map((step, index) => (
      <ListItem key={index}>{step.text}</ListItem>
    ))}
  </List>
);
```

### Simple Text List

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

<List listType="unordered" variant="primary">
  <ListItem>Simple item one</ListItem>
  <ListItem>Simple item two</ListItem>
  <ListItem>Simple item three</ListItem>
</List>
```

## Related Components

- **Flex**: Used internally for list layout control. The List component extends Flex capabilities.
- **Icon**: Can be used as children in ListItem for custom icon lists.
