Search code examples
reactjsantd

How to implement a custom checkbox group filter using Ant Design


We have a complex custom table with Custom filters (Radio group, Date picker etc.) Now we would like to implement a CheckBoxGroup filter with which user can filter based on multiple values. But the implementation doesnt works as expected (Here is the custom filter):

export const CheckBoxGroupFilter = ({ setSelectedKeys, selectedKeys, confirm, clearFilters, filters }) => {
return (
    <div style={{ padding: 8 }}>
        <Space direction="vertical">
            <Checkbox.Group onChange={(checkedValues) => setSelectedKeys(checkedValues)}>
                <Space direction="vertical">
                    {filters?.map((status, index) => (
                        <Checkbox key={status.value} value={status.value}>
                            {status.text}
                        </Checkbox>
                    ))}
                </Space>
            </Checkbox.Group>

            <Divider style={{ marginTop: '5px', marginBottom: '5px' }} />
            <div>
                <Button
                    type="primary"
                    onClick={() => confirm()}
                    icon={<SearchOutlined />}
                    size="small"
                    style={{ width: 90, marginRight: 8 }}
                >
                    Search
                </Button>
                <Button
                    size="small"
                    style={{ width: 90 }}
                    onClick={() => {
                        clearFilters()
                        confirm()
                    }}
                >
                    Reset
                </Button>
            </div>
        </Space>
    </div>
)}

Using in Custom table:

             filterDropdown: (props) => {
                  if (column.filterable === false) return null
                  if (column.radioGroupFilter) return <RadioGroupFilter {...props} />
                  if (column.datePickerFilter)
                      return <DatePickerFilter {...props} customDateFormat={column.dateFormat} />
                  if (column.checkBoxGroupFilter)
                      return <CheckBoxGroupFilter {...props} />
                  return <InputTextFilter {...props} searchInputHolder={searchInputHolder} />
              },

Currently, pressing the Search button, invoking cofirm(), does modify the url query params, so you can see for example status=approved&status=pending, and this filters the first result page. But either on pagination change, or refresh the page, the table forgets the query params, and table no longer filtered. Values stay checked. Also the Reset button, which calls clearFilters() does not work. So it seems table's column itself doesn't know that it had been filtered.

The code snippet confirms this, since filtered value doesn't change color in filterIcon method:

            filterIcon: (filtered) => {
                  if (column.checkBoxGroupFilter)
                      return <FilterFilled style={{ color: filtered ? '#1890ff' : undefined }} />
                  return <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
              },

The other custom filters works as expected, they change the filtered value in above snippet. RadioGroup filter implemantation nearly same.


Solution

  • We found the problem was that the data key and dateIndex was not the same. We renamed key to statuses since it will be an array, but we shouldn't had to. The above implementation works correctly if the names are same, otherwise the table has buggy behavior

    Solution: When you define your columns use same key and dataIndex name.

    {
            title: 'Status',
            key: 'status',
            dataIndex: 'status',
            checkBoxGroupFilter: true,
            onFilter: (value, record) => {
                return record.status && value
                    ? record.status.toString().toLowerCase().includes(value.toString().toLowerCase())
                    : ''
            },
            filters: Object.keys(DisplayBookingStatus).map((key) => ({
                text: DisplayBookingStatus[key].toLowerCase(),
                value: key,
            })),
            render: (status: string) => {
                return <ReservationStatus status={DisplayBookingStatus[status?.toLowerCase()] || status} />
            },
        },