Skip to main content
Version: 4.xx.xx

<FilterDropdown />

Usage

<FilterDropdown> is a helper component for filter dropdowns in Ant Design's <Table> components.

It serves as a bridge by synchronizing between its children's input value and <Table>'s filter values.

components/pages/postList.tsx
import {
List,
FilterDropdown,
useTable,
} from "@refinedev/antd";
import { Table, Select } from "antd";

const PostList: React.FC = (props) => {
const { tableProps } = useTable<IPost>();

return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column
dataIndex={["category", "id"]}
title="Category"
key="category.id"
filterDropdown={(props) => (
<FilterDropdown {...props}>
<Select
mode="multiple"
placeholder="Select Category"
options={[
{ label: "Ergonomic", value: "1" },
{ label: "Island", value: "2" },
]}
/>
</FilterDropdown>
)}
/>
</Table>
</List>
);
};

interface IPost {
id: number;
category: {
id: number;
};
}

Selecting categories from dropdown will send the id's of categories as filtering values to Table and data will be updated by Refine under the hood.

<FilterDropdown> will put two buttons for filtering and clearing filter actions.

Show record action

We added category options for <Select> manually for the sake of simplicity but the useSelect hook can be used to populate the props of <Select>

const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
optionLabel: "title",
optionValue: "id",
});

<Select {...categorySelectProps} />;

Properties

selectedKeys, setSelectedKeys, confirm, clearFilters

These are to be passed from <Table.Column>'s filterDropdown prop.

mapValue

The mapValue function is a utility function used to transform the selectedKeys based on certain events.

function mapValue(selectedKeys: React.Key[], event: "onChange" | "value"): any;
  • selectedKeys: The selected keys from the dropdown.
  • event: The event that triggered the mapValue function. It can be either onChange or value.
    • onChange: The event that is triggered when the value of the dropdown changes. It is used to map the value to the format that the Refine expects(data provider, syncWithLocation etc.).
    • value: When the value needs to be mapped for the child component.

For example when using useSelect for <Select /> component, in which case, the values must be mapped to numbers using mapValue.

import { getDefaultFilter } from "@refinedev/core";
import { useTable, FilterDropdown, useSelect } from "@refinedev/antd";
import { Table, Select } from "antd";

const { tableProps, filters } = useTable<IPost>({
filters: {
initial: [
{
field: "category.id",
value: [1, 2],
operator: "in",
},
],
},
});

const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
optionLabel: "title",
optionValue: "id",
defaultValue: getDefaultFilter("category.id", filters, "in"),
});

<Table>
<Table.Column dataIndex="id" title="ID" />
<Table.Column
dataIndex={["category", "id"]}
title="Category"
key="category.id"
filterDropdown={(props) => (
<FilterDropdown
{...props}
mapValue={(selectedKeys) =>
selectedKeys.map((i) => parseInt(i.toString()))
}
>
<Select
style={{ minWidth: 200 }}
mode="multiple"
placeholder="Select Category"
{...categorySelectProps}
/>
</FilterDropdown>
)}
defaultFilteredValue={getDefaultFilter("category.id", filters, "in")}
/>
</Table>;

rangePickerFilterMapper

A more complex example is using a filter dropdown with a date picker.

Imagine you need to filter data based on a date range where Refine's data provider expects dates in ISO 8601 format, but Ant Design's <DatePicker.RangePicker /> uses Dayjs objects. To solve this, use the mapValue and rangePickerFilterMapper utility function to convert selectedKeys to satisfy both the data provider and <DatePicker.RangePicker />.

import { getDefaultFilter } from "@refinedev/core";
import {
DateField,
FilterDropdown,
rangePickerFilterMapper,
useTable,
} from "@refinedev/antd";
import { Table, DatePicker } from "antd";

export const Posts = () => {
const { tableProps, filters } = useTable({
filters: {
initial: [
{
field: "created_at",
value: ["2022-01-01", "2022-01-31"],
operator: "between",
},
],
},
});

return (
<Table {...tableProps} rowKey="id">
<Table.Column dataIndex="id" title="ID" />
<Table.Column dataIndex="title" title="Title" />
<Table.Column
dataIndex="createdAt"
title="Created At"
filterDropdown={(props) => (
<FilterDropdown
{...props}
mapValue={(selectedKeys, event) => {
return rangePickerFilterMapper(selectedKeys, event);
}}
>
<DatePicker.RangePicker />
</FilterDropdown>
)}
defaultFilteredValue={getDefaultFilter(
"created_at",
filters,
"between",
)}
/>
</Table>
);
};

Let's closer look at the rangePickerFilterMapper function source code to understand how it works.

when the event is:

  • "value": It converts the selectedKeys to Dayjs objects to be used in the <DatePicker.RangePicker /> component.
  • "onChange", It converts the Dayjs objects to ISO 8601 string format to be used in the Refine(data-provider, syncWithLocation etc.) filter.
import type {
FilterDropdownProps,
MapValueEvent,
} from "@components/table/components";
import dayjs from "dayjs";

export const rangePickerFilterMapper = (
selectedKeys: FilterDropdownProps["selectedKeys"],
event: MapValueEvent,
) => {
if (!selectedKeys) {
return selectedKeys;
}

if (event === "value") {
return selectedKeys.map((key) => {
if (typeof key === "string") {
return dayjs(key);
}

return key;
});
}

if (event === "onChange") {
if (selectedKeys.every(dayjs.isDayjs)) {
return selectedKeys.map((date: any) => dayjs(date).toISOString());
}
}

return selectedKeys;
};
Good to know:

If syncWithLocation is enabled, on page refresh, the filter values will be type of string since they will be parsed from URL. This might produce some incompatibility if data for filter input comes from an API and it's not type of string.

getDefaultFilter finds filter values for a given column from the given filters. In the example, filters passed to getDefaultFilter includes filter values from the URL since it comes from useTable.

Example

Run on your local
npm create refine-app@latest -- --example table-antd-use-table