import { Meta, Canvas } from '@storybook/addon-docs/blocks'
import { Warning } from '../../../docs/helpers'
import * as CellStories from './cells.stories.tsx'

<Meta title="pv-grid/Components/Grid/Cells" />

# Cells

How cells are rendered are controlled by the [cell property](/docs/pv-grid-components-grid-columns-columncellconfig--docs) on a [Column definition](/docs/pv-grid-components-grid-columns-column--docs). See those pages for full documentation. This page covers specifically the concepts behind how data is displayed in cells and used for sorting and copy operations.

## Displaying data in cells

Three main properties are used to control what data is displayed in a cell: `value`, `label` and `Renderer`.

- `value` is used **primarily for sorting**, but is also used for editing and in some cases, copy operations. It also becomes the default `label` if no `label` function is provided.
- `label` is used for the **displayed text in the cell**, and is also used for copy operations.
- `Renderer` is used to further customize the rendering of the cell. It can use the `value` or `label`, or it can ignore them entirely and render something else. If no `Renderer` is provided, a default renderer will be used that displays the `label` (which in turn defaults to the `value` if no `label` function is provided).

<Warning>
    **Important**: It is easy to get this wrong, and produce a grid that cannot
    sort correctly because it can't find the value, or a grid that copies blank
    data because all the formatting is handled in the `Renderer`. Please read
    the explanations below carefully to understand how these pieces fit
    together.
</Warning>

<br />
<br />

#### `value`

By default, the grid will treat the columnId as a property key and will return the value from the row object with that property key. Since this column is used for sorting, its best if it returns raw values like Dates, numbers, booleans or strings. (Remember, when dealing with strings, you likely want `sortStrategy` set to `natural` so text sorts properly).

In the following example, no `value` function is provided because the columnId matches the property on the row object:

```tsx
type User = {
    id: string
    name: string
    /* ... */
}

const rows: User[] = [
    { id: '1', name: 'Jane Doe' /* ... */ },
    { id: '2', name: 'John Smith' /* ... */ },
]

const columns: Column<User>[] = [
    {
        id: 'name',
        label: 'Name',
    },
]
```

<Canvas of={CellStories.DefaultValues} />

This does _not_ work automatically for nested properties. For example, if the row object is:

```tsx
type User = {
    id: string
    profile: {
        name: string
        email: string
    }
}
```

You might be tempted to have the id be `profile.name` but that will not work. Instead, the column definition would need to provide a `value` function to return the correct value:

```tsx
const columns: Column<User>[] = [
    {
        id: 'name',
        label: 'Name',
        cell: {
            value({ row }) {
                return row.profile.name
            },
        },
    },
]
```

<Canvas of={CellStories.NestedValues} />

#### `label`

By default, the grid will use the `value` as the `label`. If you want to customize the label, you can provide a `label` function. It can use the value but also has access to all row data. For example, to format a currency value:

```tsx
type Donation = {
    id: string
    donation: number
    donationCurrency: string
}

const columns: Column<Donation>[] = [
    {
        id: 'donation',
        label: 'Donation',
        cell: {
            /* Value here will be `donation` due to the columnId matching the property on the row */
            label({ row, value }) {
                return `${intl.formatNumber(value, {
                    style: 'currency',
                    currency: row.donationCurrency,
                })}`
            },
        },
    },
]
```

<Canvas of={CellStories.DonationLabel} />

#### `Renderer`

The default renderer will display the `label`. If you want to customize the rendering (adding an icon, etc) you can do that here. The `Renderer` has access to the `value`, `label`, but not all row data without using the [useGridRow hook](/docs/pv-grid-hooks-usegridrow--docs).

To get the most consistent experience with the grid, don't use the `Renderer` to handle value formatting of the label. That should happen in the dedicated `label` function, and then use the `Renderer` to add additional UI elements, icons, etc.

You can read more about customizing cell rendering in the [Cell Rendering Guide](/docs/pv-grid-guides-1-cell-rendering--docs).

A complete example for a donation column could look like this. In this contrived example, we add a warning icon with a disclaimer that this data is stale:

```tsx
type Donation = {
    id: string
    donation: number
    donationCurrency: string
}

const columns: Column<Donation>[] = [
    {
        id: 'donation',
        label: 'Donation',
        cell: {
            /* Value here will be `donation` due to the columnId matching the property on the row */
            label({ row, value }) {
                if (!value) {
                    return ''
                }
                return `${intl.formatNumber(value, {
                    style: 'currency',
                    currency: row.donationCurrency,
                })}`
            },
            Renderer({ label, tabIndex }) {
                return (
                    <GridCellDefault
                        icon={<Warning />}
                        leftContentTooltip="This data is current as of 2 months ago."
                        tabIndex={tabIndex}
                        label={label}
                    />
                )
            },
        },
    },
]
```

<Canvas of={CellStories.DonationRenderer} />
