refactor(tree-select): rewrite with hook (#27593)

This commit is contained in:
Tom Xu 2020-11-08 15:27:30 +08:00 committed by GitHub
parent 535400f7cd
commit cd15ba4c1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 128 additions and 147 deletions

View File

@ -6,7 +6,7 @@ import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
describe('TreeSelect', () => { describe('TreeSelect', () => {
focusTest(TreeSelect); focusTest(TreeSelect, { refFocus: true });
mountTest(TreeSelect); mountTest(TreeSelect);
rtlTest(TreeSelect); rtlTest(TreeSelect);

View File

@ -8,7 +8,8 @@ import RcTreeSelect, {
} from 'rc-tree-select'; } from 'rc-tree-select';
import classNames from 'classnames'; import classNames from 'classnames';
import omit from 'omit.js'; import omit from 'omit.js';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { DefaultValueType } from 'rc-tree-select/lib/interface';
import { ConfigContext } from '../config-provider';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import { AntTreeNodeProps } from '../tree'; import { AntTreeNodeProps } from '../tree';
import getIcons from '../select/utils/iconUtil'; import getIcons from '../select/utils/iconUtil';
@ -35,56 +36,16 @@ export interface TreeSelectProps<T>
bordered?: boolean; bordered?: boolean;
} }
class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> { export interface RefTreeSelectProps {
static TreeNode = TreeNode; focus: () => void;
blur: () => void;
}
static SHOW_ALL: typeof SHOW_ALL = SHOW_ALL; const InternalTreeSelect = <T extends DefaultValueType>(
{
static SHOW_PARENT: typeof SHOW_PARENT = SHOW_PARENT;
static SHOW_CHILD: typeof SHOW_CHILD = SHOW_CHILD;
static defaultProps = {
transitionName: 'slide-up',
choiceTransitionName: '',
bordered: true,
};
selectRef = React.createRef<RcTreeSelect>();
constructor(props: TreeSelectProps<T>) {
super(props);
devWarning(
props.multiple !== false || !props.treeCheckable,
'TreeSelect',
'`multiple` will alway be `true` when `treeCheckable` is true',
);
}
focus() {
if (this.selectRef.current) {
this.selectRef.current.focus();
}
}
blur() {
if (this.selectRef.current) {
this.selectRef.current.blur();
}
}
renderTreeSelect = ({
getPopupContainer: getContextPopupContainer,
getPrefixCls,
renderEmpty,
direction,
virtual,
dropdownMatchSelectWidth,
}: ConfigConsumerProps) => {
const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
size: customizeSize, size: customizeSize,
bordered = true,
className, className,
treeCheckable, treeCheckable,
multiple, multiple,
@ -95,27 +56,42 @@ class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> {
treeLine, treeLine,
getPopupContainer, getPopupContainer,
dropdownClassName, dropdownClassName,
bordered,
treeIcon = false, treeIcon = false,
} = this.props; transitionName = 'slide-up',
choiceTransitionName = '',
...props
}: TreeSelectProps<T>,
ref: React.Ref<RefTreeSelectProps>,
) => {
const {
getPopupContainer: getContextPopupContainer,
getPrefixCls,
renderEmpty,
direction,
virtual,
dropdownMatchSelectWidth,
} = React.useContext(ConfigContext);
const size = React.useContext(SizeContext);
devWarning(
multiple !== false || !treeCheckable,
'TreeSelect',
'`multiple` will alway be `true` when `treeCheckable` is true',
);
const prefixCls = getPrefixCls('select', customizePrefixCls); const prefixCls = getPrefixCls('select', customizePrefixCls);
const treePrefixCls = getPrefixCls('select-tree', customizePrefixCls); const treePrefixCls = getPrefixCls('select-tree', customizePrefixCls);
const treeSelectPrefixCls = getPrefixCls('tree-select', customizePrefixCls); const treeSelectPrefixCls = getPrefixCls('tree-select', customizePrefixCls);
const mergedDropdownClassName = classNames( const mergedDropdownClassName = classNames(dropdownClassName, `${treeSelectPrefixCls}-dropdown`, {
dropdownClassName,
`${treeSelectPrefixCls}-dropdown`,
{
[`${treeSelectPrefixCls}-dropdown-rtl`]: direction === 'rtl', [`${treeSelectPrefixCls}-dropdown-rtl`]: direction === 'rtl',
}, });
);
const isMultiple = !!(treeCheckable || multiple); const isMultiple = !!(treeCheckable || multiple);
// ===================== Icons ===================== // ===================== Icons =====================
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({ const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({
...this.props, ...props,
multiple: isMultiple, multiple: isMultiple,
prefixCls, prefixCls,
}); });
@ -129,20 +105,14 @@ class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> {
} }
// ==================== Render ===================== // ==================== Render =====================
const selectProps = omit(this.props, [ const selectProps = omit(props, [
'prefixCls',
'suffixIcon', 'suffixIcon',
'itemIcon', 'itemIcon',
'removeIcon', 'removeIcon',
'clearIcon', 'clearIcon',
'switcherIcon', 'switcherIcon',
'size',
'bordered',
]); ]);
return (
<SizeContext.Consumer>
{size => {
const mergedSize = customizeSize || size; const mergedSize = customizeSize || size;
const mergedClassName = classNames( const mergedClassName = classNames(
!customizePrefixCls && treeSelectPrefixCls, !customizePrefixCls && treeSelectPrefixCls,
@ -160,20 +130,17 @@ class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> {
virtual={virtual} virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth} dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps} {...selectProps}
ref={this.selectRef} ref={ref}
prefixCls={prefixCls} prefixCls={prefixCls}
className={mergedClassName} className={mergedClassName}
listHeight={listHeight} listHeight={listHeight}
listItemHeight={listItemHeight} listItemHeight={listItemHeight}
treeCheckable={ treeCheckable={
treeCheckable ? ( treeCheckable ? <span className={`${prefixCls}-tree-checkbox-inner`} /> : treeCheckable
<span className={`${prefixCls}-tree-checkbox-inner`} />
) : (
treeCheckable
)
} }
inputIcon={suffixIcon} inputIcon={suffixIcon}
menuItemSelectedIcon={itemIcon} menuItemSelectedIcon={itemIcon}
multiple={multiple}
removeIcon={removeIcon} removeIcon={removeIcon}
clearIcon={clearIcon} clearIcon={clearIcon}
switcherIcon={(nodeProps: AntTreeNodeProps) => switcherIcon={(nodeProps: AntTreeNodeProps) =>
@ -184,18 +151,32 @@ class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> {
getPopupContainer={getPopupContainer || getContextPopupContainer} getPopupContainer={getPopupContainer || getContextPopupContainer}
treeMotion={null} treeMotion={null}
dropdownClassName={mergedDropdownClassName} dropdownClassName={mergedDropdownClassName}
choiceTransitionName={choiceTransitionName}
transitionName={transitionName}
/> />
); );
}} };
</SizeContext.Consumer>
);
};
render() { const TreeSelectRef = React.forwardRef(InternalTreeSelect) as <T extends DefaultValueType>(
return <ConfigConsumer>{this.renderTreeSelect}</ConfigConsumer>; props: TreeSelectProps<T> & { ref?: React.Ref<RefTreeSelectProps> },
} ) => React.ReactElement;
type InternalTreeSelectType = typeof TreeSelectRef;
interface TreeSelectInterface extends InternalTreeSelectType {
TreeNode: typeof TreeNode;
SHOW_ALL: typeof SHOW_ALL;
SHOW_PARENT: typeof SHOW_PARENT;
SHOW_CHILD: typeof SHOW_CHILD;
} }
const TreeSelect = TreeSelectRef as TreeSelectInterface;
TreeSelect.TreeNode = TreeNode;
TreeSelect.SHOW_ALL = SHOW_ALL;
TreeSelect.SHOW_PARENT = SHOW_PARENT;
TreeSelect.SHOW_CHILD = SHOW_CHILD;
export { TreeNode }; export { TreeNode };
export default TreeSelect; export default TreeSelect;