Filtering
Filtering allows you to limit the rows available in the table.
Both client-side and server-side filtering are available in Infinite Table - but the way the are configured is pretty similar, so this page documents the common parts, while pointing to the respective pages for the differences.
Configuring Filters for Columns
The most common way to use filtering in Infinite Table is by configuring filters for columns (this works both for client-side and server-side filtering).
You specify an uncontrolled defaultFilterValue
on the <DataSource />
component (or the controlled version, filterValue
) and the specified value will be used as the initial filter.
Based on the column type, the correct filter editor is displayed in the column header, along with the correct operator icon. In the UI, you can change the operator being used for the filter.
<DataSource<Developer>
data={...}
defaultFilterValue={[
{
field: 'age',
filter: {
operator: 'gt',
value: 30,
type: 'number'
}
}
]}
>
<InfiniteTable<Developer>
columns={...}
/>
</DataSource>
Note
If you don't need to specify some initial filters, but want the column filter bar to be visible, you need to specify defaultFilterValue = []
(or the controlled filterValue = []
).
Specifying any of those props will make the column filter bar visible.
Whenever filters change, onFilterChange
will be called with the new filter value - note however, it might not be called immediately, due to the filterDelay
prop.
The above snippet will show a number
filter for the age
column. There are two filter types available at this stage in Infinite Table:
string
- with the following operators available:contains
,eq
,startsWith
andendsWith
number
- with the following operators available:eq
,neq
,gt
,gte
,lt
andlte
Defining Filterable Columns
By default, all columns are filterable.
If you want to make columns by default not filterable, use the columnDefaultFilterable
prop and set it to false
.
You can specifically configure each column by using the defaultFilterable
property - this overrides the global columnDefaultFilterable
prop.
Defining a Filter Type for a Column
Besides being filterable, a column can decide what type of filter it will display.
Use the columns.type
property to specify the type of filter the column will use. Using the type
property also configures the data type of the column, which in turn determines the sort type.
Note
If the type of filter you want to show does not match the column type
, you can specify the filter with the column.filterType
property. Only use this when the type of the data differs from the type of the filter (eg: you have a numeric column, with a custom filter type).
Understanding Filter Types
A filter type is a concept that defines how a certain type of data is to be filtered. A filter type will have
- a
key
- the key used to define the filter in thefilterTypes
object - a
label
, - an array of values considered to be empty values - when any of these values is used in the filter, the filter will not be applied.
- an array of
operators
- a default operator.
Let's imagine you have a DataSource
with developers, each with a salary
column, and for that column you want to allow >
, >=
, <
and <=
comparisons (operators).
For this, you would define the following filter type:
const filterTypes = {
income: {
label: 'Income',
emptyValues: ['', null, undefined],
defaultOperator: 'gt',
operators: [
{
name: 'gt',
label: 'Greater than',
fn: ({ currentValue, filterValue, emptyValues }) => {
if (emptyValues.has(currentValue)) {
return true;
}
return currentValue > filterValue;
},
},
{
name: 'gte',
//...
},
{
name: 'lt',
//...
},
{
name: 'lte',
//...
},
],
},
};
Note
Each operator for a certain filter type needs to at least have a name
and fn
defined. The fn
property is a function that will be called when client-side filtering is enabled, with an object that has the following properties:
currentValue
- the cell value of the current row for the column being filteredfilterValue
- the value of the filter editoremptyValues
- the array of values considered to be empty values for the filter typedata
- the current row data object -typeof DATA_TYPE
index
- the index of the current row in the table -number
dataArray
- the array of all rows originally in the table -typeof DATA_TYPE[]
field?
- the field the current column is bound to (can be undefined if the column is not bound to a field)
The salary
column has a custom filter type, with the following operators: gt
, gte
, lt
and lte
.
import * as React from 'react'; import { DataSourceData, DataSource, InfiniteTable, InfiniteTablePropColumns, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 100, }, salary: { defaultFilterable: true, field: 'salary', type: 'number', filterType: 'salary', }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, currency: { field: 'currency', defaultFilterable: false }, }; function getIcon(icon: string) { return () => ( <div style={{ width: 20, display: 'flex', justifyContent: 'center', flexFlow: 'row', }} > {icon} </div> ); } const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" defaultFilterValue={[]} filterDelay={0} filterMode="local" filterTypes={{ salary: { defaultOperator: 'gt', emptyValues: ['', null, undefined], operators: [ { name: 'gt', label: 'Greater Than', components: { Icon: getIcon('>'), }, fn: ({ currentValue, filterValue }) => { return currentValue > filterValue; }, }, { name: 'gte', components: { Icon: getIcon('>='), }, label: 'Greater Than or Equal', fn: ({ currentValue, filterValue }) => { return currentValue >= filterValue; }, }, { name: 'lt', components: { Icon: getIcon('<'), }, label: 'Less Than', fn: ({ currentValue, filterValue }) => { return currentValue < filterValue; }, }, { name: 'lte', components: { Icon: getIcon('<='), }, label: 'Less Than or Equal', fn: ({ currentValue, filterValue }) => { return currentValue <= filterValue; }, }, ], }, }} > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>
Specifying the filter mode
As already mentioned, filtering can happen either client-side or server-side. If the DataSource data
property is a function (and not an array or a Promise
), then the filtering will happen server-side by default.
However, you can explicitly specify where the filtering should happen by setting the filterMode
property on the <DataSource />
component - possible values are
filterMode="local"
- filtering will happen client-sidefilterMode="remote"
- filtering will happen remotely and thefilterValue
will be passed as a property to the parameter object sent to thedata
function.
Filter mode ⚠️
Explicitly specify filterMode
as either "local"
or "remote"
if you want to change the default behavior.
Filtering Columns Not Bound to a Field
If a column is not bound to a field
, it can still be used for filtering, even client-side filtering, if it is configured with a columns.valueGetter
.
Note
If you don't need a default filter value, the filterValue
that's set when the user interacts with the column filter will use the column valueGetter
to filter values.
If however, you need initial filtering by that column, the filterValue
needs to specify a valueGetter
itself.
defaultFilterValue={[
{
id: 'salary',
valueGetter: ({ data }) => data.salary,
filter: {
operator: 'gt',
value: '',
type: 'number',
}
},
]}
The salary
column is not bound to a field
- however, it can still be used for filtering, as it's configured with a valueGetter
.
import * as React from 'react'; import { DataSourceData, InfiniteTable, InfiniteTablePropColumns, DataSource, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 70, defaultFilterable: false, }, salary: { // we're intentionally using not binding this column to a `field` type: 'number', header: 'Salary', valueGetter: ({ data }) => data.salary, }, firstName: { field: 'firstName', }, stack: { field: 'stack' }, currency: { field: 'currency' }, }; const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" defaultFilterValue={[ // if you want the salary column to be filtered by default // you need to pass a valueGetter to the filter value // if you don't need a default filter, when you start filtering by // the column, the filter value will use the valueGetter of the column { id: 'salary', valueGetter: ({ data }) => data.salary, filter: { operator: 'gt', value: '', type: 'number', }, }, ]} filterDelay={0} filterMode="local" > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>
Customizing the Filter Icon for Columns
Columns can customize the filter icon by using the columns.renderFilterIcon
property.
The salary
column will show a bolded label when filtered.
The firstName
column will show a custom filter icon when filtered.
import * as React from 'react'; import { DataSourceData, InfiniteTable, InfiniteTablePropColumns, DataSource, } from '@infinite-table/infinite-react'; type Developer = { id: number; firstName: string; lastName: string; currency: string; preferredLanguage: string; stack: string; canDesign: 'yes' | 'no'; salary: number; }; const data: DataSourceData<Developer> = () => { return fetch('https://infinite-table.com/.netlify/functions/json-server' + `/developers1k-sql?`) .then((r) => r.json()) .then((data: Developer[]) => data); }; const columns: InfiniteTablePropColumns<Developer> = { id: { field: 'id', type: 'number', defaultWidth: 100, }, salary: { field: 'salary', type: 'number', header: ({ filtered }) => { return filtered ? <b>Salary</b> : 'Salary'; }, renderFilterIcon: () => { return null; }, }, firstName: { field: 'firstName', renderFilterIcon: ({ filtered }) => { return filtered ? '🔥' : ''; }, }, stack: { field: 'stack' }, currency: { field: 'currency' }, }; const domProps = { style: { height: '100%', }, }; export default () => { return ( <> <React.StrictMode> <DataSource<Developer> data={data} primaryKey="id" defaultFilterValue={[]} filterDelay={0} filterMode="local" > <InfiniteTable<Developer> domProps={domProps} columnDefaultWidth={150} columnMinWidth={50} columns={columns} /> </DataSource> </React.StrictMode> </>
Client-side filtering
Learn how to use filtering in the browser.
Server-side filtering
Figure out how to use filtering with server-side integration.