# Table

A component for displaying large amounts of data in rows and columns.

Tabs: [Guidelines](/components/web/table/table.guidelines.md) · [Code](/components/web/table/table.code.md)

## Basic usage

`Table` is a set of building blocks that render a native `<table>`. Import the `Table` namespace and compose its parts: `Table.Table`, `Table.Head`, `Table.HeaderRow`, `Table.HeaderCell`, `Table.Body`, `Table.Row` and `Table.Cell`.

`caption` is required and describes the table content. It isn't displayed. Every `Table.HeaderCell` needs a `scope` so screen readers can tie it to its data cells.

```tsx
import {Table} from '@pleo-io/telescope'

const Heroes = () => (
    <Table.Table caption="Super heroes and their real names">
        <Table.Head>
            <Table.HeaderRow>
                <Table.HeaderCell scope="col">Super hero</Table.HeaderCell>
                <Table.HeaderCell scope="col">Real name</Table.HeaderCell>
            </Table.HeaderRow>
        </Table.Head>
        <Table.Body>
            <Table.Row>
                <Table.Cell>Iron Man</Table.Cell>
                <Table.Cell>Tony Stark</Table.Cell>
            </Table.Row>
            <Table.Row>
                <Table.Cell>Batman</Table.Cell>
                <Table.Cell>Bruce Wayne</Table.Cell>
            </Table.Row>
        </Table.Body>
    </Table.Table>
)
```

## Alignment

Right-align numeric columns with `$textAlign` on both the header and the data cells.

```tsx
<Table.HeaderCell scope="col" $textAlign="right">
    Amount
</Table.HeaderCell>
...
<Table.Cell $textAlign="right">1,000.00</Table.Cell>
```

## Clickable and active rows

`$isClickable` adds a hover state and pointer cursor. Make the row keyboard reachable with `tabIndex={0}` and handle both click and `Enter`. `$isActive` outlines the row, for example to mark the row whose details are open in a drawer.

```tsx
<Table.Row
    $isClickable
    $isActive={selectedId === expense.id}
    tabIndex={0}
    onClick={() => open(expense.id)}
    onKeyDown={(event) => event.key === 'Enter' && open(expense.id)}
>
    <Table.Cell>{expense.merchant}</Table.Cell>
</Table.Row>
```

## Cell content with subtext

Use `Table.CellContent` to show a quieter second line, e.g. an email under a name. Both lines truncate rather than wrap, so rows keep the same height with or without subtext.

```tsx
<Table.Cell>
    <Table.CellContent subtext="tony@stark.com">Tony Stark</Table.CellContent>
</Table.Cell>
```

## Links and actions in cells

`Table.CellLink` and `Table.CellAction` are styled for use inside a cell.

```tsx
<Table.Cell>
    <Table.CellLink href={`/people/${person.id}`}>{person.name}</Table.CellLink>
</Table.Cell>
<Table.Cell>
    <Table.CellAction onClick={() => edit(person.id)}>Edit</Table.CellAction>
</Table.Cell>
```

## Group summary rows

`Table.GroupSummaryRow` renders a summary row, e.g. totals for a group. `$isHighlighted` gives it a louder background.

```tsx
<Table.GroupSummaryRow $isHighlighted>
    <Table.Cell>3 expenses</Table.Cell>
    <Table.Cell $textAlign="right">1,200.00</Table.Cell>
</Table.GroupSummaryRow>
```

## Scrolling and sticky header

Wrap the table in `Table.ScrollableWrapper` to scroll it within a constrained area. Pass `$stickyHeader` to keep the header visible while the body scrolls.

```tsx
<Table.ScrollableWrapper $stickyHeader style={{height: 400}}>
    <Table.Table caption="Expenses">...</Table.Table>
</Table.ScrollableWrapper>
```

## Virtualization

When only a window of rows is rendered, pass the data index of the first rendered row as `virtualStartIndex` so the zebra striping doesn't flicker while scrolling.

```tsx
<Table.Table caption="Expenses" virtualStartIndex={virtualRows[0]?.index ?? 0}>
    ...
</Table.Table>
```

## API reference

### Table.Table

#### `caption` (required)

Type: `string`

Default: -

Description of the table content. Required for accessibility, but not displayed.

#### `virtualStartIndex`

Type: `number | undefined`

Default: `0`

When the table is virtualized, the data index of the first rendered row. Keeps the zebra striping stable while scrolling.

### Table.HeaderCell

#### `$textAlign`

Type: `"center" | "left" | "right" | undefined`

Default: `'left'`

Horizontal alignment of the cell content

#### `$withBorder`

Type: `boolean`

Default: -

Adds a border on the right side of the cell

#### `scope` (required)

Type: `"col" | "colgroup" | "row" | "rowgroup"`

Default: -

The cells the header applies to. Required so screen readers can associate it with its data.

### Table.Row

#### `$isActive`

Type: `boolean`

Default: -

Outlines the row, e.g. to mark the row whose details are open

#### `$isClickable`

Type: `boolean`

Default: -

Adds a hover background and pointer cursor. Pair with `tabIndex={0}` and a click handler.

### Table.Cell

#### `$textAlign`

Type: `"center" | "left" | "right" | undefined`

Default: `'left'`

Horizontal alignment of the cell content

#### `$withBorder`

Type: `boolean`

Default: -

Adds a border on the right side of the cell

### Table.CellContent

#### `subtext`

Type: `ReactNode`

##### Allowed values

- `null`
- `string`
- `number`
- `bigint`
- `false`
- `true`
- `ReactElement<unknown, string | JSXElementConstructor<any>>`
- `Iterable<ReactNode>`
- `ReactPortal`
- `Promise<AwaitedReactNode>`

Default: -

Quieter second line under the main content, e.g. an email under a person's name

### Table.GroupSummaryRow

#### `$isHighlighted`

Type: `boolean`

Default: -

Gives the row a louder background, overriding the zebra striping

### Table.ScrollableWrapper

#### `$stickyHeader`

Type: `boolean`

Default: -

Keeps the table header visible while the body scrolls
