Search code examples
javascriptreactjsfrontendcomponentsantd

How to create a reusable component for ant design table columns filter that we can use it for all our table?


I want to add filter, search to ant design tables. Based on the documentation, we need to add some props to table column but it will be repeatative. Is there a way that we can creat a seperate component for the columns and filtering those columns?

I tried to develop that component but the column props' do not accept JSX.


Solution

  • I've done it in this way:

    SearchHighliter:

    const HighlighterWrapper = memo(({ searchWords, textToHighlight }) => (
      <Highlighter
        highlightStyle={{
          backgroundColor: '#ffc069',
          padding: 0,
        }}
        searchWords={searchWords}
        autoEscape
        textToHighlight={textToHighlight}
      />
    ))
    

    FilterHook:

    import { useRef, useState } from 'react'
    import { Button, Input, Space } from 'antd'
    import { SearchOutlined } from '@ant-design/icons'
    
    const useTableFilter = () => {
      const [searchText, setSearchText] = useState('')
      const [searchedColumn, setSearchedColumn] = useState('')
      const searchInput = useRef(null)
    
      const handleSearch = (selectedKeys, confirm, dataIndex) => {
        confirm()
        setSearchText(selectedKeys[0])
        setSearchedColumn(dataIndex)
      }
    
      const handleReset = (clearFilters, confirm) => {
        clearFilters()
        setSearchText('')
        confirm()
      }
    
      const getColumnSearchProps = (dataIndex) => ({
        filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
          <div
            style={{
              padding: 8,
            }}
          >
            <Input
              ref={searchInput}
              placeholder="Search text"
              value={selectedKeys[0]}
              onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])}
              onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
              style={{
                marginBottom: 8,
                display: 'block',
              }}
            />
            <Space>
              <Button
                type="primary"
                onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
                icon={<SearchOutlined />}
                size="small"
                style={{
                  width: 90,
                }}
              >
                Search
              </Button>
              <Button
                onClick={() => clearFilters && handleReset(clearFilters, confirm)}
                size="small"
                style={{
                  width: 90,
                }}
              >
                Reset
              </Button>
            </Space>
          </div>
        ),
        filterIcon: (filtered) => (
          <SearchOutlined
            style={{
              color: filtered ? '#1890ff' : undefined,
              marginRight: 10,
            }}
          />
        ),
        onFilterDropdownVisibleChange: (visible) => {
          if (visible) {
            setTimeout(() => searchInput.current?.select(), 100)
          }
        },
      })
    
      return [searchText, searchedColumn, getColumnSearchProps]
    }
    
    export default useTableFilter
    

    Table:

    const SummaryReport = () => {
      const [data, setData] = useState([])
      const [isLoading, setIsLoading] = useState(false)
      const [searchText, searchedColumn, getColumnSearchProps] = useTableFilter()
    
      const columns = [
        {
          title: () => <span style={{ paddingLeft: 8 }}>Name</span>,
          key: 'name',
          dataIndex: 'name',
          className: 'no-padding-cell-report',
          width: 250,
          ...getColumnSearchProps('name'),
          onFilter: (value, record) => get(record, 'name').toString().toLowerCase().includes(value.toLowerCase()),
          render: (_, record) => (
            <div style={{ padding: 8 }}>
              {searchedColumn === 'name' ? (
                <HighlighterWrapper
                  searchWords={[searchText]}
                  textToHighlight={get(record, 'name') ? get(record, 'name').toString() : ''}
                />
              ) : (
                get(record, 'name')
              )}
            </div>
          ),
        },
      ]
    
    ....
    
    }
    

    All you need is just provide 2 functions to your column:

    • onFilter - here you describe your filter logic, if your data object is simple like in my case, the function also is trivial
    • render - you need to provide this function if you want to highlight text that the user inputs to filter input.