Fix Table filter can not support other type of value (#15046)

* use new life cycle

* filter support inner value maps

* update snapshot

* update test case

* simple code
This commit is contained in:
zombieJ 2019-02-26 14:26:08 +08:00 committed by GitHub
parent b30e90ba31
commit a802a72d56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 59 deletions

View File

@ -5,6 +5,15 @@ import Table from '..';
import Input from '../../input';
import Button from '../../button';
function getDropdownWrapper(wrapper) {
return mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
}
describe('Table.filter', () => {
const filterFn = (value, record) => record.name.indexOf(value) !== -1;
const column = {
@ -268,12 +277,7 @@ describe('Table.filter', () => {
it('fires change event', () => {
const handleChange = jest.fn();
const wrapper = mount(createTable({ onChange: handleChange }));
const dropdownWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
const dropdownWrapper = getDropdownWrapper(wrapper);
dropdownWrapper
.find('MenuItem')
@ -294,12 +298,7 @@ describe('Table.filter', () => {
it('should not fire change event on close filterDropdown without changing anything', () => {
const handleChange = jest.fn();
const wrapper = mount(createTable({ onChange: handleChange }));
const dropdownWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
const dropdownWrapper = getDropdownWrapper(wrapper);
dropdownWrapper.find('.clear').simulate('click');
@ -339,12 +338,7 @@ describe('Table.filter', () => {
}),
);
jest.useFakeTimers();
const dropdownWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
const dropdownWrapper = getDropdownWrapper(wrapper);
dropdownWrapper
.find('.ant-dropdown-menu-submenu-title')
.at(0)
@ -367,6 +361,40 @@ describe('Table.filter', () => {
jest.useRealTimers();
});
describe('should support value types', () => {
[['Light', 93], ['Bamboo', false]].forEach(([text, value]) => {
it(`${typeof value} type`, () => {
const onFilter = jest.fn();
const filters = [{ text, value }];
const wrapper = mount(
createTable({
columns: [
{
...column,
filters,
onFilter,
},
],
}),
);
jest.useFakeTimers();
const dropdownWrapper = getDropdownWrapper(wrapper);
dropdownWrapper
.find('MenuItem')
.first()
.simulate('click');
dropdownWrapper.find('.confirm').simulate('click');
wrapper.update();
expect(onFilter.mock.calls.length > 0).toBeTruthy();
onFilter.mock.calls.forEach(([val]) => {
expect(val).toBe(value);
});
jest.useRealTimers();
});
});
});
it('works with JSX in controlled mode', () => {
const { Column } = Table;
@ -397,12 +425,7 @@ describe('Table.filter', () => {
}
const wrapper = mount(<App />);
const dropdownWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
const dropdownWrapper = getDropdownWrapper(wrapper);
dropdownWrapper
.find('MenuItem')

View File

@ -1,5 +1,6 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { polyfill } from 'react-lifecycles-compat';
import Menu, { SubMenu, Item as MenuItem } from 'rc-menu';
import closest from 'dom-closest';
import classNames from 'classnames';
@ -10,6 +11,7 @@ import Checkbox from '../checkbox';
import Radio from '../radio';
import FilterDropdownMenuWrapper from './FilterDropdownMenuWrapper';
import { FilterMenuProps, FilterMenuState, ColumnProps, ColumnFilterItem } from './interface';
import { generateValueMaps } from './util';
function stopPropagation(e: React.SyntheticEvent<any>) {
e.stopPropagation();
@ -18,38 +20,18 @@ function stopPropagation(e: React.SyntheticEvent<any>) {
}
}
export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, FilterMenuState> {
class FilterMenu<T> extends React.Component<FilterMenuProps<T>, FilterMenuState<T>> {
static defaultProps = {
handleFilter() {},
column: {},
};
neverShown: boolean;
constructor(props: FilterMenuProps<T>) {
super(props);
const visible =
'filterDropdownVisible' in props.column ? props.column.filterDropdownVisible : false;
this.state = {
selectedKeys: props.selectedKeys,
keyPathOfSelectedItem: {}, // 记录所有有选中子菜单的祖先菜单
visible,
};
}
componentDidMount() {
const { column } = this.props;
this.setNeverShown(column);
}
componentWillReceiveProps(nextProps: FilterMenuProps<T>) {
static getDerivedStateFromProps<T>(nextProps: FilterMenuProps<T>, prevState: FilterMenuState<T>) {
const { column } = nextProps;
this.setNeverShown(column);
const newState = {} as {
selectedKeys: string[];
visible: boolean;
const { prevProps } = prevState;
const newState: Partial<FilterMenuState<T>> = {
prevProps: nextProps,
};
/**
@ -61,16 +43,44 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
*/
if (
'selectedKeys' in nextProps &&
!shallowequal(this.props.selectedKeys, nextProps.selectedKeys)
!shallowequal(prevProps.selectedKeys, nextProps.selectedKeys)
) {
newState.selectedKeys = nextProps.selectedKeys;
}
if (!shallowequal((prevProps.column || {}).filters, (nextProps.column || {}).filters)) {
newState.valueKeys = generateValueMaps(nextProps.column.filters);
}
if ('filterDropdownVisible' in column) {
newState.visible = column.filterDropdownVisible as boolean;
}
if (Object.keys(newState).length > 0) {
this.setState(newState);
}
return newState;
}
neverShown: boolean;
constructor(props: FilterMenuProps<T>) {
super(props);
const visible =
'filterDropdownVisible' in props.column ? props.column.filterDropdownVisible : false;
this.state = {
selectedKeys: props.selectedKeys,
valueKeys: generateValueMaps(props.column.filters),
keyPathOfSelectedItem: {}, // 记录所有有选中子菜单的祖先菜单
visible,
prevProps: props,
};
}
componentDidMount() {
const { column } = this.props;
this.setNeverShown(column);
}
componentDidUpdate() {
const { column } = this.props;
this.setNeverShown(column);
}
getDropdownVisible() {
@ -128,10 +138,14 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
};
confirmFilter() {
const { selectedKeys } = this.state;
const { selectedKeys, valueKeys } = this.state;
const { filterDropdown } = this.props.column;
if (!shallowequal(selectedKeys, this.props.selectedKeys)) {
this.props.confirmFilter(this.props.column, selectedKeys);
this.props.confirmFilter(
this.props.column,
filterDropdown ? selectedKeys : selectedKeys.map(key => valueKeys[key]),
);
}
}
@ -227,6 +241,7 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
};
render() {
const { selectedKeys: originSelectedKeys } = this.state;
const { column, locale, prefixCls, dropdownPrefixCls, getPopupContainer } = this.props;
// default multiple selection in filter dropdown
const multiple = 'filterMultiple' in column ? column.filterMultiple : true;
@ -238,7 +253,7 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
filterDropdown = filterDropdown({
prefixCls: `${dropdownPrefixCls}-custom`,
setSelectedKeys: (selectedKeys: Array<any>) => this.setSelectedKeys({ selectedKeys }),
selectedKeys: this.state.selectedKeys,
selectedKeys: originSelectedKeys,
confirm: this.handleConfirm,
clearFilters: this.handleClearFilters,
filters: column.filters,
@ -259,7 +274,7 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
className={dropdownMenuClass}
onSelect={this.setSelectedKeys}
onDeselect={this.setSelectedKeys}
selectedKeys={this.state.selectedKeys}
selectedKeys={originSelectedKeys && originSelectedKeys.map(val => val.toString())}
getPopupContainer={(triggerNode: HTMLElement) => triggerNode.parentNode}
>
{this.renderMenus(column.filters!)}
@ -290,3 +305,7 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
);
}
}
polyfill(FilterMenu);
export default FilterMenu;

View File

@ -239,10 +239,12 @@ export interface FilterMenuProps<T> {
getPopupContainer?: GetPopupContainer;
}
export interface FilterMenuState {
export interface FilterMenuState<T> {
selectedKeys: string[];
valueKeys: { [name: string]: any };
keyPathOfSelectedItem: { [key: string]: string };
visible?: boolean;
prevProps: FilterMenuProps<T>;
}
export type PrepareParamsArgumentsReturn<T> = [

View File

@ -1,4 +1,5 @@
import * as React from 'react';
import { ColumnFilterItem } from './interface';
export function flatArray(data: any[] = [], childrenName = 'children') {
const result: any[] = [];
@ -69,3 +70,11 @@ export function normalizeColumns(elements: React.ReactChildren) {
});
return columns;
}
export function generateValueMaps(items?: ColumnFilterItem[], maps: { [name: string]: any } = {}) {
(items || []).forEach(({ value, children }) => {
maps[value.toString()] = value;
generateValueMaps(children, maps);
});
return maps;
}