Multiple Sorting
By default, if you don't specify otherwise, the DataGrid is configured with single sorting. For multiple sorting, you need to specify the sorting information as an array:
<DataSource<Developer>
primaryKey="id"
data={data}
// we want an array here
defaultSortInfo={[]}
>
<InfiniteTable<Developer> columns={columns} />
</DataSource>
Note
An empty array means no sorting. However, it does specify that sorting is configured as multiple sorting, so it's useful to set it to []
Try clicking the age
column and then the firstName
column.
If the multi-sort behavior is replace
, clicking the second column will remove the sort from the first column.
In order for the sorting to be additive, even if the behavior is replace
, use the Ctrl
/Cmd
key while clicking the column header.
If the multi-sort behavior is append
, clicking the second column will add it to the sort.
import { InfiniteTable, DataSource, InfiniteTablePropMultiSortBehavior, } from '@infinite-table/infinite-react'; import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react'; import * as React from 'react'; type Developer = { id: number; firstName: string; country: string; city: string; currency: string; email: string; preferredLanguage: string; hobby: string; salary: number; age: number; }; const columns: InfiniteTablePropColumns<Developer> = { firstName: { field: 'firstName', header: 'First Name' }, age: { field: 'age', header: 'Age' }, salary: { field: 'salary', header: 'Salary', type: 'number', }, country: { field: 'country', header: 'Country' }, preferredLanguage: { field: 'preferredLanguage' }, id: { field: 'id' }, hobby: { field: 'hobby' }, city: { field: 'city' }, currency: { field: 'currency' }, }; export default function LocalUncontrolledSingleSortingExample() { const [multiSortBehavior, setMultiSortBehavior] = React.useState< 'append' | 'replace' >('replace'); return ( <> <div style={{ background: 'var(--infinite-background)', color: 'var(--infinite-cell-color)', display: 'flex', flexDirection: 'row', }} > <p style={{ padding: 10 }}>Select the multi-sort behavior</p> <select style={{ margin: '10px 0', display: 'inline-block', background: 'var(--infinite-background)', color: 'var(--infinite-cell-color)', padding: 'var(--infinite-space-3)', }} value={multiSortBehavior} onChange={(event) => { const multiSortBehavior = event.target .value as InfiniteTablePropMultiSortBehavior; setMultiSortBehavior(multiSortBehavior); }} > <option value="replace">replace</option> <option value="append">append</option> </select> </div> <DataSource<Developer> primaryKey="id" data={dataSource} defaultSortInfo={[]} > <InfiniteTable<Developer> columns={columns} columnDefaultWidth={120} multiSortBehavior={multiSortBehavior} /> </DataSource> </> ); } const dataSource: Developer[] = [ { id: 0, firstName: 'Nya', country: 'India', city: 'Unnao', age: 24, currency: 'JPY', preferredLanguage: 'TypeScript', salary: 60000, hobby: 'sports', email: 'Nya44@gmail.com', }, { id: 1, firstName: 'Axel', country: 'Mexico', city: 'Cuitlahuac', age: 46, currency: 'USD', preferredLanguage: 'TypeScript', salary: 100000, hobby: 'sports', email: 'Axel93@hotmail.com', }, { id: 2, firstName: 'Gonzalo', country: 'United Arab Emirates', city: 'Fujairah', age: 24, currency: 'JPY', preferredLanguage: 'Go', salary: 120000, hobby: 'photography', email: 'Gonzalo_McGlynn34@gmail.com', }, { id: 3, firstName: 'Sherwood', country: 'Mexico', city: 'Tlacolula de Matamoros', age: 24, currency: 'CHF', preferredLanguage: 'Rust', salary: 99000, hobby: 'cooking', email: 'Sherwood_McLaughlin65@hotmail.com', }, { id: 4, firstName: 'Alexandre', country: 'France', city: 'Persan', age: 24, currency: 'EUR', preferredLanguage: 'Go', salary: 97000, hobby: 'reading', email: 'Alexandre_Harber@hotmail.com', }, { id: 5, firstName: 'Mariane', country: 'United States', city: 'Hays', age: 23, currency: 'EUR', preferredLanguage: 'TypeScript', salary: 58000, hobby: 'cooking', email: 'Mariane0@hotmail.com', }, { id: 6, firstName: 'Rosalind', country: 'Mexico', city: 'Nuevo Casas Grandes', age: 23, currency: 'AUD', preferredLanguage: 'JavaScript', salary: 198000, hobby: 'dancing', email: 'Rosalind69@gmail.com', }, { id: 7, firstName: 'Lolita', country: 'Sweden', city: 'Delsbo', age: 22, currency: 'JPY', preferredLanguage: 'TypeScript', salary: 200000, hobby: 'cooking', email: 'Lolita.Hayes@hotmail.com', }, { id: 8, firstName: 'Tre', country: 'Germany', city: 'Bad Camberg', age: 23, currency: 'GBP', preferredLanguage: 'TypeScript', salary: 200000, hobby: 'sports', email: 'Tre28@gmail.com', }, { id
User interaction and multi sort behavior
When InfiniteTable
is configured with multiple sorting there are two supported behaviors:
append
- when this behavior is used, clicking a column header adds that column to the alredy existing sort. If the column is already sorted, the sort direction is reversed. In order to remove a column from the sort, the user needs to click the column header in order to toggle sorting from ascending to descending and then to no sorting.replace
- the default behavior - a user clicking a column header removes any existing sorting and sets that column as sorted. In order to add a new column to the sort, the user needs to hold theCtrl/Cmd
key while clicking the column header.
Note
The behavior of multiple sorting is configured via the multiSortBehavior
- the default value for this prop is "replace"
.
❗️❗️❗️ The multiSortBehavior
prop is defined on the InfiniteTable
component, not on the DataSource
component - as it's the InfiniteTable
that handles user interaction, even though the DataSource
does the actual sorting.
Multi sort behavior - append
Scenario 1
- user clicks a column header to sort by that column - an ascending sort is added, and the column header will contain the sort index -
1
- if user clicks the same column, the sort direction is reversed - sort index is preserved as
1
, but descending order is set. - user clicks the same column again - the column is removed from the sort.
Scenario 2
- user clicks a column header to sort by that column - an ascending sort is added, and the column header will contain the sort index -
1
- user clicks another column - the new column is added to the sort, with ascending order and sort index
2
. The initial clicked column is still the sorted, and that sort is applied first. For equal values on column1
, the sort by column2
is applied. - user clicks column
2
again - the sort direction is reversed for the second column. So now the sort order is1
ascending,2
descending. - user clicks column
2
again - the column is removed from the sort. The sorting now only contains the first column, in ascending order.
Multi sort behavior - replace
Note
This is the default behavior for multiple sorting.
In the replace
behavior, clicking a column header will remove any existing sorting and set that specific column as sorted.
In order to add a new column to the sort, the user needs to hold the Ctrl
/Cmd
key while clicking a column header. Holding the Ctrl
/Cmd
key while clicking a column header results in the same behavior as the append
.
Controlled and uncontrolled sorting
As noted above, for multiple sorting, you need to specify an array of objects - see DataSourceSingleSortInfo
for more on the shape of those objects:
// sort by age in descending order, then by `firstName` in ascending order
sortInfo = [
{ field: 'age', type: 'number', dir: -1 },
{ field: 'firstName', dir: 1 },
];
// no sorting
sortInfo = [];
The simplest way to use multiple sorting is via the uncontrolled defaultSortInfo
prop. Specify an empty array as the default value, and multiple sorting will be enabled.
This allows sorting by multiple fields (to which columns are bound) - you can specify however many you want - so when sorting two objects in the DataSource
, the first sortInfo
is used to compare the two, and then, on equal values, the next sortInfo
is used and so on.
Note
If you want to change the sorting from code, after the component is mounted, you need to use the controlled sortInfo
prop.
In this case, make sure you update the sortInfo
prop as a result of user interaction, by using the onSortInfoChange
callback.
This table allows sorting multiple columns - initially the country
column is sorted in descending order and the salary
column is sorted in ascending order. Ctrl
/Cmd
+ click the salary
column to toggle the column sort to descending. Ctrl
/Cmd
clicking it a second time will remove it from the sort altogether.
import { InfiniteTable, DataSource, DataSourceData, } from '@infinite-table/infinite-react'; import type { InfiniteTablePropColumns } 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: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { firstName: { field: 'firstName' }, country: { field: 'country' }, salary: { field: 'salary', type: 'number', }, age: { field: 'age' }, id: { field: 'id' }, canDesign: { field: 'canDesign' }, preferredLanguage: { field: 'preferredLanguage' }, stack: { field: 'stack' }, hobby: { field: 'hobby' }, city: { field: 'city' }, currency: { field: 'currency' }, }; const domProps = { style: { height: '90vh' } }; const shouldReloadData = { sortInfo: false, }; export default function LocalUncontrolledMultiSortingExampleWithRemoteData() { return ( <> <DataSource<Developer> primaryKey="id" data={dataSource} defaultSortInfo={[ { field: 'country', dir: -1 }, { field: 'salary', dir: 1 }, ]} shouldReloadData={shouldReloadData} > <InfiniteTable<Developer> domProps={domProps} columns={columns} columnDefaultWidth={120} /> </DataSource> </>
import { InfiniteTable, DataSource, DataSourceData, } from '@infinite-table/infinite-react'; import type { InfiniteTablePropColumns } 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: DataSourceData<Developer> = ({ sortInfo }) => { if (sortInfo && !Array.isArray(sortInfo)) { sortInfo = [sortInfo]; } const args = [ sortInfo ? 'sortInfo=' + JSON.stringify( sortInfo.map((s) => ({ field: s.field, dir: s.dir, })), ) : null, ] .filter(Boolean) .join('&'); return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?` + args) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { salary: { field: 'salary', type: 'number', }, age: { field: 'age' }, canDesign: { field: 'canDesign' }, country: { field: 'country' }, preferredLanguage: { field: 'preferredLanguage' }, firstName: { field: 'firstName' }, stack: { field: 'stack' }, id: { field: 'id' }, hobby: { field: 'hobby' }, city: { field: 'city' }, currency: { field: 'currency' }, }; const shouldReloadData = { sortInfo: true, }; export default function RemoteUncontrolledMultiSortingExample() { return ( <> <DataSource<Developer> primaryKey="id" data={dataSource} defaultSortInfo={[ { field: 'salary', dir: -1, }, ]} shouldReloadData={shouldReloadData} > <InfiniteTable<Developer> columns={columns} columnDefaultWidth={120} /> </DataSource> </>
Note
If you use uncontrolled sorting via defaultSortInfo
there's no way to switch between single and multiple sorting after the component is mounted. If you have this use-case, you need to use the controlled sortInfo
prop.
Remote Sorting
Sorting remotely makes a lot of sense when using a function as your data
source. Whenever the sort information is changed, the function will be called with all the information needed to retrieve the data from the remote endpoint.
Note
For remote sorting, make sure you specify shouldReloadData.sortInfo=true
- if you don't, the data will also be sorted locally in the browser (which most of the times will be harmless, but it means wasted CPU cycles).
import { InfiniteTable, DataSource, DataSourceData, DataSourcePropSortInfo, } from '@infinite-table/infinite-react'; import type { InfiniteTablePropColumns } 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: DataSourceData<Developer> = ({ sortInfo }) => { if (sortInfo && !Array.isArray(sortInfo)) { sortInfo = [sortInfo]; } const args = [ sortInfo ? 'sortInfo=' + JSON.stringify( sortInfo.map((s) => ({ field: s.field, dir: s.dir, })), ) : null, ] .filter(Boolean) .join('&'); return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers100-sql?` + args) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { preferredLanguage: { field: 'preferredLanguage' }, salary: { field: 'salary', type: 'number', }, age: { field: 'age' }, canDesign: { field: 'canDesign' }, country: { field: 'country' }, firstName: { field: 'firstName' }, stack: { field: 'stack' }, id: { field: 'id' }, hobby: { field: 'hobby' }, city: { field: 'city' }, currency: { field: 'currency' }, }; export default function RemoteControlledMultiSortingExample() { const [sortInfo, setSortInfo] = React.useState< DataSourcePropSortInfo<Developer> >([ { field: 'salary', dir: -1, }, ]); const shouldReloadData = { sortInfo: true, }; return ( <> <DataSource<Developer> primaryKey="id" data={dataSource} sortInfo={sortInfo} shouldReloadData={shouldReloadData} onSortInfoChange={setSortInfo} > <InfiniteTable<Developer> columns={columns} columnDefaultWidth={220} /> </DataSource> </>
In the example above, remote and controlled sorting are combined - because shouldReloadData.sortInfo=true
is specified, the <DataSource />
will call the data
function whenever sorting changes, and will pass in the dataParams
object that contains the sort information.