<StackedFilters>
This Enterprise Edition component provides an alternative filter UI for <List>
pages. It introduces the concept of operators to allow richer filtering.
Usage
Use a <StackedFilters>
component in the <List actions>
element. Define the filters using the config
prop, which must contain a filtering configuration.
// in src/posts/PostList.tsx
import {
BooleanField,
Datagrid,
List,
NumberField,
ReferenceArrayField,
TextField,
} from 'react-admin';
import { PostListToolbar } from './PostListToolbar';
const PostList = () => (
<List actions={<PostListToolbar />}>
<Datagrid>
<TextField source="title" />
<NumberField source="views" />
<ReferenceArrayField tags="tags" source="tag_ids" />
<BooleanField source="published" />
</Datagrid>
</List>
);
// in src/posts/PostListToolbar.tsx
import { CreateButton, TopToolbar } from 'react-admin';
import {
StackedFilters,
FiltersConfig,
textFilter,
numberFilter,
referenceFilter,
booleanFilter,
} from '@react-admin/ra-form-layout';
const postListFilters: FiltersConfig = {
title: textFilter(),
views: numberFilter(),
tag_ids: referenceFilter({ reference: 'tags' }),
published: booleanFilter(),
};
export const PostListToolbar = () => (
<TopToolbar>
<CreateButton />
<StackedFilters config={postListFilters} />
</TopToolbar>
);
You must also update your data provider to support filters with operators. See the data provider configuration section below.
Filters Configuration
Define the filter configuration in the <StackedFilters config>
prop. The value must be an object defining the operators and UI for each field that can be used as a filter.
It looks like this:
import { FiltersConfig, StackedFilters, textFilter } from '@react-admin/ra-form-layout';
import { NumberInput } from 'react-admin';
import { MyNumberRangeInput } from './MyNumberRangeInput';
const postListFilters: FiltersConfig = {
title: textFilter(),
views: {
operators: [
{ value: 'eq', label: 'Equals' },
{ value: 'neq', label: 'Not Equals' },
{
value: 'between',
label: 'Between',
input: ({ source }) => <MyNumberRangeInput source={source} />,
},
],
input: ({ source }) => <NumberInput source={source} />,
},
};
export const PostListToolbar = () => (
<TopToolbar>
// ...
<StackedFilters config={postListFilters} />
</TopToolbar>
);
For a given field, the filter configuration should be an object containing an array of operators
and a default input
, used for operators that don’t define their own. You can use the filter configuration builders (like textFilter
) to build filter configuration objects.
An operator is an object that has a label
and a value
.
- The
label
is a string, and can be a translation key. - The
value
is used as a suffix to thesource
and passed to the list filters.
For instance, if the user adds the views
filter with the eq
operator and a value of 0
, the dataProvider.getList()
will receive the following filter
parameter:
{ views_eq: 0 }
Filter Configuration Builders
To make it easier to create a filter configuration, ra-form-layout
provides some helper functions. Each of them has predefined operators and inputs. They accept an array of operators if you want to remove some of them.
textFilter
: A filter for text fields. Defines the following operator:eq
,neq
andq
.numberFilter
: A filter for number fields. Defines the following operator:eq
,neq
,lt
andgt
.dateFilter
: A filter for date fields. Defines the following operator:eq
,neq
,lt
andgt
.booleanFilter
: A filter for boolean fields. Defines the following operator:eq
.choicesFilter
: A filter for fields that accept a value from a list of choices. Defines the following operator:eq
,neq
,eq_any
andneq_any
.choicesArrayFilter
: A filter for array fields. Defines the following operator:inc
,inc_any
andninc_any
.referenceFilter
: A filter for reference fields. Defines the following operator:eq
,neq
,eq_any
andneq_any
.
Build your filter configuration by calling the helpers for each source:
import {
FiltersConfig,
textFilter,
numberFilter,
referenceFilter,
booleanFilter,
} from '@react-admin/ra-form-layout';
const postListFilters: FiltersConfig = {
title: textFilter(),
views: numberFilter(),
tag_ids: referenceFilter({ reference: 'tags' }),
published: booleanFilter(),
};
Data Provider Configuration
In react-admin, dataProvider.getList()
accepts a filter
parameter to filter the records. There is no notion of operators in this parameter, as the expected format is an object like { field: value }
. As StackedFilters
needs operators, it uses a convention to concatenate the field name and the operator with an underscore.
For instance, if the Post resource has a title
field, and you configure <StackedFilters>
to allow filtering on this field as a text field, the dataProvider.getList()
may receive the following filter
parameter:
- title_eq
- title_neq
- title_q
The actual suffixes depend on the type of filter configured in <StackedFilter>
(see filters configuration builders above). Here is an typical call to dataProvider.getList()
with a posts list using <StackedFilters>
:
const { data } = useGetList('posts', {
filter: {
title_q: 'lorem',
date_gte: '2021-01-01',
views_eq: 0,
tags_inc_any: [1, 2],
},
pagination: { page: 1, perPage: 10 },
sort: { field: 'title', order: 'ASC' },
});
It’s up to your data provider to convert the filter
parameter into a query that your API understands.
For instance, if your API expects filters as an array of criteria objects ([{ field, operator, value }]
), dataProvider.getList()
should convert the filter
parameter as follows:
const dataProvider = {
// ...
getList: async (resource, params) => {
const { filter } = params;
const filterFields = Object.keys(filter);
const criteria = [];
// eq operator
filterFields.filter(field => field.endsWith('_eq')).forEach(field => {
criteria.push({ field: field.replace('_eq', ''), operator: 'eq', value: filter[field] });
});
// neq operator
filterFields.filter(field => field.endsWith('_neq')).forEach(field => {
criteria.push({ field: field.replace('_neq', ''), operator: 'neq', value: filter[field] });
});
// q operator
filterFields.filter(field => field.endsWith('_q')).forEach(field => {
criteria.push({ field: field.replace('_q', ''), operator: 'q', value: filter[field] });
});
// ...
},
}
Few of the existing data providers implement this convention. this means you’ll probably have to adapt your data provider to support the operators used by <StackedFilters>
.
Props
Prop | Required | Type | Default | Description |
---|---|---|---|---|
config |
Required | object | - | The stacked filters configuration |
BadgeProps |
Optional | object | - | Additional props to pass to the MUI Badge element |
ButtonProps |
Optional | object | - | Additional props to pass to the Button element |
className |
Optional | string | - | Additional CSS class applied on the root component |
PopoverProps |
Optional | Object | - | Additional props to pass to the MUI Popover element |
StackedFilters FormProps |
Optional | Object | - | Additional props to pass to the StackedFiltersForm element |
sx |
Optional | Object | - | An object containing the MUI style overrides to apply to the root component |
BadgeProps
This prop lets you pass additional props for the MUI Badge.
import { StackedFilters } from '@react-admin/ra-form-layout';
export const MyStackedFilter = () => (
<StackedFilters config={config} BadgeProps={{ showZero: true }} />
);
ButtonProps
This prop lets you pass additional props for the Button.
import { StackedFilters } from '@react-admin/ra-form-layout';
export const MyStackedFilter = () => (
<StackedFilters config={config} ButtonProps={{ variant: 'contained' }} />
);
className
This prop lets you pass additional CSS classes to apply to the root element (a div
).
import { StackedFilters } from '@react-admin/ra-form-layout';
export const MyStackedFilter = () => (
<StackedFilters config={config} className="my-css-class" />
);
config
This prop lets you define the filter configuration, which is required. This is an object defining the operators and UI for each source that can be used as a filter:
import { FiltersConfig, StackedFilters } from '@react-admin/ra-form-layout';
import { NumberInput } from 'react-admin';
import { MyNumberRangeInput } from './MyNumberRangeInput';
const postListFilters: FiltersConfig = {
views: {
operators: [
{ value: 'eq', label: 'Equals' },
{ value: 'neq', label: 'Not Equals' },
{
value: 'between',
label: 'Between',
input: ({ source }) => <MyNumberRangeInput source={source} />,
},
],
input: ({ source }) => <NumberInput source={source} />,
},
};
export const MyStackedFilter = () => (
<StackedFilters config={postListFilters} />
);
PopoverProps
This prop lets you pass additional props for the MUI Popover.
import { StackedFilters } from '@react-admin/ra-form-layout';
export const MyStackedFilter = () => (
<StackedFilters config={config} PopoverProps={{ elevation: 4 }} />
);
StackedFiltersFormProps
This prop lets you pass additional props for the StackedFiltersForm.
import { StackedFilters } from '@react-admin/ra-form-layout';
export const MyStackedFilter = () => (
<StackedFilters config={config} StackedFiltersForm={{ className: 'my-css-class' }} />
);
sx
: CSS API
This prop lets you override the styles of the inner components thanks to the sx
property. This property accepts the following subclasses:
Rule name | Description |
---|---|
RaStackedFilters |
Applied to the root component |
& .RaStackedFilters-popover |
Applied to the MUI Popover |
& .RaStackedFilters-formContainer |
Applied to the form container (a div ) |
Internationalization
The source field names are translatable. ra-form-layout
uses the react-admin resource and field name translation system.
This is an example of an English translation file for the customer
resource:
// in i18n/en.js
export default {
resources: {
customer: {
name: 'Customer |||| Customers',
fields: {
first_name: 'First name',
last_name: 'Last name',
dob: 'Date of birth',
},
},
},
};
<StackedFilters>
also supports internationalization for operators. To leverage it, pass a translation key as the operator label:
import { FiltersConfig } from '@react-admin/ra-form-layout';
import DateRangeInput from './DateRangeInput';
const MyFilterConfig: FiltersConfig = {
published_at: {
operators: [
{
value: 'between',
label: 'resources.posts.filters.operators.between',
},
{
value: 'nbetween',
label: 'resources.posts.filters.operators.nbetween',
},
],
input: ({ source }) => <DateRangeInput source={source} />,
},
};
<StackedFiltersForm>
This component is responsible for rendering the filtering form, and is used internally by <StackedFilters>
. You can use it if you want to use the filter form without the <FilterButton>
component, e.g. to always show the filter form.
Usage
Just like <StackedFilters>
, <StackedFiltersForm>
requires a filtering configuration as its config
prop value.
import {
Datagrid,
List,
TextField,
NumberField,
BooleanField,
ReferenceArrayField,
useListContext,
} from 'react-admin';
import {
StackedFiltersForm,
FiltersConfig,
textFilter,
referenceFilter,
booleanFilter,
dateFilter,
} from '@react-admin/ra-form-layout';
import {
Accordion,
AccordionDetails,
AccordionSummary,
Card,
Typography,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
const postListFilters: FiltersConfig = {
id: textFilter({ operators: ['eq', 'neq'] }),
title: textFilter(),
published_at: dateFilter(),
is_public: booleanFilter(),
tag_ids: referenceFilter({ reference: 'tags' }),
};
const PostListFiltersForm = () => {
const { filterValues } = useListContext();
return (
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="filters-content"
id="filters-header"
>
<Typography>
{Object.keys(filterValues).length ? `${Object.keys(filterValues).length} filter(s) applied` : 'Filters'}
</Typography>
</AccordionSummary>
<AccordionDetails id="filters-content">
<StackedFiltersForm config={postListFilters} />
</AccordionDetails>
</Accordion>
);
};
If you need to be notified when users have applied filters, pass a function to the onFiltersApplied
prop. This is useful if you want to close the filters container (<Modal>
, <Drawer>
, etc.).
Props
Prop | Required | Type | Default | Description |
---|---|---|---|---|
config |
Required | object | - | The stacked filters configuration |
className |
Optional | string | - | Additional CSS class applied on the root component |
onFiltersApplied |
Optional | Function | - | A function called when users click on the apply button |
sx |
Optional | Object | - | An object containing the MUI style overrides to apply to the root component |
className
This prop lets you pass additional CSS classes to apply to the root element (a Form
).
import { StackedFiltersForm } from '@react-admin/ra-form-layout';
export const MyStackedFilterForm = () => (
<StackedFiltersForm config={config} className="my-css-class" />
);
config
This prop lets you define the filter configuration, which is required. This is an object defining the operators and UI for each source that can be used as a filter:
import { FiltersConfig, StackedFiltersForm } from '@react-admin/ra-form-layout';
import { NumberInput } from 'react-admin';
import { MyNumberRangeInput } from './MyNumberRangeInput';
const postListFilters: FiltersConfig = {
views: {
operators: [
{ value: 'eq', label: 'Equals' },
{ value: 'neq', label: 'Not Equals' },
{
value: 'between',
label: 'Between',
input: ({ source }) => <MyNumberRangeInput source={source} />,
},
],
input: ({ source }) => <NumberInput source={source} />,
},
};
export const MyStackedFiltersForm = () => (
<StackedFiltersForm config={postListFilters} />
);
onFiltersApplied
This prop lets you provide a function that will be called when users click the apply button:
import { FiltersConfig } from '@react-admin/ra-form-layout';
export const MyStackedFiltersForm = () => (
<StackedFiltersForm config={config} onFiltersApplied={() => alert('Filters applied')} />
);
sx
: CSS API
This prop lets you override the styles of the inner components thanks to the sx
property. This property accepts the following subclasses:
Rule name | Description |
---|---|
RaStackedFiltersForm |
Applied to the root component |
& .RaStackedFiltersForm-sourceInput |
Applied to the AutocompleteInput that allows users to select the field |
& .RaStackedFiltersForm-operatorInput |
Applied to the SelectInput that allows users to select the field |
& .RaStackedFiltersForm-valueInput |
Applied to the input that allows users to set the filter value |