Customizing Pivot Columns

View as Markdown
There are a number of ways to customize the generated pivot columns and we'll cover each of them in this page. For a longer walkthrough with live demos, see customizing generated pivot columns.
You can pass a column object (or a function that returns one) in three places. Choose the one that matches the scope you want:
  1. pivotColumn on <InfiniteTable /> — every generated pivot column
  2. pivotBy.column — all columns in the group generated for that pivot field
  3. aggregationReducers.pivotColumn — only columns generated for that aggregation
Those objects have the same shape as a normal column, so you can set header, size, style, defaultSortable, and so on.

Global: the pivotColumn prop

pivotColumn is applied to every generated pivot column (leaf values and totals). Use it to override inherited column properties(sortability, width, headers, etc).
<InfiniteTable
  pivotColumn={{
    defaultSortable: true,
    defaultWidth: 180,
  }}
/>
You can also pass a function when the config should depend on the generated column — for example to make leaf columns sortable but keep totals unsortable:
<InfiniteTable
  pivotColumn={({ column }) => ({
    defaultSortable: !column.pivotTotalColumn,
  })}
/>

Per pivot field: pivotBy.column

pivotBy.column applies to all pivot columns in the column group generated for that field. Use it when only one pivot level should be customized — for example, make every canDesign column sortable, without touching the country group.
const pivotBy: DataSourcePivotBy<Developer>[] = [
  { field: 'country' },
  {
    field: 'canDesign',
    column: {
      defaultSortable: true,
      defaultWidth: 400,
    },
  },
];
A function receives the generated column, so you can vary the header (or any other property) per pivot value:
const pivotBy: DataSourcePivotBy<Developer>[] = [
  { field: 'country' },
  {
    field: 'canDesign',
    column: ({ column }) => ({
      header: column.pivotGroupKey === 'yes' ? 'Designer' : 'Not a Designer',
    }),
  },
];

Per aggregation: aggregationReducers.pivotColumn

Each reducer can define a pivotColumn. That config is applied only to columns generated for that aggregation — so you can make salary columns sortable and leave a count aggregation as it is.
const aggregationReducers: DataSourceProps<Developer>['aggregationReducers'] = {
  salary: {
    field: 'salary',
    initialValue: 0,
    reducer: (acc, sum) => acc + sum,
    pivotColumn: {
      defaultSortable: true,
    },
  },
  license: {
    field: 'license',
    initialValue: 0,
    reducer: (acc) => acc + 1,
  },
};
aggregationReducers.pivotColumn on a reducer can also choose which original column to inherit from, or opt out of inheritance entirely — see below.

Inheriting from initial columns#

Pivoting is all about aggregations, so you need to specify the reducers that will aggregate your data. Each reducer can have a field property that specifies the field that will be used for aggregation.
If the table columns collection already has a column bound to the field used in the aggregation, the column configuration will be inherited by the generated pivot column. Override that with aggregationReducers.pivotColumn.inheritFromColumn — pass another column id to inherit from that column instead, or false to inherit from none. See the aggregationReducers.pivotColumn reference for the full column config you can set on the reducer.
const columns: InfiniteTablePropColumns<Developer> = {
  preferredLanguage: {
    field: 'preferredLanguage',
    style: { color: 'blue' },
  },
  age: {
    field: 'age',
    style: {
      color: 'magenta',
      background: 'yellow',
    },
  },
  salary: {
    field: 'salary',
    type: 'number',
    style: {
      color: 'red',
    },
  },
  canDesign: { field: 'canDesign' },
  country: { field: 'country' },
  firstName: { field: 'firstName' },
  id: { field: 'id' },
};

const avgReducer: InfiniteTableColumnAggregator<Developer, any> = {
  initialValue: 0,
  reducer: (acc, sum) => acc + sum,
  done: (sum, arr) => {
    return Math.floor(arr.length ? sum / arr.length : 0);
  },
};

const aggregationReducers: DataSourceProps<Developer>['aggregationReducers'] = {
  // will have the same configuration as the `salary` column
  avgSalary: { field: 'salary', ...avgReducer },
  avgAge: {
    field: 'age',
    ...avgReducer,
    pivotColumn: {
      // will have the same configuration as the `preferredLanguage` column
      inheritFromColumn: 'preferredLanguage',
      // but specify a custom default width
      defaultWidth: 500,
    },
  },
};
View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  GroupRowsState,
} from '@infinite-table/infinite-react';
import type {
  DataSourcePropAggregationReducers,
  InfiniteTableColumnAggregator,
  InfiniteTablePropColumns,
  DataSourceGroupBy,
  DataSourcePivotBy,
} from '@infinite-table/infinite-react';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch('https://data.infinite-table.com' + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const avgReducer: InfiniteTableColumnAggregator<Developer, any> = {
  initialValue: 0,
  reducer: (acc, sum) => acc + sum,
  done: (sum, arr) => (arr.length ? sum / arr.length : 0),
};

const columnAggregations: DataSourcePropAggregationReducers<Developer> = {
  avgSalary: {
    field: 'salary',
    name: 'Average salary',
    ...avgReducer,
  },
  avgAge: {
    field: 'age',
    ...avgReducer,
    pivotColumn: {
      defaultWidth: 500,
      inheritFromColumn: 'firstName',
    },
  },
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id' },
  firstName: {
    field: 'firstName',
    style: {
      fontWeight: 'bold',
    },
    renderValue: ({ value }) => <>{value}!</>,
  },
  preferredLanguage: { field: 'preferredLanguage' },
  stack: { field: 'stack' },
  country: { field: 'country' },
  canDesign: { field: 'canDesign' },
  hobby: { field: 'hobby' },

  city: { field: 'city' },
  age: { field: 'age' },
  salary: {
    field: 'salary',
    type: 'number',
    header: 'Salary',
    style: { color: 'red' },
  },
  currency: { field: 'currency' },
};

const groupRowsState = new GroupRowsState({
  expandedRows: [],
  collapsedRows: true,
});

export default function PivotByExample() {
  const groupBy: DataSourceGroupBy<Developer>[] = React.useMemo(
    () => [
      {
        field: 'preferredLanguage',
      },
      { field: 'stack' },
    ],
    [],
  );

  const pivotBy: DataSourcePivotBy<Developer>[] = React.useMemo(
    () => [
      { field: 'country' },
      {
        field: 'canDesign',
      },
    ],
    [],
  );

  return (
    <>
      <DataSource<Developer>
        primaryKey="id"
        data={dataSource}
        groupBy={groupBy}
        pivotBy={pivotBy}
        aggregationReducers={columnAggregations}
        defaultGroupRowsState={groupRowsState}
      >
        {({ pivotColumns, pivotColumnGroups }) => {
          return (
            <InfiniteTable<Developer>
              debugId="pivot-column-inherit-example"
              columns={columns}
              hideEmptyGroupColumns
              pivotColumns={pivotColumns}
              pivotColumnGroups={pivotColumnGroups}
              columnDefaultWidth={180}
            />
          );
        }}
      </DataSource>
    </>
  );
}