import { ArgTypes, Canvas, Meta } from '@storybook/addon-docs/blocks';
import {
  Table,
  TableHead,
  TableHeader,
  TableRow,
  TableCell,
  TableBody,
} from '../DataTable';

<Meta isTemplate />

# DataTable

[Source code](https://github.com/carbon-design-system/carbon/tree/main/packages/react/src/components/DataTable)
&nbsp;|&nbsp;
[Usage guidelines](https://www.carbondesignsystem.com/components/data-table/usage)
&nbsp;|&nbsp;
[Accessibility](https://www.carbondesignsystem.com/components/data-table/accessibility)

<Canvas id="components-datatable-basic--default" />

{/* <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> */}

## Table of Contents

- [Getting started](#getting-started)
- [Sorting](#sorting)
  - [Programmatic sorting](#programmatic-sorting)
  - [Custom sorting](#custom-sorting)
- [Expansion](#expansion)
  - [Programmatic expansion](#programmatic-expansion)
- [Selection](#selection)
  - [Programmatic selection](#programmatic-selection)
- [Filtering](#filtering)
  - [Multiple filters with batch updates](#multiple-filters-with-batch-updates)
- [Batch actions](#batch-actions)
- [Toolbar](#toolbar)
  - [Overflow Menu](#overflow-menu)
- [Text Wrapping Alignment](#text-wrapping-alignment)
  - [Table with `experimentalAutoAlign = true`](#table-with-experimentalautoalign--true)
  - [Table with `experimentalAutoAlign = false`](#table-with-experimentalautoalign--false)
- [Accessibility Considerations](#accessibility-considerations)
  - [Accessible Name](#accessible-name)
- [Props](#props)
  - [Render function](#render-function)
    - [Prop getters](#prop-getters)
    - [Actions](#actions)
    - [State](#state)
    - [Example](#example)
- [Feedback](#feedback)

{/* <!-- END doctoc generated TOC please keep comment here to allow auto update --> */}

## Getting started

You can implement a data table in your project by importing the `DataTable`
component and using it along with any additional table components that you need.

```js
import {
  DataTable,
  Table,
  TableHead,
  TableRow,
  TableHeader,
  TableBody,
  TableCell,
} from '@carbon/react';
```

The `DataTable` component needs two props to be provided: `rows` and `headers`.
The `rows` prop is the data you would like to display in the rows of your table,
whereas `headers` specifies which table column headings should be displayed.

Each row in the `rows` prop should provide a unique `id`, but everything else
can be configured according to your data. For example:

```js
const rows = [
  {
    id: 'a',
    name: 'Load balancer 1',
    status: 'Disabled',
  },
  {
    id: 'b',
    name: 'Load balancer 2',
    status: 'Starting',
  },
  {
    id: 'c',
    name: 'Load balancer 3',
    status: 'Active',
  },
];
```

The `headers` will specify the column headers in your table. The `header`
property for each item will describe what the user sees in the table column
header. The `key` property is used to relate what value a row has for the given
column.

```js
const headers = [
  {
    key: 'name',
    header: 'Name',
  },
  {
    key: 'status',
    header: 'Status',
  },
];
```

You can use the `rows` and `headers` props above to render a `DataTable` by
writing:

```jsx
<DataTable rows={rows} headers={headers}>
  {({
    rows,
    headers,
    getTableProps,
    getHeaderProps,
    getRowProps,
    getCellProps,
  }) => (
    <Table {...getTableProps()}>
      <TableHead>
        <TableRow>
          {headers.map((header) => (
            <TableHeader {...getHeaderProps({ header })}>
              {header.header}
            </TableHeader>
          ))}
        </TableRow>
      </TableHead>
      <TableBody>
        {rows.map((row) => (
          <TableRow {...getRowProps({ row })}>
            {row.cells.map((cell) => (
              <TableCell {...getCellProps({ cell })}>{cell.value}</TableCell>
            ))}
          </TableRow>
        ))}
      </TableBody>
    </Table>
  )}
</DataTable>
```

After rendering a `DataTable` component using the code snippet above, you can
optionally add any number of features including sorting, row expansion,
filtering, row selection, batch actions.

For more information, see each usage section below for the functionality that
you're interested in. If you want to see a full list of the arguments passed
into your render prop, visit the [render props](#render-props) section.

## Sorting

In order to sort the rows in your data table, you will need to pass in the
`isSortable` prop to the `DataTable` component.

<Canvas id="components-datatable-sorting--default" />

Optionally, you can pass in `isSortable` to each `TableHeader` that you want to
be sorted, or not sorted. This is useful if you only want to enable sorting on
some table column headers, but not all.

```jsx
<TableHeader {...getHeaderProps({ header, isSortable: false })}>
  {header.header}
</TableHeader>
```

### Programmatic sorting

In addition to the prop getter specified in the previous section, you can also
change the sort status of the table by using the `sortBy` action made available
in your `children` prop. This `sortBy` utility takes in the `key` of the header
you want to sort by as an argument. After invoking this method with the given
`key`, the table should be sorted by the header that you've specified.

### Custom sorting

If the default sorting logic doesn't match your use-case, you can provide a
custom sort method as a `sortRow` prop to `DataTable`.

`sortRow` is a method that takes in the values of two cells, in addition to some
info, and should return -1, 0, or 1 as a result (mirroring the native sort
behavior in JavaScript).

The two cells that are passed in are derived by accessing the value of the sort
header in each row that we're comparing. For example, if we're sorting on the
`Foo` header, with the `foo` key available in each row, then for row `a` and row
`b` we would get the `a.foo` and `b.foo` field values.

As a result, a custom `sortRow` function would take on the following shape:

```js
function customSortRow(cellA, cellB, { sortDirection, sortStates, locale }) {
  if (sortDirection === sortStates.DESC) {
    return compare(cellB, cellA, locale);
  }

  return compare(cellA, cellB, locale);
}
```

## Expansion

The `DataTable` component supports row-level expansion when combined with the
`TableExpandHeader`, `TableExpandRow`, and `TableExpandedRow` components.

<Canvas id="components-datatable-expansion--default" />

_Note: press "Show code" above to view a code snippet of this example_

The `TableExpandHeader` is placed before all the other headers as a placeholder
column. In each row that you would like to be expanded, you will use
`TableExpandRow`. It's important that `getRowProps` is applied to
`TableExpandRow` so that it has the necessary props. `TableExpandedRow` is used
for the content that will be displayed only when the row is expanded. It's
important that `getExpandedRowProps` is applied to `TableExpandedRow` so that it
has the necessary props.

### Programmatic expansion

You can use the `expandRow` action made available through your `children` prop
function to toggle the expansion state of a given row. This method takes in the
row id as a single argument.

## Selection

The `DataTable` component supports row selection when used with the
`TableSelectAll` and `TableSelectRow` components.

<Canvas id="components-datatable-selection--default" />

_Note: press "Show code" above to view a code snippet of this example_

In the example above, `TableSelectAll` is placed before all other headers. You
should use `getSelectionProps` on `TableSelectAll` to enable selecting all rows
in the table.

`TableSelectRow` is also used in this example, and is placed before all the
cells in a row. It also uses `getSelectionProps` props, but also takes in the
current `row` in order to retrieve selection state about the given row.

You can access all the selected rows through the `selectedRows` property passed
into your `children` prop function.

### Programmatic selection

You can use either of the following actions from your `children` prop function
to update the selection status of a row:

- `selectAll`: invoking this will toggle the selection of all rows, either by
  making all selected or de-selecting all rows
- `selectRow`: invoking this will toggle the selection of a specific row. Takes
  in a valid row id as an argument

## Filtering

Filtering in a `DataTable` is provided through usage of the `TableToolbar` and
the `TableToolbarSearch` component. By default, filtering is handled by a
built-in `filterRows` implementation. You can customize filtering behavior by
passing your own `filterRows` function as a prop.

By default `filterRows` is provided through our default implementation. However,
you can provide your own method if needed.

<Canvas id="components-datatable-filtering--default" />

In order to integrate filtering into your data table, you will need to provide
the `onInputChange` function provided to you from `DataTable`'s `children` prop
and pass it to the `onChange` prop of `TableToolbarSearch` in your
`TableToolbar` component.

### Multiple filters with batch updates

The filtering story shows an example of how to implement the
[batch filtering](https://carbondesignsystem.com/patterns/filtering/#multiple-filters-with-batch-updates)
pattern.

To implement this in your own project you should copy the
[TableToolbarFilter](https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/DataTable/stories/examples/TableToolbarFilter.tsx)
example component into your code. Then import and use it inside
`TableToolbarContent`:

```jsx
<TableToolbarFilter
  onApplyFilter={handleTableFilter}
  onResetFilter={handleOnResetFilter}
/>
```

You can write the code to handle the filters in the same file where you have
your DataTable with your data. Use the code below as a template and then
customize it as needed. Make sure to replace "YOUR_DATA" with the initial data
you want to pass into the DataTable.

```jsx
const [renderedRows, setRenderedRows] = useState(YOUR_DATA);

const handleTableFilter = (selectedCheckboxes) => {
  setRenderedRows([]);

  for (let i = 0; i < selectedCheckboxes.length; i++) {
    // Filter the items inside the rows list
    const filteredRows = YOUR_DATA.filter((row) => {
      return Object.values(row).some((value) =>
        String(value)
          .toLowerCase()
          .includes(selectedCheckboxes[i].toLowerCase())
      );
    });

    setRenderedRows((prevData) => {
      // Filter out duplicate rows
      const uniqueRows = filteredRows.filter((row) => {
        return !prevData.some((prevRow) => {
          return Object.keys(row).every((key) => {
            return row[key] === prevRow[key];
          });
        });
      });
      return [...prevData, ...uniqueRows];
    });
  }
};

const handleOnResetFilter = () => {
  setRenderedRows(rows);
};
```

Finally, pass the array of rows from the `useState` into the DataTable.

```jsx
<DataTable rows={renderedRows} headers={headers}>
  // ...
</DataTable>
```

## Batch actions

You can combine batch actions with the `DataTable` component to allow the user
to perform a single action on multiple selected rows. To do this, you can use
the following components:

- `TableToolbar`
- `TableToolbarAction`
- `TableToolbarSearch`
- `TableBatchActions`
- `TableBatchAction`
- `TableSelectAll`
- `TableSelectRow`

  <Canvas id="components-datatable-batch-actions--default" />

_Note: press "Show code" above to view a code snippet of this example_

## Toolbar

<Canvas id="components-datatable-toolbar--default" />

### Overflow Menu

An overflow menu can be added to the toolbar table. To add an overflow menu,
simply add a `TableCell` with the class `cds--table-column-menu` (replace the
prefix if you have added a custom prefix). Then add the overflow menu into the
cell.

```jsx
<TableBody>
  {rows.map((row) => (
    <TableRow {...getRowProps({ row })}>
      {row.cells.map((cell) => (
        <TableCell {...getCellProps({ cell })}>{cell.value}</TableCell>
      ))}
      <TableCell className="cds--table-column-menu">
        <OverflowMenu size="sm" flipped>
          <OverflowMenuItem>Action 1</OverflowMenuItem>
          <OverflowMenuItem>Action 2</OverflowMenuItem>
          <OverflowMenuItem>Action 3</OverflowMenuItem>
        </OverflowMenu>
      </TableCell>
    </TableRow>
  ))}
</TableBody>
```

## Text Wrapping Alignment

DataTable provides an experimental `experimentalAutoAlign` prop that you may opt
into to have the table automatically align contents to the top when at least one
table cell content is being wrapped on the table. Keep in mind this feature is
experimental and might not be performant in tables with large datasets.

### Table with `experimentalAutoAlign = true`

<div style={{ width: '300px' }}>
  <Table experimentalAutoAlign>
    <TableHead>
      <TableRow>
        <TableHeader>Header1</TableHeader>
        <TableHeader>Header2</TableHeader>
        <TableHeader>Header3</TableHeader>
      </TableRow>
    </TableHead>
    <TableBody>
      <TableRow>
        <TableCell>Wrapping content</TableCell>
        <TableCell>content</TableCell>
        <TableCell>content</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</div>

```jsx
<div style={{ width: '300px' }}>
  <Table experimentalAutoAlign>
    <TableHead>
      <TableRow>
        <TableHeader>Header1</TableHeader>
        <TableHeader>Header2</TableHeader>
        <TableHeader>Header3</TableHeader>
      </TableRow>
    </TableHead>
    <TableBody>
      <TableRow>
        <TableCell>Wrapping content</TableCell>
        <TableCell>content</TableCell>
        <TableCell>content</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</div>
```

### Table with `experimentalAutoAlign = false`

<div style={{ width: '300px' }}>
  <Table>
    <TableHead>
      <TableRow>
        <TableHeader>Header1</TableHeader>
        <TableHeader>Header2</TableHeader>
        <TableHeader>Header3</TableHeader>
      </TableRow>
    </TableHead>
    <TableBody>
      <TableRow>
        <TableCell>Wrapping content</TableCell>
        <TableCell>content</TableCell>
        <TableCell>content</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</div>

```jsx
<div style={{ width: '300px' }}>
  <Table>
    <TableHead>
      <TableRow>
        <TableHeader>Header1</TableHeader>
        <TableHeader>Header2</TableHeader>
        <TableHeader>Header3</TableHeader>
      </TableRow>
    </TableHead>
    <TableBody>
      <TableRow>
        <TableCell>Wrapping content</TableCell>
        <TableCell>content</TableCell>
        <TableCell>content</TableCell>
      </TableRow>
    </TableBody>
  </Table>
</div>
```

## Accessibility Considerations

### Accessible Name

To comply with accessibility requirements, make sure to supply the component
with an accessible name by providing either the `aria-label`, `aria-labelledby`
or `title` attribute to the `Table` component. Read more on the accessible
naming rule
[here](https://able.ibm.com/rules/archives/latest/doc/en-US/aria_accessiblename_exists.html).

## Props

<ArgTypes />

### Render function

The `DataTable` component uses a render function passed via the `children` prop
to give you full control over how your table is rendered, while Carbon manages
internal state like sorting, filtering, selection, and expansion.

The function you provide to the `children` prop receives a render props object
with access to:

- [Prop getters](#prop-getters)
- [Actions](#actions)
- [State](#state)

#### Prop getters

These functions must be applied to the corresponding components to enable
built-in functionality like sorting, selection, and expansion.

| Getter                   | Description                                                       |
| ------------------------ | ----------------------------------------------------------------- |
| `getBatchActionProps`    | Returns props for the batch action toolbar.                       |
| `getCellProps`           | Returns props for a cell.                                         |
| `getExpandedRowProps`    | Returns props for an expanded row.                                |
| `getExpandHeaderProps`   | Returns props for the expand-all header cell.                     |
| `getHeaderProps`         | Returns props for a header.                                       |
| `getRowProps`            | Returns props for a row.                                          |
| `getSelectionProps`      | Returns props for row-level or header-level selection checkboxes. |
| `getTableContainerProps` | Returns props for the table container.                            |
| `getTableProps`          | Returns props for the table.                                      |
| `getToolbarProps`        | Returns props for the toolbar.                                    |

For full details on the arguments and returned values of these getters, refer to
the `DataTableRenderProps` type in the
[source code](https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/DataTable/DataTable.tsx).

#### Actions

These functions allow you to programmatically update the table's internal state.

| Action          | Description                               |
| --------------- | ----------------------------------------- |
| `expandAll`     | Toggles expansion for all rows.           |
| `expandRow`     | Toggles expansion for a row.              |
| `onInputChange` | Updates the table's filter/search string. |
| `selectAll`     | Selects or deselects all rows.            |
| `selectRow`     | Toggles selection for a row.              |
| `sortBy`        | Sorts the table by a header key.          |

#### State

These values represent the internal table state, useful for custom logic or
conditional rendering.

| Property       | Description                                            |
| -------------- | ------------------------------------------------------ |
| `headers`      | Headers to render.                                     |
| `radio`        | Whether the table is in single-selection (radio) mode. |
| `rows`         | Rows to render (after filtering and sorting).          |
| `selectedRows` | Selected rows.                                         |

#### Example

```jsx
<DataTable headers={headers} isSortable rows={rows}>
  {({
    getCellProps,
    getHeaderProps,
    getRowProps,
    getSelectionProps,
    getTableProps,
    headers,
    rows,
  }) => (
    <Table {...getTableProps()}>
      <TableHead>
        <TableRow>
          <TableSelectAll {...getSelectionProps()} />
          {headers.map((header) => (
            <TableHeader {...getHeaderProps({ header })}>
              {header.header}
            </TableHeader>
          ))}
        </TableRow>
      </TableHead>
      <TableBody>
        {rows.map((row) => (
          <TableRow {...getRowProps({ row })}>
            <TableSelectRow {...getSelectionProps({ row })} />
            {row.cells.map((cell) => (
              <TableCell {...getCellProps({ cell })}>{cell.value}</TableCell>
            ))}
          </TableRow>
        ))}
      </TableBody>
    </Table>
  )}
</DataTable>
```

## Feedback

Help us improve this component by providing feedback, asking questions on Slack,
or updating this file on
[GitHub](https://github.com/carbon-design-system/carbon/edit/main/packages/react/src/components/DataTable/DataTable.mdx).
