# List

The `List` component renders a collection of items in a structured way. It
supports both
[flat lists](/storybook/web/?path=/story/components-lists-and-tables-list--basic)
and
[sectioned lists](/storybook/web/?path=/story/components-lists-and-tables-list--sectioned-list-items),
and allows for
[custom rendering](/storybook/web/?path=/story/components-lists-and-tables-list--sectioned-list-with-custom-item)
of list items via a render function..

## Design & usage Guidelines

The `List` component is a great solution if you are looking to display a
collection of objects that are either the same type or related to one another,
like billing or activity feed. The main purpose of a list is to help the user
find an object that leads to its full representation.

The majority of list implementations can be achieved without needing to
customize rendering. If your items conform to the `ListItemProps` interface, the
component will render them automatically with the default behavior. A prop of
type `ListItemProps` must be used when using default rendering.

#### Basic Flat List

```tsx
import React from "react";
import type { ComponentProps } from "react";
import { action } from "storybook/actions";
import { List } from "@jobber/components/List";
import { Card } from "@jobber/components/Card";
import type { ListItemProps } from "@jobber/components/List";

const basicItems: ListItemProps[] = [
  {
    id: 1,
    icon: "wallet",
    iconColor: "orange",
    content: "Payment for Invoice #39",
    value: "-$300.00",
    caption: "Sep 25, 2019",
    onClick: () => {
      action("alert")("TODO: Implement onClick");
    },
  },
  {
    id: 3,
    icon: "paidInvoice",
    content: "Invoice #39",
    value: "$300.00",
    caption: "Sep 24, 2019",
    onClick: () => {
      action("alert")("TODO: Implement onClick");
    },
  },
];

export function ListBasicExample(props: Partial<ComponentProps<typeof List>>) {
  return (
    <Card>
      <List items={basicItems} {...props} />
    </Card>
  );
}
```

#### Sectioned List

If a section field is present in any item, the list will group the items by
their section and display them under section headers. If even one section key
exists in any of items then it will be expected that every object within that
collection will have a section defined.

If a section is missing, "Other" will be used by default, and any items with a
section of "Other" will be grouped with that first occurrence that has no
section. To provide your header value for items missing a section, use the
`defaultSectionHeader` prop.

```tsx
import React from "react";
import type { ComponentProps } from "react";
import { action } from "storybook/actions";
import { List } from "@jobber/components/List";
import { Card } from "@jobber/components/Card";
import type { ListItemProps } from "@jobber/components/List";

const sectionedListItems: ListItemProps[] = [
  {
    id: 1,
    icon: "addNote",
    title: "Darryl Tec added a note",
    content: [
      "_'Called the client. Asked if they want the luxury package, they said yes!'_",
      "Deck Build",
    ],
    caption: "1 minute ago",
    section: "Today",
    isActive: true,
    onClick: () => {
      action("alert")("TODO: Implement onClick");
    },
  },
  {
    id: 2,
    icon: "checkmark",
    iconColor: "green",
    title: "Josh Elford completed a visit",
    content: "Annual Maintenance",
    caption: "2 hours ago",
    section: "Today",
    onClick: () => {
      action("alert")("TODO: Implement onClick");
    },
  },
  {
    id: 3,
    icon: "badInvoice",
    title: "Payment failed",
    content: "For services rendered",
    value: "$300.00",
    caption: "1 day ago",
    section: "Yesterday",
    onClick: () => {
      action("alert")("TODO: Implement onClick");
    },
  },
];

export function ListSectionedListItemsExample(
  props: Partial<ComponentProps<typeof List>>,
) {
  return (
    <Card>
      <List items={sectionedListItems} {...props} />
    </Card>
  );
}
```


## Component customization

### Custom rendering (advanced usage)

If you need more control over the rendering of list items, you can provide a
**custom render function** using one or more of the `customRenderItem` or
`customRenderSection` props.

This is useful if the default rendering of the `ListItem` or section header
component doesn’t meet your requirements. If a custom render function is
provided onClick, url, and hover functionality will still work. Sectioned list
functionality will also remain the same.

If a custom render function is provided for the section only the appearance of
the header will change.

**Example: Custom Render Function with Custom Interface**

```tsx
import { List } from "./List";

interface ProductListItemProps extends BaseListItemProps {
  readonly name: string;
  readonly price: string;
  readonly section: string;
  readonly onClick?: () => void;
}

const items: ProductListItemProps[] = [
  { id: 1, name: "Lawn Mower", price: "$250.00", section: "Garden Tools", onClick: () => alert("Lawn Mower clicked") },
  { id: 2, name: "Hedge Trimmer", price: "$85.00", section: "Garden Tools", onClick: () => alert("Hedge Trimmer clicked") },
  { id: 3, name: "Drill", price: "$50.00", section: "Power Tools", onClick: () => alert("Drill clicked") },
  { id: 4, name: "Screwdriver", price: "$10.00", section: "Hand Tools", onClick: () => alert("Screwdriver clicked") },
];


const renderProductItem = (item: ProductListItemProps) => (
    <Flex template={["shrink", "grow"]} align="start">
        <Text>{item.price}</Text>
        <Heading level={4}>{item.name}</Heading>
    </Flex>
  </div>
);

export const CustomRenderExample = () => (
  <List items={items} customRenderItem={renderProductItem} />
);

```

**In this example**:

* We define a custom interface (`ProductListItemProps`) that extends
  `BaseListItemProps`.
* The `customRenderItem` function provides complete control over how each item
  is rendered.

**About Custom Rendering:**

* Use a custom render function when you need complex layouts that the `ListItem`
  component can’t provide.
* Create your own interface extending `BaseListItemProps` to define the fields
  you need.

#### Using Your own Custom Item Styles

Some styles (such as hover and active item styles) are always applied to list
items, even when using a custom render function. However, other styles (like
padding, alignment, and border-bottom) are applied by default but can be
disabled by setting `customItemStyles={true}` alongside `customRenderItem`. This
option allows you to remove some of the default item styles and apply your own.

For instance, in the Simple List with Custom Styles example, setting
`customItemStyles={true}` allows for the creation of a list with rounded corners
and no border-bottom. You can also experiment by toggling this prop in the other
custom render examples to observe its impact.

Note: The `customItemStyles` prop is only valid when a `customRenderItem`
function is provided. The same principle applies to the `customSectionStyles`,
and `customRenderSection`.


## Props

### Web

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `items` | `T[]` | Yes | — | Array of the list items. |
| `customItemStyles` | `boolean` | No | — | Set to true for more control over the item styles. Only modifies styles when used with customRenderItem. |
| `customRenderItem` | `(item: T) => ReactNode` | No | — | A function that will be called for each item instead of the default rendering @argument item - The item to render |
| `customRenderSection` | `(sectionName: string) => ReactNode` | No | — | A function that will be called for each section heading instead of the default rendering @argument sectionName - The ... |
| `customSectionStyles` | `boolean` | No | — | Set to true for more control over the section heading styles. Only modifies styles when used with customRenderItem. |
| `defaultSectionHeader` | `string` | No | `Other` | A default section header value for items that do not have a section. |
| `labelledBy` | `string` | No | — | An ID of an element that provides the labelling for this list. |
