Extending existing filters

By default InfiniteTable has the following default filter types:

  • string
  • number

and each of them has a collection of operators that are supported - see filterTypes for the respective list of supported operators.

You may find those operators limiting - but it's easy to extend them and add new operators or even new filter types.

Adding new operators to existing filter types

You can import defaultFilterTypes from the root of the package.

Adding a new operator to the string filter type
import { defaultFilterTypes } from '@infinite-table/infinite-react';

// add new operators for the `string` filter type
defaultFilterTypes.string.operators.push({
  name: 'notContains',
  component: { Icon: ReactComponentForIcon }
  label: 'Not Contains',
  fn: ({currentValue, filterValue }) => {
    return typeof currentValue === 'string' &&
            typeof filterValue == 'string' &&
            !currentValue.toLowerCase().includes(filterValue.toLowerCase())
  }
})

Note

When you import the named defaultFilterTypes value and extend it, that will affect all InfiniteTable components in your application.

If you don't want that, you need to use the filterTypes prop of the <DataSource /> component. Either build an entirely new object for filterTypes, or start by cloning defaultFilterTypes and extend it.

Enhanced string filter type - new 'Not includes' operator

The string columns have a new Not includes operator.

View Mode
Fork
import * as React from 'react';

import {
  DataSourceData,
  InfiniteTable,
  InfiniteTablePropColumns,
  DataSource,
  defaultFilterTypes,
} from '@infinite-table/infinite-react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;

  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';

  salary: number;
};

defaultFilterTypes.string.operators.push({
  name: 'Not contains',
  label: 'Not Contains',
  fn: ({ currentValue, filterValue, emptyValues }) => {
    if (
      emptyValues.includes(currentValue) ||
      emptyValues.includes(filterValue)
    ) {
      return true;
    }
    return (
      typeof currentValue === 'string' &&
      typeof filterValue == 'string' &&
      !currentValue.toLowerCase().includes(filterValue.toLowerCase())
    );
  },
});

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,
  },

  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },

  salary: {
    field: 'salary',
    type: 'number',
  },
  currency: { field: 'currency', defaultFilterable: false },
};

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>
    </>

Adding new filter types

If the existing filter types are not enough, it's easy to add new ones.

As already mentioned, you can either update the value of defaultFilterTypes or use the filterTypes prop of the <DataSource /> component. Updating the value defaultFilterTypes will affect all your InfiniteTable DataGrid components.

Adding a new filter type by updating defaultFilterTypes
import { defaultFilterTypes } from '@infinite-table/infinite-react';

defaultFilterTypes.bool = {
  defaultOperator: 'eq',
  emptyValues: [null],
  operators: [
    {
      name: 'eq',
      label: 'Equals',
      fn: ({ currentValue, filterValue }) => currentValue === filterValue,
    },
  ],
};
Adding a new filter type by using the filterTypes prop
import { DataSource } from '@infinite-table/infinite-react';

<DataSource
  filterTypes={{
    bool: {
      defaultOperator: 'eq',
      emptyValues: [null],
      operators: [
        {
          name: 'eq',
          label: 'Equals',
          fn: ({ currentValue, filterValue }) => currentValue === filterValue,
        },
      ],
    },
  }}
/>;

Note

When passing filterTypes to the <DataSource /> component, the object will be merged with the defaultFilterTypes. As a result, the existing string and number filterTypes will be preserved, unless explicitly overridden.

Writing a `bool` filter type with a custom filter editor

The canDesign column is using a custom bool filter type with a custom filter editor.

View Mode
Fork
import * as React from 'react';

import {
  InfiniteTable,
  InfiniteTablePropColumns,
  DataSource,
  components,
  useInfiniteColumnFilterEditor,
} from '@infinite-table/infinite-react';

const { CheckBox } = components;

type Developer = {
  id: number;
  firstName: string;
  canDesign: boolean;
  stack: string;
  hobby: string;
};
const dataSource: Developer[] = [
  {
    id: 1,
    firstName: 'John',

    canDesign: true,
    stack: 'frontend',
    hobby: 'gaming',
  },
  {
    id: 2,
    firstName: 'Jane',

    canDesign: false,
    stack: 'backend',
    hobby: 'reading',
  },
  {
    id: 3,
    firstName: 'Jack',

    canDesign: true,
    stack: 'frontend',
    hobby: 'gaming',
  },
  {
    id: 4,
    firstName: 'Jill',

    canDesign: false,
    stack: 'backend',
    hobby: 'reading',
  },
  {
    id: 5,
    firstName: 'Seb',

    canDesign: false,
    stack: 'backend',
    hobby: 'reading',
  },
];

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    type: 'number',
    defaultWidth: 100,
  },
  canDesign: {
    field: 'canDesign',
    filterType: 'bool',
    renderValue: ({ value }) => (value ? 'Yes' : 'No'),
  },
  firstName: {
    field: 'firstName',
  },
  stack: { field: 'stack' },
};

const domProps = {
  style: {
    height: '100%',
  },
};

function BoolFilterEditor() {
  const { value, setValue, className } =
    useInfiniteColumnFilterEditor<Developer>();

  return (
    <div className={className} style={{ textAlign: 'center' }}>
      <CheckBox
        checked={value}
        onChange={(newValue) => {
          if (value === true) {
            // after the value was true, make it go to indeterminate state
            newValue = null;
          }
          if (value === null) {
            // from indeterminate, goto false
            newValue = false;
          }
          setValue(newValue);
        }}
      />
    </div>
  );
}

export default () => {
  return (
    <>
      <React.StrictMode>
        <DataSource<Developer>
          data={dataSource}
          primaryKey="id"
          defaultFilterValue={[]}
          filterDelay={0}
          filterTypes={{
            bool: {
              defaultOperator: 'eq',
              emptyValues: [null],
              components: {
                FilterEditor: BoolFilterEditor,
                FilterOperatorSwitch: () => null,
              },
              operators: [
                {
                  name: 'eq',
                  label: 'Equals',
                  fn: ({ currentValue, filterValue }) =>
                    currentValue === filterValue,
                },
              ],
            },
          }}
        >
          <InfiniteTable<Developer>
            domProps={domProps}
            columnDefaultWidth={150}
            columnMinWidth={50}
            columns={columns}
          />
        </DataSource>
      </React.StrictMode>
    </>