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

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

# Footer

The grid footer row is used when a flat grid has the aggregation feature enabled. Its purpose is to display aggregated values for each column, when applicable. [Design system specs](https://design.planview.com/components/grid/grid-footer-row)

<Warning>
    Note: This feature is only available on flat grids and will not display in
    tree grids.
</Warning>

Data aggregation in the footer row is configured using the `ColumnFooterConfig` part of the column configuration.

The configuration lets the user configure how to:

- Retrieve value from each row (using the `value` attribute)
- Aggregate collected row data (using the `aggregation` attribute)
- Format the value of the aggregated column in the footer (using the `label` attribute)
- Customize rendering using bespoke components. (using the `Renderer` attribute)

Read more about the [ColumnFooterConfig type definition](/docs/pv-grid-components-grid-columns-columnfooterconfig--docs)

### Simple usage

In the example below the "price" column is configured to have aggregated data displayed in the footer row.
The value of each row is collected implicitly by looking up the attribute, which in this case already is a numeric value. Data is aggregated using the built-in `sum` method to summarize all values and the result of the aggregation is passed through a currency formatter function which returns a string.

```tsx
const rows: Books[] = [
    { id: '1001', title: 'Journey to the Center of the Earth', price: 11.99 },
    {
        id: '1002',
        title: 'Twenty Thousand Leagues Under the Sea',
        price: 35.65,
    },
    { id: '1003', title: 'Around the World In Eighty Days', price: 29.98 },
]

const columns: Column<Books>[] = [
    {
        id: 'price',
        label: 'Price',
        header: {
            align: 'right',
        },
        footer: {
            aggregation: 'sum',
            label: (value) => currencyFormatter.format(value),
        },
        cell: {
            label: ({ value }: { value: number }) =>
                currencyFormatter.format(value),
            Renderer: ({ label, tabIndex }) => (
                <GridCellDefault numeric tabIndex={tabIndex} label={label} />
            ),
        },
    },
]
```

<Canvas of={GridStories.Footer} />

## Value

### Writing your value getter

When configuring the aggregation, by default the grid is going to map the `id` of the column to the attribute of each row to get the value of the row. This is also how a column is sorted if no value getter is provided.

If that attribute is not a numeric value or a date it might be necessary to define a value getter. This is done in the `cell` configuration of the column by adding a value getter to the `value` attribute. This may also be useful if you would like to combine attributes from each row to get the value.

Let's have a look at one example:

```tsx
const rows = [
    {
        id: '1',
        tags: [
            { id: 'tag1', label: 'My tag 1', removable: true },
            { id: 'tag2', label: 'My tag 2', removable: true },
            { id: 'tag3', label: 'My tag 3', removable: false },
        ],
    },
    {
        id: '2',
        tags: [
            { id: 'tag1', label: 'My tag 1', removable: true },
            { id: 'tag4', label: 'My tag 4', removable: false },
            { id: 'tag5', label: 'My tag 5', removable: false },
        ],
    },
]

const columns = [{
    id: 'tags',
    label: 'Tags',
    cell: {
        value: ({ row }) => row.tags.length
    },
    footer: {
        aggregation: 'sum'
    }
}]


<Grid columns={columns} rows={rows} />


```

In the example above, the "Tags" column will be sorted on **the number of tags** in each row and the footer will show the sum of all tags.

But let's say that you would want to show **the amount of removable tags** in the footer, but you do not want to affect sorting.
In this case you would add a overriding value getter in the `footer` configuration.

```tsx

const columns = [{
    id: 'tags',
    label: 'Tags',
    cell: {
        value: ({ row }) => row.tags.length
    },
    footer: {
        value: ({ row }) => row.tags.filter(t => t.removable).length
        aggregation: 'sum'
    }
}]


```

## Aggregation

Aggregation will be done on all values collected by the value getter. If a value returned is `null` it will be ignored and will not affect aggregation.

<Warning>
    Note: Filtering does affect aggregation. The Grid will aggregate the values
    of all _visible_ rows.
</Warning>

### Built-in aggregation

The grid ships with these built-in aggregation methods:

- `sum`: Returns the sum of all values
- `avg`: Returns the non-rounded average of all values
- `min`: Returns the smallest value
- `max`: Returns the largest value
- `size`: Returns the number of values

Here is an example of using a built-in aggregation function showing the average amount of tags per row:

```tsx
{
    id: 'tags',
    cell: {
        value: ({row}) => row.tags.length,
    },
    footer: {
        aggregation: 'avg',
    },
}
```

### Customize aggregation

The `aggregation` property of the `ColumnFooterConfig` is a union-type between the built-in aggregation methods (strings) and a function. This function takes one argument (an array of values collected from each row) that must return a `number`, `Date` or `null`

```tsx
{
    id: 'tags',
    footer: {
        aggregation: (values) => Number(values.join('')),
    },
}
```

## Rendering

### Customize formatting

In most cases at least some formatting of the aggregated data will be necessary. You might want to add the currency and proper formatting of the aggregated values collected from the rows.
This is done by using the `label` property. Label is a function that is passed the value of the aggregated data and must return a string.

```tsx
{
    id: 'price',
    header: {
        align: 'right',
    },
    footer: {
        aggregation: 'sum',
        label: (value) =>
            typeof value === 'number'
                ? currencyFormatter.format(value)
                : '',
    },
}


```

Note in the example above; `price` is a numeric value which we want to display right-aligned. **The footer default cell renderer will infer alignment from the header configuration.**

In cases where you do not wish to render aggregated data in the footer, there is the option to simply provide a `label` without providing a `aggregation` property in which case no data-aggregation is done and the string is simply rendered in the footer cell.

```tsx

{
    id: 'tags',
    footer: {
        label: () =>  'I like my tags'
    },
}

```

### Custom Renderer

Similar to the column `cell` and `header`, the `footer` -cell can also be fully customized using the `Renderer` attribute.

```tsx

{
    id: 'tags',
    footer: {
        aggregation: 'sum',
        Renderer({label, columnId}) {
            return <MyCustomComponent>{label}</MyCustomComponent>
        }
    }
}

```
