mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-04 00:49:39 +08:00
refactor(checkbox-group): rewrite with hook (#27147)
This commit is contained in:
parent
e234a71ecd
commit
261018053f
@ -2,7 +2,7 @@ import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import Checkbox, { CheckboxChangeEvent } from './Checkbox';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
export type CheckboxValueType = string | number | boolean;
|
||||
|
||||
@ -29,11 +29,6 @@ export interface CheckboxGroupProps extends AbstractCheckboxGroupProps {
|
||||
onChange?: (checkedValue: Array<CheckboxValueType>) => void;
|
||||
}
|
||||
|
||||
export interface CheckboxGroupState {
|
||||
value: CheckboxValueType[];
|
||||
registeredValues: CheckboxValueType[];
|
||||
}
|
||||
|
||||
export interface CheckboxGroupContext {
|
||||
toggleOption?: (option: CheckboxOptionType) => void;
|
||||
value?: any;
|
||||
@ -42,135 +37,119 @@ export interface CheckboxGroupContext {
|
||||
|
||||
export const GroupContext = React.createContext<CheckboxGroupContext | null>(null);
|
||||
|
||||
class CheckboxGroup extends React.PureComponent<CheckboxGroupProps, CheckboxGroupState> {
|
||||
static defaultProps = {
|
||||
options: [],
|
||||
};
|
||||
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
|
||||
defaultValue,
|
||||
children,
|
||||
options = [],
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
style,
|
||||
onChange,
|
||||
...restProps
|
||||
}) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
|
||||
static getDerivedStateFromProps(nextProps: CheckboxGroupProps) {
|
||||
if ('value' in nextProps) {
|
||||
return {
|
||||
value: nextProps.value || [],
|
||||
};
|
||||
const [value, setValue] = React.useState<CheckboxValueType[]>(
|
||||
restProps.value || defaultValue || [],
|
||||
);
|
||||
const [registeredValues, setRegisteredValues] = React.useState<CheckboxValueType[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if ('value' in restProps) {
|
||||
setValue(restProps.value || []);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, [restProps.value]);
|
||||
|
||||
constructor(props: CheckboxGroupProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: props.value || props.defaultValue || [],
|
||||
registeredValues: [],
|
||||
};
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
const { options } = this.props;
|
||||
// https://github.com/Microsoft/TypeScript/issues/7960
|
||||
return (options as Array<CheckboxOptionType>).map(option => {
|
||||
const getOptions = () => {
|
||||
return options.map(option => {
|
||||
if (typeof option === 'string') {
|
||||
return {
|
||||
label: option,
|
||||
value: option,
|
||||
} as CheckboxOptionType;
|
||||
};
|
||||
}
|
||||
return option;
|
||||
});
|
||||
}
|
||||
|
||||
cancelValue = (value: string) => {
|
||||
this.setState(({ registeredValues }) => ({
|
||||
registeredValues: registeredValues.filter(val => val !== value),
|
||||
}));
|
||||
};
|
||||
|
||||
registerValue = (value: string) => {
|
||||
this.setState(({ registeredValues }) => ({
|
||||
registeredValues: [...registeredValues, value],
|
||||
}));
|
||||
const cancelValue = (val: string) => {
|
||||
setRegisteredValues(prevValues => prevValues.filter(v => v !== val));
|
||||
};
|
||||
|
||||
toggleOption = (option: CheckboxOptionType) => {
|
||||
const { registeredValues } = this.state;
|
||||
const optionIndex = this.state.value.indexOf(option.value);
|
||||
const value = [...this.state.value];
|
||||
const registerValue = (val: string) => {
|
||||
setRegisteredValues(prevValues => [...prevValues, val]);
|
||||
};
|
||||
|
||||
const toggleOption = (option: CheckboxOptionType) => {
|
||||
const optionIndex = value.indexOf(option.value);
|
||||
const newValue = [...value];
|
||||
if (optionIndex === -1) {
|
||||
value.push(option.value);
|
||||
newValue.push(option.value);
|
||||
} else {
|
||||
value.splice(optionIndex, 1);
|
||||
newValue.splice(optionIndex, 1);
|
||||
}
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
if (!('value' in restProps)) {
|
||||
setValue(newValue);
|
||||
}
|
||||
const { onChange } = this.props;
|
||||
if (onChange) {
|
||||
const options = this.getOptions();
|
||||
const opts = getOptions();
|
||||
onChange(
|
||||
value
|
||||
newValue
|
||||
.filter(val => registeredValues.indexOf(val) !== -1)
|
||||
.sort((a, b) => {
|
||||
const indexA = options.findIndex(opt => opt.value === a);
|
||||
const indexB = options.findIndex(opt => opt.value === b);
|
||||
const indexA = opts.findIndex(opt => opt.value === a);
|
||||
const indexB = opts.findIndex(opt => opt.value === b);
|
||||
return indexA - indexB;
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderGroup = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const { props, state } = this;
|
||||
const { prefixCls: customizePrefixCls, className, style, options, ...restProps } = props;
|
||||
const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
|
||||
const groupPrefixCls = `${prefixCls}-group`;
|
||||
const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
|
||||
const groupPrefixCls = `${prefixCls}-group`;
|
||||
|
||||
const domProps = omit(restProps, ['children', 'defaultValue', 'value', 'onChange', 'disabled']);
|
||||
const domProps = omit(restProps, ['value', 'disabled']);
|
||||
|
||||
let { children } = props;
|
||||
if (options && options.length > 0) {
|
||||
children = this.getOptions().map(option => (
|
||||
<Checkbox
|
||||
prefixCls={prefixCls}
|
||||
key={option.value.toString()}
|
||||
disabled={'disabled' in option ? option.disabled : props.disabled}
|
||||
value={option.value}
|
||||
checked={state.value.indexOf(option.value) !== -1}
|
||||
onChange={option.onChange}
|
||||
className={`${groupPrefixCls}-item`}
|
||||
style={option.style}
|
||||
>
|
||||
{option.label}
|
||||
</Checkbox>
|
||||
));
|
||||
}
|
||||
if (options && options.length > 0) {
|
||||
children = getOptions().map(option => (
|
||||
<Checkbox
|
||||
prefixCls={prefixCls}
|
||||
key={option.value.toString()}
|
||||
disabled={'disabled' in option ? option.disabled : restProps.disabled}
|
||||
value={option.value}
|
||||
checked={value.indexOf(option.value) !== -1}
|
||||
onChange={option.onChange}
|
||||
className={`${groupPrefixCls}-item`}
|
||||
style={option.style}
|
||||
>
|
||||
{option.label}
|
||||
</Checkbox>
|
||||
));
|
||||
}
|
||||
|
||||
const context = {
|
||||
toggleOption: this.toggleOption,
|
||||
value: this.state.value,
|
||||
disabled: this.props.disabled,
|
||||
name: this.props.name,
|
||||
const context = {
|
||||
toggleOption,
|
||||
value,
|
||||
disabled: restProps.disabled,
|
||||
name: restProps.name,
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/16376
|
||||
registerValue: this.registerValue,
|
||||
cancelValue: this.cancelValue,
|
||||
};
|
||||
|
||||
const classString = classNames(
|
||||
groupPrefixCls,
|
||||
{
|
||||
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
return (
|
||||
<div className={classString} style={style} {...domProps}>
|
||||
<GroupContext.Provider value={context}>{children}</GroupContext.Provider>
|
||||
</div>
|
||||
);
|
||||
// https://github.com/ant-design/ant-design/issues/16376
|
||||
registerValue,
|
||||
cancelValue,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderGroup}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
const classString = classNames(
|
||||
groupPrefixCls,
|
||||
{
|
||||
[`${groupPrefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
return (
|
||||
<div className={classString} style={style} {...domProps}>
|
||||
<GroupContext.Provider value={context}>{children}</GroupContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxGroup;
|
||||
export default React.memo(CheckboxGroup);
|
||||
|
@ -14,25 +14,13 @@ describe('CheckboxGroup', () => {
|
||||
const wrapper = mount(
|
||||
<Checkbox.Group options={['Apple', 'Pear', 'Orange']} onChange={onChange} />,
|
||||
);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith(['Apple']);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(1)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith(['Apple', 'Pear']);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(2)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(2).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith(['Apple', 'Pear', 'Orange']);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(1)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith(['Apple', 'Orange']);
|
||||
});
|
||||
|
||||
@ -47,15 +35,9 @@ describe('CheckboxGroup', () => {
|
||||
const groupWrapper = mount(
|
||||
<Checkbox.Group options={options} onChange={onChangeGroup} disabled />,
|
||||
);
|
||||
groupWrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
groupWrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChangeGroup).not.toHaveBeenCalled();
|
||||
groupWrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(1)
|
||||
.simulate('change');
|
||||
groupWrapper.find('.ant-checkbox-input').at(1).simulate('change');
|
||||
expect(onChangeGroup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -68,15 +50,9 @@ describe('CheckboxGroup', () => {
|
||||
];
|
||||
|
||||
const groupWrapper = mount(<Checkbox.Group options={options} onChange={onChangeGroup} />);
|
||||
groupWrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
groupWrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChangeGroup).toHaveBeenCalledWith(['Apple']);
|
||||
groupWrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(1)
|
||||
.simulate('change');
|
||||
groupWrapper.find('.ant-checkbox-input').at(1).simulate('change');
|
||||
expect(onChangeGroup).toHaveBeenCalledWith(['Apple']);
|
||||
});
|
||||
|
||||
@ -105,10 +81,10 @@ describe('CheckboxGroup', () => {
|
||||
];
|
||||
|
||||
const wrapper = mount(<Checkbox.Group options={options} />);
|
||||
|
||||
expect(wrapper.instance().state.value).toEqual([]);
|
||||
expect(wrapper.find('.ant-checkbox-checked').length).toBe(0);
|
||||
wrapper.setProps({ value: ['Apple'] });
|
||||
expect(wrapper.instance().state.value).toEqual(['Apple']);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('.ant-checkbox-checked').length).toBe(1);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/12642
|
||||
@ -119,10 +95,7 @@ describe('CheckboxGroup', () => {
|
||||
<Checkbox value="my" onChange={onChange} />
|
||||
</Checkbox.Group>,
|
||||
);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
expect(onChange.mock.calls[0][0].target.value).toEqual('my');
|
||||
});
|
||||
@ -141,10 +114,7 @@ describe('CheckboxGroup', () => {
|
||||
children: [<Checkbox key={2} value={2} />],
|
||||
});
|
||||
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith([2]);
|
||||
});
|
||||
@ -160,12 +130,7 @@ describe('CheckboxGroup', () => {
|
||||
wrapper.setProps({
|
||||
children: [<Checkbox key={1} value={2} />],
|
||||
});
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.prop('checked'),
|
||||
).toBe(false);
|
||||
expect(wrapper.find('.ant-checkbox-input').at(0).prop('checked')).toBe(false);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/17297
|
||||
@ -180,25 +145,13 @@ describe('CheckboxGroup', () => {
|
||||
</Checkbox.Group>,
|
||||
);
|
||||
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith([1]);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(1)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith([1, 2]);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith([2]);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(onChange).toHaveBeenCalledWith([1, 2]);
|
||||
});
|
||||
|
||||
@ -215,30 +168,10 @@ describe('CheckboxGroup', () => {
|
||||
</Collapse>
|
||||
</Checkbox.Group>,
|
||||
);
|
||||
wrapper
|
||||
.find('.ant-collapse-item')
|
||||
.at(0)
|
||||
.find('.ant-collapse-header')
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
expect(
|
||||
wrapper
|
||||
.find(Checkbox.Group)
|
||||
.at(0)
|
||||
.state('value'),
|
||||
).toEqual(['1']);
|
||||
wrapper
|
||||
.find('.ant-checkbox-input')
|
||||
.at(0)
|
||||
.simulate('change');
|
||||
expect(
|
||||
wrapper
|
||||
.find(Checkbox.Group)
|
||||
.at(0)
|
||||
.state('value'),
|
||||
).toEqual([]);
|
||||
wrapper.find('.ant-collapse-item').at(0).find('.ant-collapse-header').simulate('click');
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(wrapper.find('.ant-checkbox-checked').length).toBe(1);
|
||||
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
|
||||
expect(wrapper.find('.ant-checkbox-checked').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user