From 3ef0a402b56fb65cb3bdb44f2ece94c1873a2336 Mon Sep 17 00:00:00 2001 From: Olivier Louvignes Date: Wed, 11 Dec 2019 08:15:23 +0100 Subject: [PATCH] feat(table): Add defaultFilteredValues to table columns (#20088) --- .../table/__tests__/Table.filter.test.js | 77 ++++++++++++++----- components/table/hooks/useFilter/index.tsx | 29 ++++--- components/table/index.en-US.md | 1 + components/table/index.zh-CN.md | 1 + components/table/interface.tsx | 4 +- 5 files changed, 81 insertions(+), 31 deletions(-) diff --git a/components/table/__tests__/Table.filter.test.js b/components/table/__tests__/Table.filter.test.js index 6a1486cd12..46bc1c6aeb 100644 --- a/components/table/__tests__/Table.filter.test.js +++ b/components/table/__tests__/Table.filter.test.js @@ -20,10 +20,7 @@ describe('Table.filter', () => { { text: 'Title', value: 'title', - children: [ - { text: 'Designer', value: 'designer' }, - { text: 'Coder', value: 'coder' }, - ], + children: [{ text: 'Designer', value: 'designer' }, { text: 'Coder', value: 'coder' }], }, ], onFilter: filterFn, @@ -310,6 +307,58 @@ describe('Table.filter', () => { expect(wrapper.find('tbody tr').length).toBe(4); }); + it('can read defaults from defaultFilteredValue', () => { + const wrapper = mount( + createTable({ + columns: [ + { + ...column, + defaultFilteredValue: ['Lucy'], + }, + ], + }), + ); + expect(wrapper.find('tbody tr').length).toBe(1); + expect(wrapper.find('tbody tr').text()).toBe('Lucy'); + + // Should properly ignore further defaultFilteredValue changes + wrapper.setProps({ + columns: [ + { + ...column, + defaultFilteredValue: [], + }, + ], + }); + expect(wrapper.find('tbody tr').length).toBe(1); + expect(wrapper.find('tbody tr').text()).toBe('Lucy'); + + // Should properly be overidden by non-null filteredValue + wrapper.setProps({ + columns: [ + { + ...column, + defaultFilteredValue: ['Lucy'], + filteredValue: ['Tom'], + }, + ], + }); + expect(wrapper.find('tbody tr').length).toBe(1); + expect(wrapper.find('tbody tr').text()).toBe('Tom'); + + // Should properly be overidden by a null filteredValue + wrapper.setProps({ + columns: [ + { + ...column, + defaultFilteredValue: ['Lucy'], + filteredValue: null, + }, + ], + }); + expect(wrapper.find('tbody tr').length).toBe(4); + }); + it('fires change event', () => { const handleChange = jest.fn(); const wrapper = mount(createTable({ onChange: handleChange })); @@ -418,10 +467,7 @@ describe('Table.filter', () => { // }); describe('should support value types', () => { - [ - ['Light', 93], - ['Bamboo', false], - ].forEach(([text, value]) => { + [['Light', 93], ['Bamboo', false]].forEach(([text, value]) => { it(`${typeof value} type`, () => { const onFilter = jest.fn(); const filters = [{ text, value }]; @@ -505,10 +551,7 @@ describe('Table.filter', () => { title="name" dataIndex="name" key="name" - filters={[ - { text: 'Jack', value: 'Jack' }, - { text: 'Lucy', value: 'Lucy' }, - ]} + filters={[{ text: 'Jack', value: 'Jack' }, { text: 'Lucy', value: 'Lucy' }]} filteredValue={filters.name} onFilter={filterFn} /> @@ -547,10 +590,7 @@ describe('Table.filter', () => { title: 'Name', dataIndex: 'name', key: 'name', - filters: [ - { text: 'Jack', value: 'Jack' }, - { text: 'Lucy', value: 'Lucy' }, - ], + filters: [{ text: 'Jack', value: 'Jack' }, { text: 'Lucy', value: 'Lucy' }], onFilter: filterFn, filteredValue: ['Jack'], }, @@ -580,10 +620,7 @@ describe('Table.filter', () => { columns: [ { ...column, - filters: [ - { text: 'Jack', value: 'Jack' }, - { text: 'Lucy', value: 'Lucy' }, - ], + filters: [{ text: 'Jack', value: 'Jack' }, { text: 'Lucy', value: 'Lucy' }], }, ], onChange: handleChange, diff --git a/components/table/hooks/useFilter/index.tsx b/components/table/hooks/useFilter/index.tsx index 08e3ca2405..f20341d86f 100644 --- a/components/table/hooks/useFilter/index.tsx +++ b/components/table/hooks/useFilter/index.tsx @@ -22,6 +22,7 @@ export interface FilterState { function collectFilterStates( columns: ColumnsType, + init: boolean, pos?: string, ): FilterState[] { let filterStates: FilterState[] = []; @@ -30,14 +31,24 @@ function collectFilterStates( const columnPos = getColumnPos(index, pos); if ('children' in column) { - filterStates = [...filterStates, ...collectFilterStates(column.children, columnPos)]; + filterStates = [...filterStates, ...collectFilterStates(column.children, init, columnPos)]; } else if ('filters' in column || 'filterDropdown' in column) { - // Controlled - filterStates.push({ - column, - key: getColumnKey(column, columnPos), - filteredKeys: column.filteredValue, - }); + if ('filteredValue' in column) { + // Controlled + filterStates.push({ + column, + key: getColumnKey(column, columnPos), + filteredKeys: column.filteredValue, + }); + } else { + // Uncontrolled + filterStates.push({ + column, + key: getColumnKey(column, columnPos), + filteredKeys: + init && column.defaultFilteredValue ? column.defaultFilteredValue! : undefined, + }); + } } }); @@ -172,11 +183,11 @@ function useFilter({ const tableLocale = (locale.Table || {}) as TableLocale; const [filterStates, setFilterStates] = React.useState[]>( - collectFilterStates(columns), + collectFilterStates(columns, true), ); const mergedFilterStates = React.useMemo(() => { - const collectedStates = collectFilterStates(columns); + const collectedStates = collectFilterStates(columns, false); // Return if not controlled if (collectedStates.every(({ filteredKeys }) => filteredKeys === undefined)) { diff --git a/components/table/index.en-US.md b/components/table/index.en-US.md index a559bd5b4f..58ab97e266 100644 --- a/components/table/index.en-US.md +++ b/components/table/index.en-US.md @@ -116,6 +116,7 @@ One of the Table `columns` prop for describing the table's columns, Column has t | className | className of this column | string | - | | colSpan | Span of this column's title | number | - | | dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | +| defaultFilteredValue | Default filtered values | string\[] | - | | | defaultSortOrder | Default order of sorted values | 'ascend' \| 'descend' | - | | filterDropdown | Customized filter overlay | React.ReactNode \| (props: [FilterDropdownProps](https://git.io/fjP5h)) => React.ReactNode | - | | filterDropdownVisible | Whether `filterDropdown` is visible | boolean | - | diff --git a/components/table/index.zh-CN.md b/components/table/index.zh-CN.md index 088ab76488..8b0905882c 100644 --- a/components/table/index.zh-CN.md +++ b/components/table/index.zh-CN.md @@ -121,6 +121,7 @@ const columns = [ | className | 列样式类名 | string | - | | colSpan | 表头列合并,设置为 0 时,不渲染 | number | - | | dataIndex | 列数据在数据项中对应的路径,支持通过数组查询嵌套路径 | string \| string\[] | - | +| defaultFilteredValue | 默认筛选值 | string\[] | - | | | defaultSortOrder | 默认排序顺序 | 'ascend' \| 'descend' | - | | filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | React.ReactNode \| (props: [FilterDropdownProps](https://git.io/fjP5h)) => React.ReactNode | - | | filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | boolean | - | diff --git a/components/table/interface.tsx b/components/table/interface.tsx index 20b6765cb8..4e4abe0765 100644 --- a/components/table/interface.tsx +++ b/components/table/interface.tsx @@ -83,6 +83,7 @@ export interface ColumnType extends RcColumnType { filterDropdown?: React.ReactNode | ((props: FilterDropdownProps) => React.ReactNode); filterMultiple?: boolean; filteredValue?: Key[] | null; + defaultFilteredValue?: Key[] | null; filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode); onFilter?: (value: any, record: RecordType) => boolean; filterDropdownVisible?: boolean; @@ -95,8 +96,7 @@ export interface ColumnGroupType extends ColumnType { export type ColumnsType = ( | ColumnGroupType - | ColumnType -)[]; + | ColumnType)[]; export interface SelectionItem { key: string;