From ba5eb7570506bde47e43947280b8d16c785c68cc Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 21 Jun 2021 16:04:43 +0800 Subject: [PATCH 01/54] chore: option of it --- assets/index.less | 168 +----------- assets/list.less | 40 +++ assets/select.less | 3 + docs/demo/debug.md | 3 + examples/debug.tsx | 97 +++++++ package.json | 1 + src.bac/Cascader.tsx | 413 +++++++++++++++++++++++++++++ {src => src.bac}/Menus.tsx | 0 {src => src.bac}/index.ts | 0 {src => src.bac}/placements.ts | 0 {src => src.bac}/utils.ts | 0 src/Cascader.tsx | 471 +++++++-------------------------- src/OptionList/Column.tsx | 68 +++++ src/OptionList/index.tsx | 87 ++++++ src/context.ts | 7 + src/index.tsx | 3 + src/interface.ts | 18 ++ src/util.ts | 21 ++ 18 files changed, 856 insertions(+), 544 deletions(-) create mode 100644 assets/list.less create mode 100644 assets/select.less create mode 100644 docs/demo/debug.md create mode 100644 examples/debug.tsx create mode 100644 src.bac/Cascader.tsx rename {src => src.bac}/Menus.tsx (100%) rename {src => src.bac}/index.ts (100%) rename {src => src.bac}/placements.ts (100%) rename {src => src.bac}/utils.ts (100%) create mode 100644 src/OptionList/Column.tsx create mode 100644 src/OptionList/index.tsx create mode 100644 src/context.ts create mode 100644 src/index.tsx create mode 100644 src/interface.ts create mode 100644 src/util.ts diff --git a/assets/index.less b/assets/index.less index b93d9bab..eb16af54 100644 --- a/assets/index.less +++ b/assets/index.less @@ -1,166 +1,2 @@ -.effect() { - animation-duration: .3s; - animation-fill-mode: both; - transform-origin: 0 0; -} - -.rc-cascader { - font-size: 12px; - &-menus { - font-size: 12px; - overflow: hidden; - background: #fff; - position: absolute; - border: 1px solid #d9d9d9; - border-radius: 6px; - box-shadow: 0 0 4px rgba(0,0,0,0.17); - white-space: nowrap; - - &-hidden { - display: none; - } - - &.slide-up-enter, &.slide-up-appear { - .effect(); - opacity: 0; - animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); - animation-play-state: paused; - } - - &.slide-up-leave { - .effect(); - opacity: 1; - animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); - animation-play-state: paused; - } - - &.slide-up-enter.slide-up-enter-active&-placement-bottomLeft, - &.slide-up-appear.slide-up-appear-active&-placement-bottomLeft { - animation-name: SlideUpIn; - animation-play-state: running; - } - - &.slide-up-enter.slide-up-enter-active&-placement-topLeft, - &.slide-up-appear.slide-up-appear-active&-placement-topLeft { - animation-name: SlideDownIn; - animation-play-state: running; - } - - &.slide-up-leave.slide-up-leave-active&-placement-bottomLeft { - animation-name: SlideUpOut; - animation-play-state: running; - } - - &.slide-up-leave.slide-up-leave-active&-placement-topLeft { - animation-name: SlideDownOut; - animation-play-state: running; - } - } - &-menu { - display: inline-block; - width: 100px; - height: 192px; - list-style: none; - margin: 0; - padding: 0; - border-right: 1px solid #e9e9e9; - overflow: auto; - &:last-child { - border-right: 0; - } - } - &-menu-item { - height: 32px; - line-height: 32px; - padding: 0 16px; - cursor: pointer; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - transition: all 0.3s ease; - position: relative; - &:hover { - background: tint(#2db7f5, 90%); - } - &-disabled { - cursor: not-allowed; - color: #ccc; - &:hover { - background: transparent; - } - } - &-loading:after { - position: absolute; - right: 12px; - content: 'loading'; - color: #aaa; - font-style: italic; - } - &-active { - background: tint(#2db7f5, 80%); - &:hover { - background: tint(#2db7f5, 80%); - } - } - &-expand { - position: relative; - &-icon { - font-size: 12px; - color: #999; - position: absolute; - right: 16px; - line-height: 32px; - } - } - } -} - -@keyframes SlideUpIn { - 0% { - opacity: 0; - transform-origin: 0% 0%; - transform: scaleY(.8); - } - 100% { - opacity: 1; - transform-origin: 0% 0%; - transform: scaleY(1); - } -} -@keyframes SlideUpOut { - 0% { - opacity: 1; - transform-origin: 0% 0%; - transform: scaleY(1); - } - 100% { - opacity: 0; - transform-origin: 0% 0%; - transform: scaleY(0.8); - } -} - -@keyframes SlideDownIn { - 0% { - opacity: 0; - transform-origin: 0% 100%; - transform: scaleY(0.8); - } - 100% { - opacity: 1; - transform-origin: 0% 100%; - transform: scaleY(1); - } -} -@keyframes SlideDownOut { - 0% { - opacity: 1; - transform-origin: 0% 100%; - transform: scaleY(1); - } - 100% { - opacity: 0; - transform-origin: 0% 100%; - transform: scaleY(0.8); - } -} \ No newline at end of file +@import "./select.less"; +@import "./list.less"; \ No newline at end of file diff --git a/assets/list.less b/assets/list.less new file mode 100644 index 00000000..f2c3da9a --- /dev/null +++ b/assets/list.less @@ -0,0 +1,40 @@ +@select-prefix: ~'rc-cascader'; + +.@{select-prefix} { + &-dropdown { + min-height: auto; + } + + &-holder { + display: flex; + flex-wrap: nowrap; + } + + &-menu { + flex: none; + margin: 0; + padding: 0; + list-style: none; + border-left: 1px solid blue; + + &:first-child { + border-left: 0; + } + + &-item { + display: flex; + flex-wrap: nowrap; + + &-disabled { + opacity: 0.5; + } + } + } + + &-checkbox { + display: block; + width: 20px; + height: 20px; + border: 1px solid blue; + } +} diff --git a/assets/select.less b/assets/select.less new file mode 100644 index 00000000..8f2cd8fa --- /dev/null +++ b/assets/select.less @@ -0,0 +1,3 @@ +@import '~rc-select/assets/index'; + +@select-prefix: ~'rc-cascader'; \ No newline at end of file diff --git a/docs/demo/debug.md b/docs/demo/debug.md new file mode 100644 index 00000000..6dd26237 --- /dev/null +++ b/docs/demo/debug.md @@ -0,0 +1,3 @@ +## debug + + diff --git a/examples/debug.tsx b/examples/debug.tsx new file mode 100644 index 00000000..6d90fc8a --- /dev/null +++ b/examples/debug.tsx @@ -0,0 +1,97 @@ +/* eslint-disable no-console */ +import React from 'react'; +import '../assets/index.less'; +import Cascader from '../src'; + +const addressOptions = [ + { + label: '福建', + value: 'fj', + children: [ + { + label: '福州', + value: 'fuzhou', + children: [ + { + label: '马尾', + value: 'mawei', + }, + ], + }, + { + label: '泉州', + value: 'quanzhou', + }, + ], + }, + { + label: '浙江', + value: 'zj', + children: [ + { + label: '杭州', + value: 'hangzhou', + children: [ + { + label: '余杭', + value: 'yuhang', + }, + ], + }, + ], + }, + { + label: '北京', + value: 'bj', + children: [ + { + label: '朝阳区', + value: 'chaoyang', + }, + { + label: '海淀区', + value: 'haidian', + disabled: true, + }, + ], + }, + { + label: '顶层禁用', + value: 'disabled', + disabled: true, + children: [ + { + label: '看不见', + value: 'invisible', + }, + ], + }, +]; + +const Demo = () => { + const [multiple, setMultiple] = React.useState(true); + const [inputValue, setInputValue] = React.useState(''); + + const onChange = (value: any, selectedOptions: any) => { + console.log('[DEBUG] onChange:', value, selectedOptions); + setInputValue(selectedOptions.map((o) => o.label).join(', ')); + }; + + return ( + <> + + + + ); +}; + +export default Demo; diff --git a/package.json b/package.json index e3d67560..74344e99 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "dependencies": { "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", + "rc-tree-select": "^4.4.0-alpha.1", "rc-trigger": "^5.0.4", "rc-util": "^5.0.1", "warning": "^4.0.1" diff --git a/src.bac/Cascader.tsx b/src.bac/Cascader.tsx new file mode 100644 index 00000000..37360c84 --- /dev/null +++ b/src.bac/Cascader.tsx @@ -0,0 +1,413 @@ +import * as React from 'react'; +import type { BuildInPlacements, TriggerProps } from 'rc-trigger'; +import Trigger from 'rc-trigger'; +import warning from 'warning'; +import KeyCode from 'rc-util/lib/KeyCode'; +import arrayTreeFilter from 'array-tree-filter'; +import { isEqualArrays } from './utils'; +import Menus from './Menus'; +import BUILT_IN_PLACEMENTS from './placements'; + +export interface CascaderFieldNames { + value?: string | number; + label?: string; + children?: string; +} + +export type CascaderValueType = (string | number)[]; + +export interface CascaderOption { + value?: string | number; + label?: React.ReactNode; + disabled?: boolean; + isLeaf?: boolean; + loading?: boolean; + children?: CascaderOption[]; + [key: string]: any; +} + +export interface CascaderProps extends Pick { + value?: CascaderValueType; + defaultValue?: CascaderValueType; + options?: CascaderOption[]; + onChange?: (value: CascaderValueType, selectOptions: CascaderOption[]) => void; + onPopupVisibleChange?: (popupVisible: boolean) => void; + popupVisible?: boolean; + disabled?: boolean; + transitionName?: string; + popupClassName?: string; + popupPlacement?: string; + prefixCls?: string; + dropdownMenuColumnStyle?: React.CSSProperties; + dropdownRender?: (menu: React.ReactElement) => React.ReactElement; + builtinPlacements?: BuildInPlacements; + loadData?: (selectOptions: CascaderOption[]) => void; + changeOnSelect?: boolean; + children?: React.ReactElement; + onKeyDown?: (e: React.KeyboardEvent) => void; + expandTrigger?: string; + fieldNames?: CascaderFieldNames; + filedNames?: CascaderFieldNames; // typo but for compatibility + expandIcon?: React.ReactNode; + loadingIcon?: React.ReactNode; +} + +interface CascaderState { + popupVisible?: boolean; + activeValue?: CascaderValueType; + value?: CascaderValueType; + prevProps?: CascaderProps; +} + +class Cascader extends React.Component { + defaultFieldNames: Record; + + trigger: any; + + constructor(props: CascaderProps) { + super(props); + let initialValue = []; + if ('value' in props) { + initialValue = props.value || []; + } else if ('defaultValue' in props) { + initialValue = props.defaultValue || []; + } + + warning( + !('filedNames' in props), + '`filedNames` of Cascader is a typo usage and deprecated, please use `fieldNames` instead.', + ); + + this.state = { + popupVisible: props.popupVisible, + activeValue: initialValue, + value: initialValue, + prevProps: props, + }; + this.defaultFieldNames = { + label: 'label', + value: 'value', + children: 'children', + }; + } + + static defaultProps: CascaderProps = { + onChange: () => {}, + onPopupVisibleChange: () => {}, + disabled: false, + transitionName: '', + prefixCls: 'rc-cascader', + popupClassName: '', + popupPlacement: 'bottomLeft', + builtinPlacements: BUILT_IN_PLACEMENTS, + expandTrigger: 'click', + fieldNames: { label: 'label', value: 'value', children: 'children' }, + expandIcon: '>', + }; + + static getDerivedStateFromProps(nextProps: CascaderProps, prevState: CascaderState) { + const { prevProps = {} } = prevState; + const newState: CascaderState = { + prevProps: nextProps, + }; + + if ('value' in nextProps && !isEqualArrays(prevProps.value, nextProps.value)) { + newState.value = nextProps.value || []; + + // allow activeValue diff from value + // https://github.com/ant-design/ant-design/issues/2767 + if (!('loadData' in nextProps)) { + newState.activeValue = nextProps.value || []; + } + } + if ('popupVisible' in nextProps) { + newState.popupVisible = nextProps.popupVisible; + } + + return newState; + } + + getPopupDOMNode() { + return this.trigger.getPopupDomNode(); + } + + getFieldName(name: string): string { + const { defaultFieldNames } = this; + const { fieldNames, filedNames } = this.props; + if ('filedNames' in this.props) { + return filedNames[name] || defaultFieldNames[name]; // For old compatibility + } + return fieldNames[name] || defaultFieldNames[name]; + } + + getFieldNames(): CascaderFieldNames { + const { fieldNames, filedNames } = this.props; + if ('filedNames' in this.props) { + return filedNames; // For old compatibility + } + return fieldNames; + } + + getCurrentLevelOptions(): CascaderOption[] { + const { options = [] } = this.props; + const { activeValue = [] } = this.state; + const result = arrayTreeFilter( + options, + (o, level) => o[this.getFieldName('value')] === activeValue[level], + { childrenKeyName: this.getFieldName('children') }, + ); + if (result[result.length - 2]) { + return result[result.length - 2][this.getFieldName('children')]; + } + return [...options].filter((o) => !o.disabled); + } + + getActiveOptions(activeValue: CascaderValueType): CascaderOption[] { + return arrayTreeFilter( + this.props.options || [], + (o, level) => o[this.getFieldName('value')] === activeValue[level], + { childrenKeyName: this.getFieldName('children') }, + ); + } + + setPopupVisible = (popupVisible: boolean) => { + const { value } = this.state; + if (!('popupVisible' in this.props)) { + this.setState({ popupVisible }); + } + // sync activeValue with value when panel open + if (popupVisible && !this.state.popupVisible) { + this.setState({ + activeValue: value, + }); + } + this.props.onPopupVisibleChange(popupVisible); + }; + + handleChange = (options: CascaderOption[], { visible }, e: React.KeyboardEvent) => { + if (e.type !== 'keydown' || e.keyCode === KeyCode.ENTER) { + this.props.onChange( + options.map((o) => o[this.getFieldName('value')]), + options, + ); + this.setPopupVisible(visible); + } + }; + + handlePopupVisibleChange = (popupVisible: boolean) => { + this.setPopupVisible(popupVisible); + }; + + handleMenuSelect = ( + targetOption: CascaderOption, + menuIndex: number, + e: React.KeyboardEvent, + ) => { + // Keep focused state for keyboard support + const triggerNode = this.trigger.getRootDomNode(); + if (triggerNode && triggerNode.focus) { + triggerNode.focus(); + } + const { changeOnSelect, loadData, expandTrigger } = this.props; + if (!targetOption || targetOption.disabled) { + return; + } + let { activeValue } = this.state; + activeValue = activeValue.slice(0, menuIndex + 1); + activeValue[menuIndex] = targetOption[this.getFieldName('value')]; + const activeOptions = this.getActiveOptions(activeValue); + if (targetOption.isLeaf === false && !targetOption[this.getFieldName('children')] && loadData) { + if (changeOnSelect) { + this.handleChange(activeOptions, { visible: true }, e); + } + this.setState({ activeValue }); + loadData(activeOptions); + return; + } + const newState: CascaderState = {}; + if ( + !targetOption[this.getFieldName('children')] || + !targetOption[this.getFieldName('children')].length + ) { + this.handleChange(activeOptions, { visible: false }, e); + // set value to activeValue when select leaf option + newState.value = activeValue; + // add e.type judgement to prevent `onChange` being triggered by mouseEnter + } else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) { + if (expandTrigger === 'hover') { + this.handleChange(activeOptions, { visible: false }, e); + } else { + this.handleChange(activeOptions, { visible: true }, e); + } + // set value to activeValue on every select + newState.value = activeValue; + } + newState.activeValue = activeValue; + // not change the value by keyboard + if ('value' in this.props || (e.type === 'keydown' && e.keyCode !== KeyCode.ENTER)) { + delete newState.value; + } + this.setState(newState); + }; + + handleItemDoubleClick = () => { + const { changeOnSelect } = this.props; + if (changeOnSelect) { + this.setPopupVisible(false); + } + }; + + handleKeyDown = (e: React.KeyboardEvent) => { + const { children } = this.props; + // https://github.com/ant-design/ant-design/issues/6717 + // Don't bind keyboard support when children specify the onKeyDown + if (children && children.props.onKeyDown) { + children.props.onKeyDown(e); + return; + } + const activeValue = [...this.state.activeValue]; + const currentLevel = activeValue.length - 1 < 0 ? 0 : activeValue.length - 1; + const currentOptions = this.getCurrentLevelOptions(); + const currentIndex = currentOptions + .map((o) => o[this.getFieldName('value')]) + .indexOf(activeValue[currentLevel]); + if ( + e.keyCode !== KeyCode.DOWN && + e.keyCode !== KeyCode.UP && + e.keyCode !== KeyCode.LEFT && + e.keyCode !== KeyCode.RIGHT && + e.keyCode !== KeyCode.ENTER && + e.keyCode !== KeyCode.SPACE && + e.keyCode !== KeyCode.BACKSPACE && + e.keyCode !== KeyCode.ESC && + e.keyCode !== KeyCode.TAB + ) { + return; + } + // Press any keys above to reopen menu + if ( + !this.state.popupVisible && + e.keyCode !== KeyCode.BACKSPACE && + e.keyCode !== KeyCode.LEFT && + e.keyCode !== KeyCode.RIGHT && + e.keyCode !== KeyCode.ESC && + e.keyCode !== KeyCode.TAB + ) { + this.setPopupVisible(true); + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + return; + } + if (e.keyCode === KeyCode.DOWN || e.keyCode === KeyCode.UP) { + e.preventDefault(); + let nextIndex = currentIndex; + if (nextIndex !== -1) { + if (e.keyCode === KeyCode.DOWN) { + nextIndex += 1; + nextIndex = nextIndex >= currentOptions.length ? 0 : nextIndex; + } else { + nextIndex -= 1; + nextIndex = nextIndex < 0 ? currentOptions.length - 1 : nextIndex; + } + } else { + nextIndex = 0; + } + activeValue[currentLevel] = currentOptions[nextIndex][this.getFieldName('value')]; + } else if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.BACKSPACE) { + e.preventDefault(); + activeValue.splice(activeValue.length - 1, 1); + } else if (e.keyCode === KeyCode.RIGHT) { + e.preventDefault(); + if ( + currentOptions[currentIndex] && + currentOptions[currentIndex][this.getFieldName('children')] + ) { + activeValue.push( + currentOptions[currentIndex][this.getFieldName('children')][0][ + this.getFieldName('value') + ], + ); + } + } else if (e.keyCode === KeyCode.ESC || e.keyCode === KeyCode.TAB) { + this.setPopupVisible(false); + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + return; + } + if (!activeValue || activeValue.length === 0) { + this.setPopupVisible(false); + } + const activeOptions = this.getActiveOptions(activeValue); + const targetOption = activeOptions[activeOptions.length - 1]; + this.handleMenuSelect(targetOption, activeOptions.length - 1, e); + + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + }; + + saveTrigger = (node) => { + this.trigger = node; + }; + + render() { + const { + prefixCls, + transitionName, + popupClassName, + options = [], + disabled, + builtinPlacements, + popupPlacement, + children, + dropdownRender, + ...restProps + } = this.props; + // Did not show popup when there is no options + let menus =
; + let emptyMenuClassName = ''; + if (options && options.length > 0) { + menus = ( + + ); + } else { + emptyMenuClassName = ` ${prefixCls}-menus-empty`; + } + let popupNode = menus; + if (dropdownRender) { + popupNode = dropdownRender(menus); + } + return ( + + {React.cloneElement(children, { + onKeyDown: this.handleKeyDown, + tabIndex: disabled ? undefined : 0, + })} + + ); + } +} + +export default Cascader; diff --git a/src/Menus.tsx b/src.bac/Menus.tsx similarity index 100% rename from src/Menus.tsx rename to src.bac/Menus.tsx diff --git a/src/index.ts b/src.bac/index.ts similarity index 100% rename from src/index.ts rename to src.bac/index.ts diff --git a/src/placements.ts b/src.bac/placements.ts similarity index 100% rename from src/placements.ts rename to src.bac/placements.ts diff --git a/src/utils.ts b/src.bac/utils.ts similarity index 100% rename from src/utils.ts rename to src.bac/utils.ts diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 37360c84..1a71d7a2 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -1,413 +1,128 @@ import * as React from 'react'; import type { BuildInPlacements, TriggerProps } from 'rc-trigger'; -import Trigger from 'rc-trigger'; -import warning from 'warning'; -import KeyCode from 'rc-util/lib/KeyCode'; -import arrayTreeFilter from 'array-tree-filter'; -import { isEqualArrays } from './utils'; -import Menus from './Menus'; -import BUILT_IN_PLACEMENTS from './placements'; +import generate from 'rc-tree-select/lib/generate'; +import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; +import type { RefSelectProps } from 'rc-select/lib/generate'; +import OptionList from './OptionList'; +import type { CascaderValueType, DataNode } from './interface'; +import CascaderContext from './context'; +import { restoreCompatibleValue } from './util'; -export interface CascaderFieldNames { - value?: string | number; - label?: string; - children?: string; -} +const RefCascader = generate({ + prefixCls: 'rc-cascader', + optionList: OptionList, +}); -export type CascaderValueType = (string | number)[]; +// ====================================== Wrap ====================================== -export interface CascaderOption { - value?: string | number; - label?: React.ReactNode; - disabled?: boolean; - isLeaf?: boolean; - loading?: boolean; - children?: CascaderOption[]; - [key: string]: any; -} +interface BaseCascaderProps extends Pick { + options?: DataNode[]; -export interface CascaderProps extends Pick { - value?: CascaderValueType; - defaultValue?: CascaderValueType; - options?: CascaderOption[]; - onChange?: (value: CascaderValueType, selectOptions: CascaderOption[]) => void; - onPopupVisibleChange?: (popupVisible: boolean) => void; - popupVisible?: boolean; - disabled?: boolean; - transitionName?: string; - popupClassName?: string; - popupPlacement?: string; - prefixCls?: string; - dropdownMenuColumnStyle?: React.CSSProperties; - dropdownRender?: (menu: React.ReactElement) => React.ReactElement; - builtinPlacements?: BuildInPlacements; - loadData?: (selectOptions: CascaderOption[]) => void; changeOnSelect?: boolean; - children?: React.ReactElement; - onKeyDown?: (e: React.KeyboardEvent) => void; - expandTrigger?: string; - fieldNames?: CascaderFieldNames; - filedNames?: CascaderFieldNames; // typo but for compatibility - expandIcon?: React.ReactNode; - loadingIcon?: React.ReactNode; -} -interface CascaderState { - popupVisible?: boolean; - activeValue?: CascaderValueType; - value?: CascaderValueType; - prevProps?: CascaderProps; + // value?: CascaderValueType; + // defaultValue?: CascaderValueType; + + // onPopupVisibleChange?: (popupVisible: boolean) => void; + // popupVisible?: boolean; + // disabled?: boolean; + // transitionName?: string; + // popupClassName?: string; + // popupPlacement?: string; + // prefixCls?: string; + // dropdownMenuColumnStyle?: React.CSSProperties; + // dropdownRender?: (menu: React.ReactElement) => React.ReactElement; + // builtinPlacements?: BuildInPlacements; + // loadData?: (selectOptions: DataNode[]) => void; + + // children?: React.ReactElement; + // onKeyDown?: (e: React.KeyboardEvent) => void; + // expandTrigger?: string; + // // fieldNames?: CascaderFieldNames; + // // filedNames?: CascaderFieldNames; // typo but for compatibility + // expandIcon?: React.ReactNode; + // loadingIcon?: React.ReactNode; } -class Cascader extends React.Component { - defaultFieldNames: Record; +type OnSingleChange = (value: CascaderValueType, selectOptions: DataNode[]) => void; +type OnMultipleChange = (value: CascaderValueType[], selectOptions: DataNode[][]) => void; +interface SingleCascaderProps extends BaseCascaderProps { + multiple?: false; - trigger: any; + onChange?: OnSingleChange; +} - constructor(props: CascaderProps) { - super(props); - let initialValue = []; - if ('value' in props) { - initialValue = props.value || []; - } else if ('defaultValue' in props) { - initialValue = props.defaultValue || []; - } +interface MultipleCascaderProps extends BaseCascaderProps { + multiple: true; - warning( - !('filedNames' in props), - '`filedNames` of Cascader is a typo usage and deprecated, please use `fieldNames` instead.', - ); + onChange?: OnMultipleChange; +} - this.state = { - popupVisible: props.popupVisible, - activeValue: initialValue, - value: initialValue, - prevProps: props, - }; - this.defaultFieldNames = { - label: 'label', - value: 'value', - children: 'children', - }; - } +export type CascaderProps = SingleCascaderProps | MultipleCascaderProps; - static defaultProps: CascaderProps = { - onChange: () => {}, - onPopupVisibleChange: () => {}, - disabled: false, - transitionName: '', - prefixCls: 'rc-cascader', - popupClassName: '', - popupPlacement: 'bottomLeft', - builtinPlacements: BUILT_IN_PLACEMENTS, - expandTrigger: 'click', - fieldNames: { label: 'label', value: 'value', children: 'children' }, - expandIcon: '>', - }; +interface CascaderRef { + focus: () => void; + blur: () => void; +} - static getDerivedStateFromProps(nextProps: CascaderProps, prevState: CascaderState) { - const { prevProps = {} } = prevState; - const newState: CascaderState = { - prevProps: nextProps, - }; +const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref) => { + const { changeOnSelect, options, onChange, ...restProps } = props; + const { multiple } = restProps; - if ('value' in nextProps && !isEqualArrays(prevProps.value, nextProps.value)) { - newState.value = nextProps.value || []; + const context = React.useMemo(() => ({ changeOnSelect }), [changeOnSelect]); - // allow activeValue diff from value - // https://github.com/ant-design/ant-design/issues/2767 - if (!('loadData' in nextProps)) { - newState.activeValue = nextProps.value || []; - } - } - if ('popupVisible' in nextProps) { - newState.popupVisible = nextProps.popupVisible; - } + // ============================ Ref ============================= + const cascaderRef = React.useRef(); - return newState; - } + React.useImperativeHandle(ref, () => ({ + focus: cascaderRef.current.focus, + blur: cascaderRef.current.blur, + })); - getPopupDOMNode() { - return this.trigger.getPopupDomNode(); - } + const getEntityByValue = (value: React.Key): FlattenDataNode => + (cascaderRef.current as any).getEntityByValue(value); - getFieldName(name: string): string { - const { defaultFieldNames } = this; - const { fieldNames, filedNames } = this.props; - if ('filedNames' in this.props) { - return filedNames[name] || defaultFieldNames[name]; // For old compatibility - } - return fieldNames[name] || defaultFieldNames[name]; - } + // =========================== Change =========================== + const onInternalChange = (value: React.Key | React.Key[]) => { + if (onChange) { + const valueList = (multiple ? value : [value]) as React.Key[]; - getFieldNames(): CascaderFieldNames { - const { fieldNames, filedNames } = this.props; - if ('filedNames' in this.props) { - return filedNames; // For old compatibility - } - return fieldNames; - } + const pathList: CascaderValueType[] = []; + const optionsList: DataNode[][] = []; - getCurrentLevelOptions(): CascaderOption[] { - const { options = [] } = this.props; - const { activeValue = [] } = this.state; - const result = arrayTreeFilter( - options, - (o, level) => o[this.getFieldName('value')] === activeValue[level], - { childrenKeyName: this.getFieldName('children') }, - ); - if (result[result.length - 2]) { - return result[result.length - 2][this.getFieldName('children')]; - } - return [...options].filter((o) => !o.disabled); - } - - getActiveOptions(activeValue: CascaderValueType): CascaderOption[] { - return arrayTreeFilter( - this.props.options || [], - (o, level) => o[this.getFieldName('value')] === activeValue[level], - { childrenKeyName: this.getFieldName('children') }, - ); - } + const valueEntities = valueList.map(getEntityByValue).filter((entity) => entity); - setPopupVisible = (popupVisible: boolean) => { - const { value } = this.state; - if (!('popupVisible' in this.props)) { - this.setState({ popupVisible }); - } - // sync activeValue with value when panel open - if (popupVisible && !this.state.popupVisible) { - this.setState({ - activeValue: value, + valueEntities.forEach((entity) => { + const { path, options: valueOptions } = restoreCompatibleValue(entity); + pathList.push(path); + optionsList.push(valueOptions); }); - } - this.props.onPopupVisibleChange(popupVisible); - }; - - handleChange = (options: CascaderOption[], { visible }, e: React.KeyboardEvent) => { - if (e.type !== 'keydown' || e.keyCode === KeyCode.ENTER) { - this.props.onChange( - options.map((o) => o[this.getFieldName('value')]), - options, - ); - this.setPopupVisible(visible); - } - }; - handlePopupVisibleChange = (popupVisible: boolean) => { - this.setPopupVisible(popupVisible); - }; + console.log('==>>>>>', pathList, optionsList); - handleMenuSelect = ( - targetOption: CascaderOption, - menuIndex: number, - e: React.KeyboardEvent, - ) => { - // Keep focused state for keyboard support - const triggerNode = this.trigger.getRootDomNode(); - if (triggerNode && triggerNode.focus) { - triggerNode.focus(); - } - const { changeOnSelect, loadData, expandTrigger } = this.props; - if (!targetOption || targetOption.disabled) { - return; - } - let { activeValue } = this.state; - activeValue = activeValue.slice(0, menuIndex + 1); - activeValue[menuIndex] = targetOption[this.getFieldName('value')]; - const activeOptions = this.getActiveOptions(activeValue); - if (targetOption.isLeaf === false && !targetOption[this.getFieldName('children')] && loadData) { - if (changeOnSelect) { - this.handleChange(activeOptions, { visible: true }, e); - } - this.setState({ activeValue }); - loadData(activeOptions); - return; - } - const newState: CascaderState = {}; - if ( - !targetOption[this.getFieldName('children')] || - !targetOption[this.getFieldName('children')].length - ) { - this.handleChange(activeOptions, { visible: false }, e); - // set value to activeValue when select leaf option - newState.value = activeValue; - // add e.type judgement to prevent `onChange` being triggered by mouseEnter - } else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) { - if (expandTrigger === 'hover') { - this.handleChange(activeOptions, { visible: false }, e); - } else { - this.handleChange(activeOptions, { visible: true }, e); - } - // set value to activeValue on every select - newState.value = activeValue; - } - newState.activeValue = activeValue; - // not change the value by keyboard - if ('value' in this.props || (e.type === 'keydown' && e.keyCode !== KeyCode.ENTER)) { - delete newState.value; - } - this.setState(newState); - }; - - handleItemDoubleClick = () => { - const { changeOnSelect } = this.props; - if (changeOnSelect) { - this.setPopupVisible(false); - } - }; - - handleKeyDown = (e: React.KeyboardEvent) => { - const { children } = this.props; - // https://github.com/ant-design/ant-design/issues/6717 - // Don't bind keyboard support when children specify the onKeyDown - if (children && children.props.onKeyDown) { - children.props.onKeyDown(e); - return; - } - const activeValue = [...this.state.activeValue]; - const currentLevel = activeValue.length - 1 < 0 ? 0 : activeValue.length - 1; - const currentOptions = this.getCurrentLevelOptions(); - const currentIndex = currentOptions - .map((o) => o[this.getFieldName('value')]) - .indexOf(activeValue[currentLevel]); - if ( - e.keyCode !== KeyCode.DOWN && - e.keyCode !== KeyCode.UP && - e.keyCode !== KeyCode.LEFT && - e.keyCode !== KeyCode.RIGHT && - e.keyCode !== KeyCode.ENTER && - e.keyCode !== KeyCode.SPACE && - e.keyCode !== KeyCode.BACKSPACE && - e.keyCode !== KeyCode.ESC && - e.keyCode !== KeyCode.TAB - ) { - return; - } - // Press any keys above to reopen menu - if ( - !this.state.popupVisible && - e.keyCode !== KeyCode.BACKSPACE && - e.keyCode !== KeyCode.LEFT && - e.keyCode !== KeyCode.RIGHT && - e.keyCode !== KeyCode.ESC && - e.keyCode !== KeyCode.TAB - ) { - this.setPopupVisible(true); - if (this.props.onKeyDown) { - this.props.onKeyDown(e); - } - return; - } - if (e.keyCode === KeyCode.DOWN || e.keyCode === KeyCode.UP) { - e.preventDefault(); - let nextIndex = currentIndex; - if (nextIndex !== -1) { - if (e.keyCode === KeyCode.DOWN) { - nextIndex += 1; - nextIndex = nextIndex >= currentOptions.length ? 0 : nextIndex; - } else { - nextIndex -= 1; - nextIndex = nextIndex < 0 ? currentOptions.length - 1 : nextIndex; - } + if (multiple) { + (onChange as OnMultipleChange)(pathList, optionsList); } else { - nextIndex = 0; - } - activeValue[currentLevel] = currentOptions[nextIndex][this.getFieldName('value')]; - } else if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.BACKSPACE) { - e.preventDefault(); - activeValue.splice(activeValue.length - 1, 1); - } else if (e.keyCode === KeyCode.RIGHT) { - e.preventDefault(); - if ( - currentOptions[currentIndex] && - currentOptions[currentIndex][this.getFieldName('children')] - ) { - activeValue.push( - currentOptions[currentIndex][this.getFieldName('children')][0][ - this.getFieldName('value') - ], - ); + (onChange as OnSingleChange)(pathList[0], optionsList[0]); } - } else if (e.keyCode === KeyCode.ESC || e.keyCode === KeyCode.TAB) { - this.setPopupVisible(false); - if (this.props.onKeyDown) { - this.props.onKeyDown(e); - } - return; - } - if (!activeValue || activeValue.length === 0) { - this.setPopupVisible(false); - } - const activeOptions = this.getActiveOptions(activeValue); - const targetOption = activeOptions[activeOptions.length - 1]; - this.handleMenuSelect(targetOption, activeOptions.length - 1, e); - - if (this.props.onKeyDown) { - this.props.onKeyDown(e); } }; - saveTrigger = (node) => { - this.trigger = node; - }; - - render() { - const { - prefixCls, - transitionName, - popupClassName, - options = [], - disabled, - builtinPlacements, - popupPlacement, - children, - dropdownRender, - ...restProps - } = this.props; - // Did not show popup when there is no options - let menus =
; - let emptyMenuClassName = ''; - if (options && options.length > 0) { - menus = ( - - ); - } else { - emptyMenuClassName = ` ${prefixCls}-menus-empty`; - } - let popupNode = menus; - if (dropdownRender) { - popupNode = dropdownRender(menus); - } - return ( - + - {React.cloneElement(children, { - onKeyDown: this.handleKeyDown, - tabIndex: disabled ? undefined : 0, - })} - - ); - } -} + dropdownMatchSelectWidth={false} + treeData={options} + onChange={onInternalChange} + showCheckedStrategy={RefCascader.SHOW_PARENT} + /> + + ); +}); + +Cascader.displayName = 'Cascader'; export default Cascader; diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx new file mode 100644 index 00000000..eb296d13 --- /dev/null +++ b/src/OptionList/Column.tsx @@ -0,0 +1,68 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import type { DataNode } from '../interface'; + +export interface ColumnProps { + prefixCls: string; + index: number; + multiple?: boolean; + options: DataNode[]; + /** Current Column opened item key */ + openKey?: React.Key; + onClick: (index: number, value: React.Key, isLeaf: boolean) => void; +} + +export default function Column({ prefixCls, index, multiple, options, onClick }: ColumnProps) { + const menuPrefixCls = `${prefixCls}-menu`; + const menuItemPrefixCls = `${prefixCls}-menu-item`; + + const clickLockRef = React.useRef(false); + + return ( +
    + {options.map((option) => { + const { isLeaf, children, disabled } = option; + + // >>>>> Selection + const triggerSelect = () => { + if (!disabled) { + const isMergedLeaf = isLeaf !== undefined ? isLeaf : !children?.length; + + onClick(index, option.value, isMergedLeaf); + } + }; + + // >>>>> Render + return ( +
  • + {multiple && ( + { + clickLockRef.current = true; + triggerSelect(); + Promise.resolve().then(() => { + clickLockRef.current = false; + }); + }} + /> + )} + {option.label} +
  • + ); + })} +
+ ); +} diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx new file mode 100644 index 00000000..ffa5f88f --- /dev/null +++ b/src/OptionList/index.tsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import type { OptionListProps, RefOptionListProps } from 'rc-select/lib/OptionList'; +import List from 'rc-virtual-list'; +import type { DataNode } from '../interface'; +import Column from './Column'; +import CascaderContext from '../context'; + +const RefOptionList = React.forwardRef>( + (props, ref) => { + const { changeOnSelect } = React.useContext(CascaderContext); + const { prefixCls, options, values, onSelect, multiple } = props; + console.log('OptionList Props:', props); + + const [openPath, setOpenPath] = React.useState(() => { + if (!multiple && values.size) { + const firstValue = Array.from(values.values())[0]; + + // TODO: Back of path + return []; + } + + return []; + }); + + // =========================== Path =========================== + const onPathClick = (index: number, pathValue: React.Key, isLeaf: boolean) => { + const nextOpenPath = [...openPath.slice(0, index), pathValue]; + setOpenPath(nextOpenPath); + + if (isLeaf || changeOnSelect) { + onSelect(pathValue, { selected: !values.has(pathValue) }); + } + }; + + const getPathList = (pathList: React.Key[]) => { + let currentOptions = options; + + for (let i = 0; i < pathList.length; i += 1) { + currentOptions = (currentOptions || []).find( + (option) => option.value === pathList[i], + ).children; + } + + return currentOptions; + }; + + // ========================= Keyboard ========================= + React.useImperativeHandle(ref, () => ({} as any)); + + // ========================== Render ========================== + // >>>>> Columns + const columnProps = { + ...props, + onClick: onPathClick, + }; + + const columnNodes: React.ReactElement[] = [ + , + ]; + + openPath.forEach((_, index) => { + const mergedIndex = index + 1; + const subOptions = getPathList(openPath.slice(0, mergedIndex)); + + if (subOptions) { + columnNodes.push( + , + ); + } + }); + + // >>>>> Render + return ( + <> +
{columnNodes}
+ + ); + }, +); + +export default RefOptionList; \ No newline at end of file diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 00000000..4f76b2d2 --- /dev/null +++ b/src/context.ts @@ -0,0 +1,7 @@ +import * as React from 'react'; + +const CascaderContext = React.createContext({ + changeOnSelect: false, +}); + +export default CascaderContext; \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 00000000..9e19ae0b --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,3 @@ +import Cascader from './Cascader' + +export default Cascader; \ No newline at end of file diff --git a/src/interface.ts b/src/interface.ts new file mode 100644 index 00000000..3085dc80 --- /dev/null +++ b/src/interface.ts @@ -0,0 +1,18 @@ +import type * as React from 'react'; + +export type CascaderValueType = React.Key[]; + +export interface DataNode { + label: React.ReactNode; + value: string; + disabled?: boolean; + children?: DataNode[]; + isLeaf?: boolean; +} + +export interface FlattenDataNode { + key: React.Key; + data: DataNode; + path: React.Key[]; + parent?: FlattenDataNode; +} diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 00000000..8e4cd70f --- /dev/null +++ b/src/util.ts @@ -0,0 +1,21 @@ +import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; +import type { CascaderValueType, DataNode } from './interface'; + +export function restoreCompatibleValue(entity: FlattenDataNode): { + path: CascaderValueType; + options: DataNode[]; +} { + const path: CascaderValueType = []; + const options: DataNode[] = []; + + let current = entity; + + while (current) { + path.unshift(current.data.value); + options.unshift(current.data as DataNode); + + current = current.parent; + } + + return { path, options }; +} From ec885a513e57dcb7cfc03359bc0fe2a2e6694475 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 21 Jun 2021 18:14:51 +0800 Subject: [PATCH 02/54] chore: multiple --- assets/list.less | 36 +++++++++++++++++---- src/Cascader.tsx | 3 +- src/OptionList/Column.tsx | 68 ++++++++++++++++++++++++++++----------- src/OptionList/index.tsx | 33 +++++++++++++------ 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/assets/list.less b/assets/list.less index f2c3da9a..8f46c9db 100644 --- a/assets/list.less +++ b/assets/list.less @@ -28,13 +28,35 @@ &-disabled { opacity: 0.5; } - } - } - &-checkbox { - display: block; - width: 20px; - height: 20px; - border: 1px solid blue; + &-checkbox { + position: relative; + display: block; + flex: none; + width: 20px; + height: 20px; + border: 1px solid blue; + + &::after { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + content: ''; + } + + &-checked::after { + content: '✔️'; + } + + &-indeterminate::after { + content: '➖'; + } + } + + &-content { + flex: auto; + } + } } } diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 1a71d7a2..7b71a80f 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -98,7 +98,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref>>>>', pathList, optionsList); + // console.log('==>>>>>', pathList, optionsList); if (multiple) { (onChange as OnMultipleChange)(pathList, optionsList); @@ -116,6 +116,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index eb296d13..520dc555 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -9,57 +9,87 @@ export interface ColumnProps { options: DataNode[]; /** Current Column opened item key */ openKey?: React.Key; - onClick: (index: number, value: React.Key, isLeaf: boolean) => void; + onSelect: (value: React.Key) => void; + onOpen: (index: number, value: React.Key) => void; + changeOnSelect: boolean; + checkedSet: Set; + halfCheckedSet: Set; } -export default function Column({ prefixCls, index, multiple, options, onClick }: ColumnProps) { +export default function Column({ + prefixCls, + index, + multiple, + options, + onSelect, + onOpen, + changeOnSelect, + checkedSet, + halfCheckedSet, +}: ColumnProps) { const menuPrefixCls = `${prefixCls}-menu`; const menuItemPrefixCls = `${prefixCls}-menu-item`; - - const clickLockRef = React.useRef(false); + const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`; return (
    {options.map((option) => { - const { isLeaf, children, disabled } = option; + const { isLeaf, children, disabled, value } = option; + const isMergedLeaf = isLeaf !== undefined ? isLeaf : !children?.length; + + // >>>>> checked + const checked = checkedSet.has(value); + + // >>>>> Open + const triggerOpen = () => { + if (!disabled && !isMergedLeaf) { + onOpen(index, value); + } + }; // >>>>> Selection const triggerSelect = () => { - if (!disabled) { - const isMergedLeaf = isLeaf !== undefined ? isLeaf : !children?.length; - - onClick(index, option.value, isMergedLeaf); + if (!disabled && (isMergedLeaf || changeOnSelect || multiple)) { + onSelect(value); } }; // >>>>> Render return (
  • {}} > {multiple && ( { - clickLockRef.current = true; triggerSelect(); - Promise.resolve().then(() => { - clickLockRef.current = false; - }); }} /> )} - {option.label} +
    { + triggerOpen(); + if (!multiple) { + triggerSelect(); + } + }} + > + {option.label} +
  • ); })} diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index ffa5f88f..1961770f 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import type { OptionListProps, RefOptionListProps } from 'rc-select/lib/OptionList'; +import { SelectContext } from 'rc-tree-select/lib/Context'; import List from 'rc-virtual-list'; import type { DataNode } from '../interface'; import Column from './Column'; @@ -8,12 +9,20 @@ import CascaderContext from '../context'; const RefOptionList = React.forwardRef>( (props, ref) => { const { changeOnSelect } = React.useContext(CascaderContext); - const { prefixCls, options, values, onSelect, multiple } = props; - console.log('OptionList Props:', props); + const { prefixCls, options, onSelect, multiple } = props; + const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); + // console.log('OptionList Props:', props, checkedKeys); + console.log('OptionList CheckedKeys:', checkedKeys); + + // ========================== Values ========================== + const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]); + const halfCheckedSet = React.useMemo(() => new Set(halfCheckedKeys), [halfCheckedKeys]); + + // =========================== Open =========================== const [openPath, setOpenPath] = React.useState(() => { - if (!multiple && values.size) { - const firstValue = Array.from(values.values())[0]; + if (!multiple && checkedSet.size) { + const firstValue = Array.from(checkedSet.values())[0]; // TODO: Back of path return []; @@ -23,13 +32,13 @@ const RefOptionList = React.forwardRef { + const onPathOpen = (index: number, pathValue: React.Key) => { const nextOpenPath = [...openPath.slice(0, index), pathValue]; setOpenPath(nextOpenPath); + }; - if (isLeaf || changeOnSelect) { - onSelect(pathValue, { selected: !values.has(pathValue) }); - } + const onPathSelect = (pathValue: React.Key) => { + onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); }; const getPathList = (pathList: React.Key[]) => { @@ -51,7 +60,11 @@ const RefOptionList = React.forwardRef>>>> Columns const columnProps = { ...props, - onClick: onPathClick, + onOpen: onPathOpen, + onSelect: onPathSelect, + checkedSet, + halfCheckedSet, + changeOnSelect, }; const columnNodes: React.ReactElement[] = [ @@ -84,4 +97,4 @@ const RefOptionList = React.forwardRef Date: Tue, 22 Jun 2021 10:40:18 +0800 Subject: [PATCH 03/54] feat: Add active back --- assets/list.less | 8 ++++++++ src/Cascader.tsx | 2 -- src/OptionList/Column.tsx | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/assets/list.less b/assets/list.less index 8f46c9db..ff86e412 100644 --- a/assets/list.less +++ b/assets/list.less @@ -25,6 +25,14 @@ display: flex; flex-wrap: nowrap; + &-active { + background: rgba(0, 255, 0, 0.1); + } + + &-selected { + background: rgba(0, 0, 255, 0.1); + } + &-disabled { opacity: 0.5; } diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 7b71a80f..99add9e1 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -98,8 +98,6 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref>>>>', pathList, optionsList); - if (multiple) { (onChange as OnMultipleChange)(pathList, optionsList); } else { diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 520dc555..306f6e18 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -21,6 +21,7 @@ export default function Column({ index, multiple, options, + openKey, onSelect, onOpen, changeOnSelect, @@ -59,6 +60,8 @@ export default function Column({
  • { triggerOpen(); - if (!multiple) { + if (!multiple || isMergedLeaf) { triggerSelect(); } }} From 3d098b57ef5fde509b4ba700e5cf98352e36d829 Mon Sep 17 00:00:00 2001 From: zombiej Date: Tue, 22 Jun 2021 10:50:45 +0800 Subject: [PATCH 04/54] chore: Dropdown should place it --- src/Cascader.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 99add9e1..ef9345b8 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -113,6 +113,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref Date: Tue, 22 Jun 2021 16:32:29 +0800 Subject: [PATCH 05/54] chore: Back of customize children --- src/Cascader.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index ef9345b8..660a7d68 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -17,6 +17,7 @@ const RefCascader = generate({ interface BaseCascaderProps extends Pick { options?: DataNode[]; + children?: React.ReactElement; changeOnSelect?: boolean; @@ -35,7 +36,6 @@ interface BaseCascaderProps extends Pick { // builtinPlacements?: BuildInPlacements; // loadData?: (selectOptions: DataNode[]) => void; - // children?: React.ReactElement; // onKeyDown?: (e: React.KeyboardEvent) => void; // expandTrigger?: string; // // fieldNames?: CascaderFieldNames; @@ -66,7 +66,7 @@ interface CascaderRef { } const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref) => { - const { changeOnSelect, options, onChange, ...restProps } = props; + const { changeOnSelect, children, options, onChange, ...restProps } = props; const { multiple } = restProps; const context = React.useMemo(() => ({ changeOnSelect }), [changeOnSelect]); @@ -120,6 +120,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref children, + }} /> ); From e1bcf9b1a62a2ac199714e601a40b0f83424435a Mon Sep 17 00:00:00 2001 From: zombiej Date: Tue, 22 Jun 2021 19:36:42 +0800 Subject: [PATCH 06/54] feat: Use labelRender --- package.json | 2 +- src/Cascader.tsx | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 74344e99..6272a099 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "dependencies": { "@babel/runtime": "^7.12.5", "array-tree-filter": "^2.1.0", - "rc-tree-select": "^4.4.0-alpha.1", + "rc-tree-select": "^4.4.0-alpha.2", "rc-trigger": "^5.0.4", "rc-util": "^5.0.1", "warning": "^4.0.1" diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 660a7d68..2b5ec23b 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -82,6 +82,22 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref (cascaderRef.current as any).getEntityByValue(value); + // =========================== Value ============================ + const labelRender = (entity: FlattenDataNode) => { + if (multiple) { + return entity.data.label; + } + + const pathLabel: React.ReactNode[] = []; + let current = entity; + while (current) { + pathLabel.unshift(current.data.label); + current = current.parent; + } + + return pathLabel.join('>'); + }; + // =========================== Change =========================== const onInternalChange = (value: React.Key | React.Key[]) => { if (onChange) { @@ -120,6 +136,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref children, }} From 7f13db699cd557d5e0c683f4af2436a6aef17005 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 23 Jun 2021 10:50:10 +0800 Subject: [PATCH 07/54] chore: connect value --- examples/debug.tsx | 13 +++++++-- src/Cascader.tsx | 56 ++++++++++++++++++++++++++++++------ src/OptionList/index.tsx | 2 -- src/hooks/useUpdateEffect.ts | 18 ++++++++++++ 4 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 src/hooks/useUpdateEffect.ts diff --git a/examples/debug.tsx b/examples/debug.tsx index 6d90fc8a..e672affd 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -68,12 +68,15 @@ const addressOptions = [ }, ]; +const defaultValue = ['fj', 'fuzhou', 'mawei']; + const Demo = () => { const [multiple, setMultiple] = React.useState(true); const [inputValue, setInputValue] = React.useState(''); const onChange = (value: any, selectedOptions: any) => { - console.log('[DEBUG] onChange:', value, selectedOptions); + console.log('[DEBUG] onChange - value:', value); + console.log('[DEBUG] onChange - selectedOptions:', selectedOptions); setInputValue(selectedOptions.map((o) => o.label).join(', ')); }; @@ -89,7 +92,13 @@ const Demo = () => { /> Multiple - + ); }; diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 2b5ec23b..f2cd3546 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -7,6 +7,7 @@ import OptionList from './OptionList'; import type { CascaderValueType, DataNode } from './interface'; import CascaderContext from './context'; import { restoreCompatibleValue } from './util'; +import useUpdateEffect from './hooks/useUpdateEffect'; const RefCascader = generate({ prefixCls: 'rc-cascader', @@ -19,10 +20,13 @@ interface BaseCascaderProps extends Pick { options?: DataNode[]; children?: React.ReactElement; + // Value + value?: CascaderValueType | CascaderValueType[]; + defaultValue?: CascaderValueType | CascaderValueType[]; + changeOnSelect?: boolean; - // value?: CascaderValueType; - // defaultValue?: CascaderValueType; + allowClear?: boolean; // onPopupVisibleChange?: (popupVisible: boolean) => void; // popupVisible?: boolean; @@ -46,6 +50,7 @@ interface BaseCascaderProps extends Pick { type OnSingleChange = (value: CascaderValueType, selectOptions: DataNode[]) => void; type OnMultipleChange = (value: CascaderValueType[], selectOptions: DataNode[][]) => void; + interface SingleCascaderProps extends BaseCascaderProps { multiple?: false; @@ -66,7 +71,7 @@ interface CascaderRef { } const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref) => { - const { changeOnSelect, children, options, onChange, ...restProps } = props; + const { changeOnSelect, children, options, onChange, value, defaultValue, ...restProps } = props; const { multiple } = restProps; const context = React.useMemo(() => ({ changeOnSelect }), [changeOnSelect]); @@ -79,10 +84,35 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref - (cascaderRef.current as any).getEntityByValue(value); + const getEntityByValue = (val: React.Key): FlattenDataNode => + (cascaderRef.current as any).getEntityByValue(val); // =========================== Value ============================ + /** + * Always pass props value to last value unit: + * - single: ['light', 'little'] => ['little'] + * - multiple: [['light', 'little'], ['bamboo']] => ['little', 'bamboo'] + */ + const parseToInternalValue = ( + propValue?: CascaderValueType | CascaderValueType[], + ): React.Key[] => { + let propValueList: CascaderValueType[] = []; + if (propValue) { + propValueList = (multiple ? propValue : [propValue]) as CascaderValueType[]; + } + + return propValueList.map((pathValue) => pathValue[pathValue.length - 1]); + }; + + const [internalValue, setInternalValue] = React.useState(() => + parseToInternalValue(value || defaultValue), + ); + + useUpdateEffect(() => { + setInternalValue(parseToInternalValue(value)); + }, [value]); + + // =========================== Label ============================ const labelRender = (entity: FlattenDataNode) => { if (multiple) { return entity.data.label; @@ -99,9 +129,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { + const onInternalChange = (newValue: any /** Not care current type */) => { if (onChange) { - const valueList = (multiple ? value : [value]) as React.Key[]; + const valueList = (multiple ? newValue : [newValue]) as React.Key[]; const pathList: CascaderValueType[] = []; const optionsList: DataNode[][] = []; @@ -114,10 +144,19 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref'); + if (multiple) { (onChange as OnMultipleChange)(pathList, optionsList); } else { - (onChange as OnSingleChange)(pathList[0], optionsList[0]); + // TODO: This should return null as other component. + // But its a breaking change and we should keep the logic. + (onChange as OnSingleChange)(pathList[0] || [], optionsList[0] || []); } } }; @@ -128,6 +167,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref new Set(checkedKeys), [checkedKeys]); diff --git a/src/hooks/useUpdateEffect.ts b/src/hooks/useUpdateEffect.ts new file mode 100644 index 00000000..fe9e88a0 --- /dev/null +++ b/src/hooks/useUpdateEffect.ts @@ -0,0 +1,18 @@ +import * as React from 'react'; + +/** + * Work like `componentDidUpdate` + */ +export default function useUpdateEffect(updater: () => void, condition?: any[]) { + const prepareRef = React.useRef(true); + + React.useEffect(() => { + if (prepareRef.current) { + prepareRef.current = false; + return; + } + + // eslint-disable-next-line consistent-return + return updater(); + }, condition); +} From c2d56e7045200802dc6f00929c6bf0293be27b89 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 23 Jun 2021 11:22:05 +0800 Subject: [PATCH 08/54] feat: Auto open path of select value --- examples/debug.tsx | 2 +- src/Cascader.tsx | 24 +++++++++++++----------- src/OptionList/Column.tsx | 2 +- src/OptionList/index.tsx | 29 +++++++++++++++++++---------- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/examples/debug.tsx b/examples/debug.tsx index e672affd..925b9610 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -71,7 +71,7 @@ const addressOptions = [ const defaultValue = ['fj', 'fuzhou', 'mawei']; const Demo = () => { - const [multiple, setMultiple] = React.useState(true); + const [multiple, setMultiple] = React.useState(false); const [inputValue, setInputValue] = React.useState(''); const onChange = (value: any, selectedOptions: any) => { diff --git a/src/Cascader.tsx b/src/Cascader.tsx index f2cd3546..bbc89fde 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { BuildInPlacements, TriggerProps } from 'rc-trigger'; +import type { TriggerProps } from 'rc-trigger'; import generate from 'rc-tree-select/lib/generate'; import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; import type { RefSelectProps } from 'rc-select/lib/generate'; @@ -9,6 +9,15 @@ import CascaderContext from './context'; import { restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; +/** + * `rc-cascader` is much like `rc-tree-select` but API is very different. + * It's caused that component developer is not same person + * and we do not rice the API naming standard at that time. + * + * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API. + * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version. + */ + const RefCascader = generate({ prefixCls: 'rc-cascader', optionList: OptionList, @@ -118,14 +127,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref'); + return restoreCompatibleValue(entity) + .options.map((opt) => opt.label) + .join('>'); }; // =========================== Change =========================== @@ -149,8 +153,6 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref'); - if (multiple) { (onChange as OnMultipleChange)(pathList, optionsList); } else { diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 306f6e18..db658403 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -65,7 +65,7 @@ export default function Column({ [`${menuItemPrefixCls}-disabled`]: disabled, })} role="menuitemcheckbox" - onClick={() => {}} + aria-checked={checked} > {multiple && ( >( (props, ref) => { const { changeOnSelect } = React.useContext(CascaderContext); - const { prefixCls, options, onSelect, multiple } = props; + const { prefixCls, options, onSelect, multiple, open, flattenOptions } = props; const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); @@ -18,16 +19,24 @@ const RefOptionList = React.forwardRef new Set(halfCheckedKeys), [halfCheckedKeys]); // =========================== Open =========================== - const [openPath, setOpenPath] = React.useState(() => { - if (!multiple && checkedSet.size) { - const firstValue = Array.from(checkedSet.values())[0]; - - // TODO: Back of path - return []; + const [openPath, setOpenPath] = React.useState([]); + + React.useEffect(() => { + if (open) { + let nextOpenPath: React.Key[] = []; + + if (!multiple && checkedKeys.length) { + const entity = flattenOptions.find( + (flattenOption) => flattenOption.data.value === checkedKeys[0], + ); + + if (entity) { + nextOpenPath = restoreCompatibleValue(entity as any).path; + } + } + setOpenPath(nextOpenPath); } - - return []; - }); + }, [open]); // =========================== Path =========================== const onPathOpen = (index: number, pathValue: React.Key) => { From 64125331ea192539a286b642a8e3399ef2d04ba7 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 23 Jun 2021 11:28:00 +0800 Subject: [PATCH 09/54] chore: onDropdownChange --- src/Cascader.tsx | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index bbc89fde..80fad952 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -16,6 +16,9 @@ import useUpdateEffect from './hooks/useUpdateEffect'; * * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API. * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version. + * + * Remove: + * - onPopupVisibleChange */ const RefCascader = generate({ @@ -37,7 +40,11 @@ interface BaseCascaderProps extends Pick { allowClear?: boolean; - // onPopupVisibleChange?: (popupVisible: boolean) => void; + /** @deprecated Use `onDropdownVisibleChange` instead */ + onPopupVisibleChange?: (popupVisible: boolean) => void; + + onDropdownVisibleChange?: (popupVisible: boolean) => void; + // popupVisible?: boolean; // disabled?: boolean; // transitionName?: string; @@ -80,7 +87,17 @@ interface CascaderRef { } const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref) => { - const { changeOnSelect, children, options, onChange, value, defaultValue, ...restProps } = props; + const { + changeOnSelect, + children, + options, + onChange, + value, + defaultValue, + onDropdownVisibleChange, + onPopupVisibleChange, + ...restProps + } = props; const { multiple } = restProps; const context = React.useMemo(() => ({ changeOnSelect }), [changeOnSelect]); @@ -163,6 +180,12 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { + onDropdownVisibleChange?.(nextVisible); + onPopupVisibleChange?.(nextVisible); + }; + // =========================== Render =========================== return ( @@ -178,6 +201,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref children, From 7d908508f5881f75768591a79935d33b4919864d Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 23 Jun 2021 11:57:44 +0800 Subject: [PATCH 10/54] chore: search open --- .prettierrc | 9 +++++ examples/debug.tsx | 4 ++- src/Cascader.tsx | 72 +++++++++++++++++++++++++++++++--------- src/OptionList/index.tsx | 19 ++++++++--- 4 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..7f9f8713 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "endOfLine": "lf", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "printWidth": 100, + "arrowParens": "avoid" +} \ No newline at end of file diff --git a/examples/debug.tsx b/examples/debug.tsx index 925b9610..2c8739f3 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -77,7 +77,7 @@ const Demo = () => { const onChange = (value: any, selectedOptions: any) => { console.log('[DEBUG] onChange - value:', value); console.log('[DEBUG] onChange - selectedOptions:', selectedOptions); - setInputValue(selectedOptions.map((o) => o.label).join(', ')); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; return ( @@ -93,11 +93,13 @@ const Demo = () => { Multiple ); diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 80fad952..0a871150 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; import type { TriggerProps } from 'rc-trigger'; +import warning from 'rc-util/lib/warning'; +import useMergedState from 'rc-util/lib/hooks/useMergedState'; import generate from 'rc-tree-select/lib/generate'; import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; import type { RefSelectProps } from 'rc-select/lib/generate'; @@ -17,7 +19,8 @@ import useUpdateEffect from './hooks/useUpdateEffect'; * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API. * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version. * - * Remove: + * Deprecated: + * - popupVisible * - onPopupVisibleChange */ @@ -28,25 +31,32 @@ const RefCascader = generate({ // ====================================== Wrap ====================================== -interface BaseCascaderProps extends Pick { +interface BaseCascaderProps + extends Pick, + Omit, 'value' | 'defaultValue' | 'onChange'> { options?: DataNode[]; children?: React.ReactElement; // Value value?: CascaderValueType | CascaderValueType[]; defaultValue?: CascaderValueType | CascaderValueType[]; - changeOnSelect?: boolean; - allowClear?: boolean; + disabled?: boolean; + showSearch?: boolean; - /** @deprecated Use `onDropdownVisibleChange` instead */ - onPopupVisibleChange?: (popupVisible: boolean) => void; + searchValue?: string; + onSearch?: (search: string) => void; - onDropdownVisibleChange?: (popupVisible: boolean) => void; + // Open + /** @deprecated Use `open` instead */ + popupVisible?: boolean; + open?: boolean; + + /** @deprecated Use `onDropdownVisibleChange` instead */ + onPopupVisibleChange?: (open: boolean) => void; + onDropdownVisibleChange?: (open: boolean) => void; - // popupVisible?: boolean; - // disabled?: boolean; // transitionName?: string; // popupClassName?: string; // popupPlacement?: string; @@ -94,8 +104,15 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref (cascaderRef.current as any).getEntityByValue(val); + // =========================== Search =========================== + const [mergedSearch, setMergedSearch] = useMergedState(undefined, { + value: searchValue, + onChange: onSearch, + }); + // =========================== Value ============================ /** * Always pass props value to last value unit: @@ -127,7 +150,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref pathValue[pathValue.length - 1]); + return propValueList.map(pathValue => pathValue[pathValue.length - 1]); }; const [internalValue, setInternalValue] = React.useState(() => @@ -145,7 +168,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref opt.label) + .options.map(opt => opt.label) .join('>'); }; @@ -157,9 +180,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref entity); + const valueEntities = valueList.map(getEntityByValue).filter(entity => entity); - valueEntities.forEach((entity) => { + valueEntities.forEach(entity => { const { path, options: valueOptions } = restoreCompatibleValue(entity); pathList.push(path); optionsList.push(valueOptions); @@ -181,12 +204,28 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { onDropdownVisibleChange?.(nextVisible); onPopupVisibleChange?.(nextVisible); }; // =========================== Render =========================== + const dropdownStyle: React.CSSProperties = mergedSearch + ? {} + : { + minWidth: 'auto', + }; + return ( children, diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 284f6214..8556fde4 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -10,7 +10,7 @@ import { restoreCompatibleValue } from '../util'; const RefOptionList = React.forwardRef>( (props, ref) => { const { changeOnSelect } = React.useContext(CascaderContext); - const { prefixCls, options, onSelect, multiple, open, flattenOptions } = props; + const { prefixCls, options, onSelect, multiple, open, flattenOptions, searchValue } = props; const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); @@ -27,7 +27,7 @@ const RefOptionList = React.forwardRef flattenOption.data.value === checkedKeys[0], + flattenOption => flattenOption.data.value === checkedKeys[0], ); if (entity) { @@ -53,7 +53,7 @@ const RefOptionList = React.forwardRef option.value === pathList[i], + option => option.value === pathList[i], ).children; } @@ -61,9 +61,20 @@ const RefOptionList = React.forwardRef ({} as any)); + React.useImperativeHandle(ref, () => ({ + // scrollTo: treeRef.current?.scrollTo, + onKeyDown: event => { + const { which } = event; + }, + onKeyUp: () => {}, + })); // ========================== Render ========================== + // >>>>> Search + if (searchValue) { + return '233'; + } + // >>>>> Columns const columnProps = { ...props, From c3ac760b686732b2113d8c47d74fa5cd4278ac21 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 23 Jun 2021 14:56:11 +0800 Subject: [PATCH 11/54] chore: filter nodes --- src/Cascader.tsx | 4 ++++ src/OptionList/Column.tsx | 7 +++--- src/OptionList/SearchResult.tsx | 42 +++++++++++++++++++++++++++++++++ src/OptionList/index.tsx | 28 ++++++++++++++++------ src/util.ts | 5 ++++ 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 src/OptionList/SearchResult.tsx diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 0a871150..167c0167 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -174,6 +174,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { + // TODO: Need improve motion experience + setMergedSearch(''); + if (onChange) { const valueList = (multiple ? newValue : [newValue]) as React.Key[]; @@ -236,6 +239,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref - {options.map((option) => { - const { isLeaf, children, disabled, value } = option; - const isMergedLeaf = isLeaf !== undefined ? isLeaf : !children?.length; + {options.map(option => { + const { disabled, value } = option; + const isMergedLeaf = isLeaf(option); // >>>>> checked const checked = checkedSet.has(value); diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx new file mode 100644 index 00000000..44e85cb3 --- /dev/null +++ b/src/OptionList/SearchResult.tsx @@ -0,0 +1,42 @@ +/** + * Search result UI is different with origin menu list. + * We use single component to handle this. + */ +import { isLeaf } from '../util'; +import * as React from 'react'; +import type { DataNode } from '../interface'; +import Column from './Column'; +import type { ColumnProps } from './Column'; + +export type SearchResultProps = Omit; + +function flattenOptions(options: DataNode[]): DataNode[] { + const optionList: DataNode[] = []; + + function dig(list: DataNode[], labelPath: React.ReactNode[] = []) { + (list || []).forEach(option => { + const connectLabelPath = [...labelPath, option.label]; + + if (isLeaf(option)) { + optionList.push({ + label: connectLabelPath.join(' / '), + value: option.value, + }); + } else { + dig(option.children, connectLabelPath); + } + }); + } + + dig(options); + + return optionList; +} + +export default function SearchResult(props: SearchResultProps) { + const { options } = props; + + const optionList = React.useMemo(() => flattenOptions(options), [options]); + + return ; +} diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 8556fde4..3dab15e7 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -6,11 +6,21 @@ import type { DataNode } from '../interface'; import Column from './Column'; import CascaderContext from '../context'; import { restoreCompatibleValue } from '../util'; +import SearchResult from './SearchResult'; const RefOptionList = React.forwardRef>( (props, ref) => { const { changeOnSelect } = React.useContext(CascaderContext); - const { prefixCls, options, onSelect, multiple, open, flattenOptions, searchValue } = props; + const { + prefixCls, + options, + onSelect, + multiple, + open, + flattenOptions, + searchValue, + onToggleOpen, + } = props; const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); @@ -46,6 +56,10 @@ const RefOptionList = React.forwardRef { onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); + + if (!multiple) { + onToggleOpen(false); + } }; const getPathList = (pathList: React.Key[]) => { @@ -70,12 +84,6 @@ const RefOptionList = React.forwardRef>>>> Search - if (searchValue) { - return '233'; - } - - // >>>>> Columns const columnProps = { ...props, onOpen: onPathOpen, @@ -85,6 +93,12 @@ const RefOptionList = React.forwardRef>>>> Search + if (searchValue) { + return ; + } + + // >>>>> Columns const columnNodes: React.ReactElement[] = [ , ]; diff --git a/src/util.ts b/src/util.ts index 8e4cd70f..1ecfec71 100644 --- a/src/util.ts +++ b/src/util.ts @@ -19,3 +19,8 @@ export function restoreCompatibleValue(entity: FlattenDataNode): { return { path, options }; } + +export function isLeaf(option: DataNode) { + const { isLeaf: leaf, children } = option; + return leaf !== undefined ? leaf : !children?.length; +} From 00bfdf2dda350f688f1f98f921d64ad08a1d175c Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 23 Jun 2021 16:48:47 +0800 Subject: [PATCH 12/54] chore: ts update --- src/Cascader.tsx | 47 ++++++++++++++++++++++++++++----- src/OptionList/Column.tsx | 5 ++-- src/OptionList/SearchResult.tsx | 2 +- src/OptionList/index.tsx | 3 --- src/context.ts | 8 ++++-- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 167c0167..50eac142 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -22,6 +22,7 @@ import useUpdateEffect from './hooks/useUpdateEffect'; * Deprecated: * - popupVisible * - onPopupVisibleChange + * - filedNames */ const RefCascader = generate({ @@ -30,6 +31,29 @@ const RefCascader = generate({ }); // ====================================== Wrap ====================================== +export interface FieldNames { + value?: string | number; + label?: string; + children?: string; +} + +export interface ShowSearchType { + filter?: (inputValue: string, path: CascaderValueType, names: FieldNames) => boolean; + render?: ( + inputValue: string, + path: CascaderValueType, + prefixCls: string | undefined, + names: FieldNames, + ) => React.ReactNode; + sort?: ( + a: CascaderValueType, + b: CascaderValueType, + inputValue: string, + names: FieldNames, + ) => number; + matchInputWidth?: boolean; + limit?: number | false; +} interface BaseCascaderProps extends Pick, @@ -43,8 +67,13 @@ interface BaseCascaderProps changeOnSelect?: boolean; allowClear?: boolean; disabled?: boolean; - showSearch?: boolean; + /** @deprecated Typo. Please use `fieldNames` instead. */ + filedNames?: FieldNames; + fieldNames?: FieldNames; + + // Search + showSearch?: boolean | ShowSearchType; searchValue?: string; onSearch?: (search: string) => void; @@ -57,6 +86,9 @@ interface BaseCascaderProps onPopupVisibleChange?: (open: boolean) => void; onDropdownVisibleChange?: (open: boolean) => void; + // Trigger + expandTrigger?: 'hover' | 'click'; + // transitionName?: string; // popupClassName?: string; // popupPlacement?: string; @@ -66,10 +98,6 @@ interface BaseCascaderProps // builtinPlacements?: BuildInPlacements; // loadData?: (selectOptions: DataNode[]) => void; - // onKeyDown?: (e: React.KeyboardEvent) => void; - // expandTrigger?: string; - // // fieldNames?: CascaderFieldNames; - // // filedNames?: CascaderFieldNames; // typo but for compatibility // expandIcon?: React.ReactNode; // loadingIcon?: React.ReactNode; } @@ -113,11 +141,16 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref ({ changeOnSelect }), [changeOnSelect]); + const context = React.useMemo( + () => ({ changeOnSelect, expandTrigger }), + [changeOnSelect, expandTrigger], + ); // ============================ Ref ============================= const cascaderRef = React.useRef(); @@ -174,7 +207,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { - // TODO: Need improve motion experience + // TODO: Need improve motion experience setMergedSearch(''); if (onChange) { diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index fef5a74b..c5932914 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import classNames from 'classnames'; import type { DataNode } from '../interface'; import { isLeaf } from '../util'; +import CascaderContext from '../context'; export interface ColumnProps { prefixCls: string; @@ -12,7 +13,6 @@ export interface ColumnProps { openKey?: React.Key; onSelect: (value: React.Key) => void; onOpen: (index: number, value: React.Key) => void; - changeOnSelect: boolean; checkedSet: Set; halfCheckedSet: Set; } @@ -25,7 +25,6 @@ export default function Column({ openKey, onSelect, onOpen, - changeOnSelect, checkedSet, halfCheckedSet, }: ColumnProps) { @@ -33,6 +32,8 @@ export default function Column({ const menuItemPrefixCls = `${prefixCls}-menu-item`; const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`; + const { changeOnSelect, expandTrigger } = React.useContext(CascaderContext); + return (
      {options.map(option => { diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index 44e85cb3..ca397742 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -38,5 +38,5 @@ export default function SearchResult(props: SearchResultProps) { const optionList = React.useMemo(() => flattenOptions(options), [options]); - return ; + return ; } diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 3dab15e7..00825777 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -4,13 +4,11 @@ import { SelectContext } from 'rc-tree-select/lib/Context'; import List from 'rc-virtual-list'; import type { DataNode } from '../interface'; import Column from './Column'; -import CascaderContext from '../context'; import { restoreCompatibleValue } from '../util'; import SearchResult from './SearchResult'; const RefOptionList = React.forwardRef>( (props, ref) => { - const { changeOnSelect } = React.useContext(CascaderContext); const { prefixCls, options, @@ -90,7 +88,6 @@ const RefOptionList = React.forwardRef>>>> Search diff --git a/src/context.ts b/src/context.ts index 4f76b2d2..6ef87b4c 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,7 +1,11 @@ import * as React from 'react'; +import type { CascaderProps } from './Cascader'; -const CascaderContext = React.createContext({ +const CascaderContext = React.createContext< + Pick +>({ changeOnSelect: false, + expandTrigger: 'click', }); -export default CascaderContext; \ No newline at end of file +export default CascaderContext; From 5c5fa40206c165bb561e563d97d82f55aacfbe19 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 18 Aug 2021 17:24:51 +0800 Subject: [PATCH 13/54] test: back of test init --- jest.config.js | 1 + tests/index.spec.js | 1140 ++++++++++++++++++++++--------------------- tests/setup.js | 22 +- 3 files changed, 594 insertions(+), 569 deletions(-) diff --git a/jest.config.js b/jest.config.js index 8d42d5be..76d617fc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,4 @@ module.exports = { + setupFiles: ['./tests/setup.js'], snapshotSerializers: [require.resolve('enzyme-to-json/serializer')], } diff --git a/tests/index.spec.js b/tests/index.spec.js index dfbd8789..4b33f719 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -30,11 +30,12 @@ describe('Cascader', () => { , ); - expect(wrapper.state().popupVisible).toBeFalsy(); + + expect(wrapper.isOpen()).toBeFalsy(); wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeTruthy(); + expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); + expect(wrapper.isOpen()).toBeFalsy(); }); it('should call onChange when finish select', () => { @@ -44,98 +45,28 @@ describe('Cascader', () => { , ); wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - expect(menu1Items.length).toBe(3); - expect(selectedValue).toBeFalsy(); - menu1Items.at(0).simulate('click'); - expect( - wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-active'), - ).toBe(true); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); - expect(menu2Items.length).toBe(2); - expect(wrapper.state().popupVisible).toBeTruthy(); - expect(selectedValue).toBeFalsy(); - - menu2Items.at(0).simulate('click'); - expect( - wrapper - .find('.rc-cascader-menu') - .at(1) - .find('.rc-cascader-menu-item') - .first() - .hasClass('rc-cascader-menu-item-active'), - ).toBe(true); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); - expect(menu3Items.length).toBe(1); - expect(wrapper.state().popupVisible).toBeTruthy(); - expect(selectedValue).toBeFalsy(); - - menu3Items.at(0).simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); - }); - - it('should has defaultValue', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - expect(activeMenuItems.length).toBe(3); - expect(activeMenuItems.at(0).text()).toBe('福建'); - expect(activeMenuItems.at(1).text()).toBe('福州'); - expect(activeMenuItems.at(2).text()).toBe('马尾'); - }); - it('should support expand previous item when hover', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); + // Menu 1 let menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(1); const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); expect(menu1Items.length).toBe(3); expect(selectedValue).toBeFalsy(); - menu1Items.at(0).simulate('mouseEnter'); - jest.runAllTimers(); - wrapper.update(); + wrapper.clickOption(0, 0); expect( - wrapper - .find('.rc-cascader-menu') - .at(0) - .find('.rc-cascader-menu-item') - .first() - .hasClass('rc-cascader-menu-item-active'), - ).toBe(true); + wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-active'), + ).toBeTruthy(); + + // Menu 2 menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); expect(menu2Items.length).toBe(2); - expect(wrapper.state().popupVisible).toBeTruthy(); + expect(wrapper.isOpen()).toBeTruthy(); expect(selectedValue).toBeFalsy(); - menu2Items.at(0).simulate('mouseEnter'); - jest.runAllTimers(); - wrapper.update(); + wrapper.clickOption(1, 0); expect( wrapper .find('.rc-cascader-menu') @@ -143,491 +74,568 @@ describe('Cascader', () => { .find('.rc-cascader-menu-item') .first() .hasClass('rc-cascader-menu-item-active'), - ).toBe(true); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); - expect(menu3Items.length).toBe(1); - expect(wrapper.state().popupVisible).toBeTruthy(); - expect(selectedValue).toBeFalsy(); - - menu3Items.at(0).simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); - }); - - it('should clear active selection when no finish select', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - menu1Items.at(0).simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeTruthy(); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - }); - - it('should set back to defaultValue when no finish select', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - menu1Items.at(0).simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeTruthy(); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - }); - - it('should set the value on each selection', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - menu1Items.at(0).simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeTruthy(); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - expect(selectedValue.length).toBe(1); - expect(selectedValue[0]).toBe('fj'); + ).toBeTruthy(); + + // Menu 3 + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + expect(menu3Items.length).toBe(1); + expect(wrapper.isOpen()).toBeTruthy(); + expect(selectedValue).toBeFalsy(); + + wrapper.clickOption(2, 0); + expect(wrapper.isOpen()).toBeFalsy(); + expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); }); - it('should not change value inside when it is a controlled component', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - - menu1Items.at(0).simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); - - menu2Items.at(0).simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); - - menu3Items.at(0).simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(wrapper.state().popupVisible).toBeTruthy(); - expect(menus.length).toBe(2); - }); - - it('should be disabled', () => { - const wrapper = mount( - - - , - ); - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeFalsy(); - }); - - it('should not display popup when there is no options', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(0); - wrapper.find('input').simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(0); - }); - - it('should not display when children is empty', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - }); - - it('should be unselectable when option is disabled', () => { - const newAddressOptions = [...addressOptions]; - newAddressOptions[0] = { - ...newAddressOptions[0], - disabled: true, - }; - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - expect(menu1Items.length).toBe(3); - expect(selectedValue).toBeFalsy(); - - menu1Items.at(0).simulate('click'); - expect( - wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-disabled'), - ).toBe(true); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - }); - - it('should have correct active menu items', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - expect(activeMenuItems.length).toBe(2); - expect(activeMenuItems.at(0).text()).toBe('1'); - expect(activeMenuItems.at(1).text()).toBe('2'); - const menus = wrapper.find('.rc-cascader-menu'); - const activeMenuItemsInMenu1 = menus.at(0).find('.rc-cascader-menu-item-active'); - expect(activeMenuItemsInMenu1.length).toBe(1); - }); - - // https://github.com/ant-design/ant-design/issues/5666 - it('should have not change active value when value is not changed', () => { - class Demo extends React.Component { - state = { - value: [], - }; - - componentDidMount() { - this.timeout = setTimeout(() => { - this.setState({ - value: [], - }); - }, 10); - } - - componentWillUnmount() { - clearTimeout(this.timeout); - } - - getPopupDOMNode() { - return this.cascader.getPopupDOMNode(); - } - - render() { - return ( - { - this.cascader = node; - }} - > - - - ); - } - } - const wrapper = mount(); - wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - menu1Items.at(0).simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - - jest.runAllTimers(); - wrapper.update(); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(2); - }); - - // https://github.com/ant-design/ant-design/issues/7480 - it('should call onChange on click when expandTrigger=hover with changeOnSelect', () => { - const wrapper = mount( - - - , - ); - - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - expect(menu1Items.length).toBe(3); - menu1Items.at(0).simulate('click'); - expect(selectedValue[0]).toBe('fj'); - expect(wrapper.state().popupVisible).toBeFalsy(); - }); - - it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { - const wrapper = mount( - - - , - ); - - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(1); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - expect(menu1Items.length).toBe(3); - menu1Items.at(0).simulate('mouseEnter'); - jest.runAllTimers(); - wrapper.update(); - expect(selectedValue).toBeFalsy(); - expect(wrapper.state().popupVisible).toBeTruthy(); - }); - - it('should has default fieldName when props not exist labelField and valueField and childrenField', () => { - const wrapper = mount( - - - , - ); - const props = wrapper.props(); - expect(props.fieldNames.label).toBe('label'); - expect(props.fieldNames.value).toBe('value'); - expect(props.fieldNames.children).toBe('children'); - }); - - it('should support custom fieldNames', () => { - const wrapper = mount( - - - , - ); - const props = wrapper.props(); - expect(props.fieldNames.label).toBe('name'); - expect(props.fieldNames.value).toBe('code'); - expect(props.fieldNames.children).toBe('nodes'); - const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - expect(activeMenuItems.length).toBe(3); - expect(activeMenuItems.at(0).text()).toBe('福建'); - expect(activeMenuItems.at(1).text()).toBe('福州'); - expect(activeMenuItems.at(2).text()).toBe('马尾'); - }); - - it('should works and show warning message when use typo prop name: filedNames', () => { - // eslint-disable-next-line no-console - console.error = jest.fn(); - const wrapper = mount( - - - , - ); - // eslint-disable-next-line no-console - expect(console.error).toHaveBeenCalled(); - const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - expect(activeMenuItems.length).toBe(3); - expect(activeMenuItems.at(0).text()).toBe('福建'); - expect(activeMenuItems.at(1).text()).toBe('福州'); - expect(activeMenuItems.at(2).text()).toBe('马尾'); - // eslint-disable-next-line no-console - console.error.mockClear(); - }); - - it('should support custom expand icon(text icon)', () => { - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - expect(activeMenuItems.length).toBe(3); - expect(activeMenuItems.at(0).text()).toBe('福建=>'); - expect(activeMenuItems.at(1).text()).toBe('福州=>'); - expect(activeMenuItems.at(2).text()).toBe('马尾'); - }); - - it('should close popup on double click when changeOnSelect is set', () => { - const wrapper = mount( - - - , - ); - - expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('click'); - expect(wrapper.state().popupVisible).toBeTruthy(); - wrapper.find('li').at(0).simulate('doubleClick'); - expect(wrapper.state().popupVisible).toBeFalsy(); - }); - - // https://github.com/ant-design/ant-design/issues/9084 - it('should trigger loadData when expandTrigger is hover', () => { - const options = [ - { - value: 'zhejiang', - label: 'Zhejiang', - isLeaf: false, - }, - { - value: 'jiangsu', - label: 'Jiangsu', - isLeaf: false, - }, - ]; - const loadData = jest.fn(); - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - menu1Items.at(0).simulate('mouseEnter'); - jest.runAllTimers(); - expect(loadData).toHaveBeenCalled(); - }); - - // https://github.com/ant-design/ant-design/issues/9793 - it('should not trigger onBlur and onFocus when select item', () => { - const onFocus = jest.fn(); - const onBlur = jest.fn(); - const wrapper = mount( - - - , - ); - wrapper.find('input').simulate('focus'); - wrapper.find('input').simulate('click'); - const menus = wrapper.find('.rc-cascader-menu'); - const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - menu1Items.at(0).simulate('mouseDown'); - menu1Items.at(0).simulate('click'); - jest.runAllTimers(); - expect(onFocus).toHaveBeenCalledTimes(1); - expect(onBlur).toHaveBeenCalledTimes(0); - }); - - // https://github.com/ant-design/ant-design/issues/18713 - it('should not show title when title is falsy', () => { - const options = [ - { - value: '1', - label: '1', - title: '', - }, - { - value: '2', - label: '2', - title: undefined, - }, - { - value: '3', - label: '3', - }, - { - value: '4', - label: '4', - title: 'title', - }, - ]; - const wrapper = mount( - - - , - ); - const menus = wrapper.find('.rc-cascader-menu'); - expect(menus).toMatchSnapshot(); - }); - - it('should render custom dropdown correctly', () => { - const wrapper = mount( - ( -
      - {menus} -
      - Hello, DropdownRender -
      - )} - > - -
      , - ); - const customDropdown = wrapper.find('.custom-dropdown'); - expect(customDropdown.length).toBe(1); - const customDropdownContent = wrapper.find('.custom-dropdown-content'); - expect(customDropdownContent.length).toBe(1); - const menus = wrapper.find('.rc-cascader-menus'); - expect(menus).toMatchSnapshot(); - }); + // it('should has defaultValue', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + // expect(activeMenuItems.length).toBe(3); + // expect(activeMenuItems.at(0).text()).toBe('福建'); + // expect(activeMenuItems.at(1).text()).toBe('福州'); + // expect(activeMenuItems.at(2).text()).toBe('马尾'); + // }); + + // it('should support expand previous item when hover', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // expect(menu1Items.length).toBe(3); + // expect(selectedValue).toBeFalsy(); + + // menu1Items.at(0).simulate('mouseEnter'); + // jest.runAllTimers(); + // wrapper.update(); + // expect( + // wrapper + // .find('.rc-cascader-menu') + // .at(0) + // .find('.rc-cascader-menu-item') + // .first() + // .hasClass('rc-cascader-menu-item-active'), + // ).toBe(true); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + // expect(menu2Items.length).toBe(2); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(selectedValue).toBeFalsy(); + + // menu2Items.at(0).simulate('mouseEnter'); + // jest.runAllTimers(); + // wrapper.update(); + // expect( + // wrapper + // .find('.rc-cascader-menu') + // .at(1) + // .find('.rc-cascader-menu-item') + // .first() + // .hasClass('rc-cascader-menu-item-active'), + // ).toBe(true); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + // expect(menu3Items.length).toBe(1); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(selectedValue).toBeFalsy(); + + // menu3Items.at(0).simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); + // }); + + // it('should clear active selection when no finish select', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // menu1Items.at(0).simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // }); + + // it('should set back to defaultValue when no finish select', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // menu1Items.at(0).simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // }); + + // it('should set the value on each selection', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // menu1Items.at(0).simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // expect(selectedValue.length).toBe(1); + // expect(selectedValue[0]).toBe('fj'); + // }); + + // it('should not change value inside when it is a controlled component', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + + // menu1Items.at(0).simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + + // menu2Items.at(0).simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + + // menu3Items.at(0).simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(menus.length).toBe(2); + // }); + + // it('should be disabled', () => { + // const wrapper = mount( + // + // + // , + // ); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // }); + + // it('should not display popup when there is no options', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(0); + // wrapper.find('input').simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(0); + // }); + + // it('should not display when children is empty', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // }); + + // it('should be unselectable when option is disabled', () => { + // const newAddressOptions = [...addressOptions]; + // newAddressOptions[0] = { + // ...newAddressOptions[0], + // disabled: true, + // }; + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // expect(menu1Items.length).toBe(3); + // expect(selectedValue).toBeFalsy(); + + // menu1Items.at(0).simulate('click'); + // expect( + // wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-disabled'), + // ).toBe(true); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // }); + + // it('should have correct active menu items', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + // expect(activeMenuItems.length).toBe(2); + // expect(activeMenuItems.at(0).text()).toBe('1'); + // expect(activeMenuItems.at(1).text()).toBe('2'); + // const menus = wrapper.find('.rc-cascader-menu'); + // const activeMenuItemsInMenu1 = menus.at(0).find('.rc-cascader-menu-item-active'); + // expect(activeMenuItemsInMenu1.length).toBe(1); + // }); + + // // https://github.com/ant-design/ant-design/issues/5666 + // it('should have not change active value when value is not changed', () => { + // class Demo extends React.Component { + // state = { + // value: [], + // }; + + // componentDidMount() { + // this.timeout = setTimeout(() => { + // this.setState({ + // value: [], + // }); + // }, 10); + // } + + // componentWillUnmount() { + // clearTimeout(this.timeout); + // } + + // getPopupDOMNode() { + // return this.cascader.getPopupDOMNode(); + // } + + // render() { + // return ( + // { + // this.cascader = node; + // }} + // > + // + // + // ); + // } + // } + // const wrapper = mount(); + // wrapper.find('input').simulate('click'); + // let menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // menu1Items.at(0).simulate('click'); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + + // jest.runAllTimers(); + // wrapper.update(); + // menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(2); + // }); + + // // https://github.com/ant-design/ant-design/issues/7480 + // it('should call onChange on click when expandTrigger=hover with changeOnSelect', () => { + // const wrapper = mount( + // + // + // , + // ); + + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // expect(menu1Items.length).toBe(3); + // menu1Items.at(0).simulate('click'); + // expect(selectedValue[0]).toBe('fj'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // }); + + // it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { + // const wrapper = mount( + // + // + // , + // ); + + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(1); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // expect(menu1Items.length).toBe(3); + // menu1Items.at(0).simulate('mouseEnter'); + // jest.runAllTimers(); + // wrapper.update(); + // expect(selectedValue).toBeFalsy(); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // }); + + // it('should has default fieldName when props not exist labelField and valueField and childrenField', () => { + // const wrapper = mount( + // + // + // , + // ); + // const props = wrapper.props(); + // expect(props.fieldNames.label).toBe('label'); + // expect(props.fieldNames.value).toBe('value'); + // expect(props.fieldNames.children).toBe('children'); + // }); + + // it('should support custom fieldNames', () => { + // const wrapper = mount( + // + // + // , + // ); + // const props = wrapper.props(); + // expect(props.fieldNames.label).toBe('name'); + // expect(props.fieldNames.value).toBe('code'); + // expect(props.fieldNames.children).toBe('nodes'); + // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + // expect(activeMenuItems.length).toBe(3); + // expect(activeMenuItems.at(0).text()).toBe('福建'); + // expect(activeMenuItems.at(1).text()).toBe('福州'); + // expect(activeMenuItems.at(2).text()).toBe('马尾'); + // }); + + // it('should works and show warning message when use typo prop name: filedNames', () => { + // // eslint-disable-next-line no-console + // console.error = jest.fn(); + // const wrapper = mount( + // + // + // , + // ); + // // eslint-disable-next-line no-console + // expect(console.error).toHaveBeenCalled(); + // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + // expect(activeMenuItems.length).toBe(3); + // expect(activeMenuItems.at(0).text()).toBe('福建'); + // expect(activeMenuItems.at(1).text()).toBe('福州'); + // expect(activeMenuItems.at(2).text()).toBe('马尾'); + // // eslint-disable-next-line no-console + // console.error.mockClear(); + // }); + + // it('should support custom expand icon(text icon)', () => { + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // expect(menus.length).toBe(3); + // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + // expect(activeMenuItems.length).toBe(3); + // expect(activeMenuItems.at(0).text()).toBe('福建=>'); + // expect(activeMenuItems.at(1).text()).toBe('福州=>'); + // expect(activeMenuItems.at(2).text()).toBe('马尾'); + // }); + + // it('should close popup on double click when changeOnSelect is set', () => { + // const wrapper = mount( + // + // + // , + // ); + + // expect(wrapper.state().popupVisible).toBeFalsy(); + // wrapper.find('input').simulate('click'); + // expect(wrapper.state().popupVisible).toBeTruthy(); + // wrapper.find('li').at(0).simulate('doubleClick'); + // expect(wrapper.state().popupVisible).toBeFalsy(); + // }); + + // // https://github.com/ant-design/ant-design/issues/9084 + // it('should trigger loadData when expandTrigger is hover', () => { + // const options = [ + // { + // value: 'zhejiang', + // label: 'Zhejiang', + // isLeaf: false, + // }, + // { + // value: 'jiangsu', + // label: 'Jiangsu', + // isLeaf: false, + // }, + // ]; + // const loadData = jest.fn(); + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // menu1Items.at(0).simulate('mouseEnter'); + // jest.runAllTimers(); + // expect(loadData).toHaveBeenCalled(); + // }); + + // // https://github.com/ant-design/ant-design/issues/9793 + // it('should not trigger onBlur and onFocus when select item', () => { + // const onFocus = jest.fn(); + // const onBlur = jest.fn(); + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('focus'); + // wrapper.find('input').simulate('click'); + // const menus = wrapper.find('.rc-cascader-menu'); + // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + // menu1Items.at(0).simulate('mouseDown'); + // menu1Items.at(0).simulate('click'); + // jest.runAllTimers(); + // expect(onFocus).toHaveBeenCalledTimes(1); + // expect(onBlur).toHaveBeenCalledTimes(0); + // }); + + // // https://github.com/ant-design/ant-design/issues/18713 + // it('should not show title when title is falsy', () => { + // const options = [ + // { + // value: '1', + // label: '1', + // title: '', + // }, + // { + // value: '2', + // label: '2', + // title: undefined, + // }, + // { + // value: '3', + // label: '3', + // }, + // { + // value: '4', + // label: '4', + // title: 'title', + // }, + // ]; + // const wrapper = mount( + // + // + // , + // ); + // const menus = wrapper.find('.rc-cascader-menu'); + // expect(menus).toMatchSnapshot(); + // }); + + // it('should render custom dropdown correctly', () => { + // const wrapper = mount( + // ( + //
      + // {menus} + //
      + // Hello, DropdownRender + //
      + // )} + // > + // + //
      , + // ); + // const customDropdown = wrapper.find('.custom-dropdown'); + // expect(customDropdown.length).toBe(1); + // const customDropdownContent = wrapper.find('.custom-dropdown-content'); + // expect(customDropdownContent.length).toBe(1); + // const menus = wrapper.find('.rc-cascader-menus'); + // expect(menus).toMatchSnapshot(); + // }); }); diff --git a/tests/setup.js b/tests/setup.js index 684b8a6f..4d53cc86 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -1,8 +1,24 @@ -global.requestAnimationFrame = global.requestAnimationFrame || function requestAnimationFrame(cb) { - return setTimeout(cb, 0); -}; +global.requestAnimationFrame = + global.requestAnimationFrame || + function requestAnimationFrame(cb) { + return setTimeout(cb, 0); + }; const Enzyme = require('enzyme'); const Adapter = require('enzyme-adapter-react-16'); Enzyme.configure({ adapter: new Adapter() }); + +Object.assign(Enzyme.ReactWrapper.prototype, { + isOpen() { + return !!this.find('Trigger').props().popupVisible; + }, + clickOption(menuIndex, itemIndex) { + const menu = this.find('ul.rc-cascader-menu').at(menuIndex); + const itemList = menu.find('li.rc-cascader-menu-item'); + + itemList.at(itemIndex).find('div').first().simulate('click'); + + return this; + }, +}); From 5a301d7a7f8ce00844f5ed04c33103780acd3e2f Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 18 Aug 2021 17:48:56 +0800 Subject: [PATCH 14/54] feat: Hover trigger --- src/OptionList/Column.tsx | 5 + tests/index.spec.js | 188 ++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 91 deletions(-) diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index c5932914..8937a002 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -68,6 +68,11 @@ export default function Column({ })} role="menuitemcheckbox" aria-checked={checked} + onMouseEnter={() => { + if (expandTrigger) { + triggerOpen(); + } + }} > {multiple && ( { .hasClass('rc-cascader-menu-item-active'), ).toBeTruthy(); + // Menu 3 + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + expect(menu3Items.length).toBe(1); + expect(wrapper.isOpen()).toBeTruthy(); + expect(selectedValue).toBeFalsy(); + + wrapper.clickOption(2, 0); + expect(wrapper.isOpen()).toBeFalsy(); + expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); + }); + + it('should has defaultValue', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + const menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + expect(activeMenuItems.length).toBe(3); + expect(activeMenuItems.at(0).text()).toBe('福建'); + expect(activeMenuItems.at(1).text()).toBe('福州'); + expect(activeMenuItems.at(2).text()).toBe('马尾'); + }); + + it('should support expand previous item when hover', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + + // Menu 1 + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + expect(menu1Items.length).toBe(3); + expect(selectedValue).toBeFalsy(); + + menu1Items.at(0).simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); + expect( + wrapper + .find('.rc-cascader-menu') + .at(0) + .find('.rc-cascader-menu-item') + .first() + .hasClass('rc-cascader-menu-item-active'), + ).toBeTruthy(); + + // Menu 2 + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + expect(menu2Items.length).toBe(2); + expect(wrapper.isOpen()).toBeTruthy(); + expect(selectedValue).toBeFalsy(); + + menu2Items.at(0).simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); + expect( + wrapper + .find('.rc-cascader-menu') + .at(1) + .find('.rc-cascader-menu-item') + .first() + .hasClass('rc-cascader-menu-item-active'), + ).toBeTruthy(); + // Menu 3 menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(3); @@ -89,81 +170,6 @@ describe('Cascader', () => { expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); }); - // it('should has defaultValue', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - // expect(activeMenuItems.length).toBe(3); - // expect(activeMenuItems.at(0).text()).toBe('福建'); - // expect(activeMenuItems.at(1).text()).toBe('福州'); - // expect(activeMenuItems.at(2).text()).toBe('马尾'); - // }); - - // it('should support expand previous item when hover', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // expect(menu1Items.length).toBe(3); - // expect(selectedValue).toBeFalsy(); - - // menu1Items.at(0).simulate('mouseEnter'); - // jest.runAllTimers(); - // wrapper.update(); - // expect( - // wrapper - // .find('.rc-cascader-menu') - // .at(0) - // .find('.rc-cascader-menu-item') - // .first() - // .hasClass('rc-cascader-menu-item-active'), - // ).toBe(true); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); - // expect(menu2Items.length).toBe(2); - // expect(wrapper.state().popupVisible).toBeTruthy(); - // expect(selectedValue).toBeFalsy(); - - // menu2Items.at(0).simulate('mouseEnter'); - // jest.runAllTimers(); - // wrapper.update(); - // expect( - // wrapper - // .find('.rc-cascader-menu') - // .at(1) - // .find('.rc-cascader-menu-item') - // .first() - // .hasClass('rc-cascader-menu-item-active'), - // ).toBe(true); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); - // expect(menu3Items.length).toBe(1); - // expect(wrapper.state().popupVisible).toBeTruthy(); - // expect(selectedValue).toBeFalsy(); - - // menu3Items.at(0).simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); - // expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); - // }); - // it('should clear active selection when no finish select', () => { // const wrapper = mount( // @@ -177,9 +183,9 @@ describe('Cascader', () => { // menus = wrapper.find('.rc-cascader-menu'); // expect(menus.length).toBe(2); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(wrapper.isOpen()).toBeTruthy(); // menus = wrapper.find('.rc-cascader-menu'); // expect(menus.length).toBe(1); // }); @@ -198,9 +204,9 @@ describe('Cascader', () => { // menus = wrapper.find('.rc-cascader-menu'); // expect(menus.length).toBe(2); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(wrapper.isOpen()).toBeTruthy(); // menus = wrapper.find('.rc-cascader-menu'); // expect(menus.length).toBe(3); // }); @@ -224,9 +230,9 @@ describe('Cascader', () => { // menus = wrapper.find('.rc-cascader-menu'); // expect(menus.length).toBe(2); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(wrapper.isOpen()).toBeTruthy(); // menus = wrapper.find('.rc-cascader-menu'); // expect(menus.length).toBe(2); // expect(selectedValue.length).toBe(1); @@ -255,10 +261,10 @@ describe('Cascader', () => { // const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); // menu3Items.at(0).simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); // menus = wrapper.find('.rc-cascader-menu'); - // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(wrapper.isOpen()).toBeTruthy(); // expect(menus.length).toBe(2); // }); @@ -268,11 +274,11 @@ describe('Cascader', () => { // // , // ); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // }); // it('should not display popup when there is no options', () => { @@ -418,7 +424,7 @@ describe('Cascader', () => { // expect(menu1Items.length).toBe(3); // menu1Items.at(0).simulate('click'); // expect(selectedValue[0]).toBe('fj'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // }); // it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { @@ -437,7 +443,7 @@ describe('Cascader', () => { // jest.runAllTimers(); // wrapper.update(); // expect(selectedValue).toBeFalsy(); - // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(wrapper.isOpen()).toBeTruthy(); // }); // it('should has default fieldName when props not exist labelField and valueField and childrenField', () => { @@ -528,11 +534,11 @@ describe('Cascader', () => { // , // ); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // wrapper.find('input').simulate('click'); - // expect(wrapper.state().popupVisible).toBeTruthy(); + // expect(wrapper.isOpen()).toBeTruthy(); // wrapper.find('li').at(0).simulate('doubleClick'); - // expect(wrapper.state().popupVisible).toBeFalsy(); + // expect(wrapper.isOpen()).toBeFalsy(); // }); // // https://github.com/ant-design/ant-design/issues/9084 From 727fb74f1dad0f8a4e17527b5431cf07a216d529 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 18 Aug 2021 19:13:46 +0800 Subject: [PATCH 15/54] fix: changeOnSelect props --- examples/change-on-select.jsx | 2 +- src/OptionList/Column.tsx | 4 +- src/OptionList/index.tsx | 4 +- tests/index.spec.js | 158 ++++++++++++++++++---------------- 4 files changed, 87 insertions(+), 81 deletions(-) diff --git a/examples/change-on-select.jsx b/examples/change-on-select.jsx index cc950b3a..794d99f8 100644 --- a/examples/change-on-select.jsx +++ b/examples/change-on-select.jsx @@ -74,7 +74,7 @@ class Demo extends React.Component { options={addressOptions} onChange={this.onChange} changeOnSelect - expandTrigger="hover" + // expandTrigger="hover" loadData={() => console.log('loadData')} > diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 8937a002..c453e715 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -11,7 +11,7 @@ export interface ColumnProps { options: DataNode[]; /** Current Column opened item key */ openKey?: React.Key; - onSelect: (value: React.Key) => void; + onSelect: (value: React.Key, isLeaf: boolean) => void; onOpen: (index: number, value: React.Key) => void; checkedSet: Set; halfCheckedSet: Set; @@ -53,7 +53,7 @@ export default function Column({ // >>>>> Selection const triggerSelect = () => { if (!disabled && (isMergedLeaf || changeOnSelect || multiple)) { - onSelect(value); + onSelect(value, isMergedLeaf); } }; diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 00825777..71a64f3a 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -52,10 +52,10 @@ const RefOptionList = React.forwardRef { + const onPathSelect = (pathValue: React.Key, isLeaf: boolean) => { onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); - if (!multiple) { + if (!multiple && isLeaf) { onToggleOpen(false); } }; diff --git a/tests/index.spec.js b/tests/index.spec.js index 040c6541..35629391 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -158,86 +158,92 @@ describe('Cascader', () => { ).toBeTruthy(); // Menu 3 - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(3); - const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); - expect(menu3Items.length).toBe(1); - expect(wrapper.isOpen()).toBeTruthy(); - expect(selectedValue).toBeFalsy(); - - wrapper.clickOption(2, 0); - expect(wrapper.isOpen()).toBeFalsy(); - expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + expect(menu3Items.length).toBe(1); + expect(wrapper.isOpen()).toBeTruthy(); + expect(selectedValue).toBeFalsy(); + + wrapper.clickOption(2, 0); + expect(wrapper.isOpen()).toBeFalsy(); + expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei'); }); - // it('should clear active selection when no finish select', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // menu1Items.at(0).simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeTruthy(); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // }); + it('should clear active selection when no finish select', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + wrapper.clickOption(0, 0); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); - // it('should set back to defaultValue when no finish select', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // menu1Items.at(0).simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeTruthy(); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // }); + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeFalsy(); - // it('should set the value on each selection', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // menu1Items.at(0).simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeTruthy(); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // expect(selectedValue.length).toBe(1); - // expect(selectedValue[0]).toBe('fj'); - // }); + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeTruthy(); + + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + }); + + it('should set back to defaultValue when no finish select', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + + wrapper.clickOption(0, 0); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeFalsy(); + + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeTruthy(); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + }); + + it('should set the value on each selection', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + + wrapper.clickOption(0, 0); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeFalsy(); + + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeTruthy(); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + expect(selectedValue.length).toBe(1); + expect(selectedValue[0]).toBe('fj'); + }); // it('should not change value inside when it is a controlled component', () => { // const wrapper = mount( From 8759e1c2fe2968d5033249c7ac2dc0560f7d2f6b Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 18 Aug 2021 19:15:47 +0800 Subject: [PATCH 16/54] test: Control mode --- tests/index.spec.js | 48 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/index.spec.js b/tests/index.spec.js index 35629391..70b08d50 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -245,34 +245,32 @@ describe('Cascader', () => { expect(selectedValue[0]).toBe('fj'); }); - // it('should not change value inside when it is a controlled component', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + it('should not change value inside when it is a controlled component', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); - // menu1Items.at(0).simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // const menu2Items = menus.at(1).find('.rc-cascader-menu-item'); + wrapper.clickOption(0,0); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); - // menu2Items.at(0).simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // const menu3Items = menus.at(2).find('.rc-cascader-menu-item'); + wrapper.clickOption(1, 0); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); - // menu3Items.at(0).simulate('click'); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(wrapper.isOpen()).toBeTruthy(); - // expect(menus.length).toBe(2); - // }); + wrapper.clickOption(2, 0); + expect(wrapper.isOpen()).toBeFalsy(); + + wrapper.find('input').simulate('click'); + menus = wrapper.find('.rc-cascader-menu'); + expect(wrapper.isOpen()).toBeTruthy(); + expect(menus.length).toBe(2); + }); // it('should be disabled', () => { // const wrapper = mount( From 47369102351ca7a52d70b521c680318cdef7cf59 Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 10:54:59 +0800 Subject: [PATCH 17/54] test: disabled --- tests/index.spec.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/index.spec.js b/tests/index.spec.js index 70b08d50..1b7be3ba 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -272,18 +272,18 @@ describe('Cascader', () => { expect(menus.length).toBe(2); }); - // it('should be disabled', () => { - // const wrapper = mount( - // - // - // , - // ); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeFalsy(); - // }); + it('should be disabled', () => { + const wrapper = mount( + + + , + ); + expect(wrapper.isOpen()).toBeFalsy(); + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeFalsy(); + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeFalsy(); + }); // it('should not display popup when there is no options', () => { // const wrapper = mount( From 6484da52021b8742e21348508968ae49c655bc09 Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 11:14:40 +0800 Subject: [PATCH 18/54] test: disabled --- tests/index.spec.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/index.spec.js b/tests/index.spec.js index 1b7be3ba..44467ef7 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -285,19 +285,19 @@ describe('Cascader', () => { expect(wrapper.isOpen()).toBeFalsy(); }); - // it('should not display popup when there is no options', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(0); - // wrapper.find('input').simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(0); - // }); + it('should not display popup when there is no options', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(0); + wrapper.find('input').simulate('click'); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(0); + }); // it('should not display when children is empty', () => { // const wrapper = mount( From 9a2e70a1b3816de273d687cb4266e0a82bb2e6a8 Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 11:54:41 +0800 Subject: [PATCH 19/54] fix: back of notFoundContent --- examples/change-on-select.jsx | 2 +- src/Cascader.tsx | 4 +-- src/OptionList/index.tsx | 19 ++++++++++++- tests/index.spec.js | 52 ++++++++++++++++++----------------- 4 files changed, 48 insertions(+), 29 deletions(-) diff --git a/examples/change-on-select.jsx b/examples/change-on-select.jsx index 794d99f8..ec808259 100644 --- a/examples/change-on-select.jsx +++ b/examples/change-on-select.jsx @@ -71,7 +71,7 @@ class Demo extends React.Component { render() { return ( >>>> Columns + const firstLevelOptions = options.length + ? options + : [ + { + label: notFoundContent, + value: '__EMPTY__', + disabled: true, + }, + ]; + const columnNodes: React.ReactElement[] = [ - , + , ]; openPath.forEach((_, index) => { diff --git a/tests/index.spec.js b/tests/index.spec.js index 44467ef7..c202cf5b 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -285,39 +285,41 @@ describe('Cascader', () => { expect(wrapper.isOpen()).toBeFalsy(); }); - it('should not display popup when there is no options', () => { + it('should display not found popup when there is no options', () => { const wrapper = mount( , ); wrapper.find('input').simulate('click'); - let menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(0); - wrapper.find('input').simulate('click'); - menus = wrapper.find('.rc-cascader-menu'); - expect(menus.length).toBe(0); + expect(wrapper.isOpen()).toBeTruthy(); + expect(wrapper.find('.rc-cascader-menu')).toHaveLength(1); + expect(wrapper.find('.rc-cascader-menu-item')).toHaveLength(1); + expect(wrapper.find('.rc-cascader-menu-item').text()).toEqual('Not Found'); + + wrapper.setProps({ notFoundContent: 'BambooLight' }); + expect(wrapper.find('.rc-cascader-menu-item').text()).toEqual('BambooLight'); }); - // it('should not display when children is empty', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // }); + it('should not display when children is empty', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + const menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + }); // it('should be unselectable when option is disabled', () => { // const newAddressOptions = [...addressOptions]; From 79ed8aedecf437831d0feb330e8a80e18e50502d Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 16:27:34 +0800 Subject: [PATCH 20/54] chore: Update rc-tree-select --- examples/debug.tsx | 17 ++++++- package.json | 2 +- src/Cascader.tsx | 22 +++++---- src/OptionList/Column.tsx | 6 +-- src/OptionList/SearchResult.tsx | 18 ++++--- src/OptionList/index.tsx | 8 ++-- src/interface.ts | 4 ++ src/util.ts | 8 ++-- tests/index.spec.js | 85 +++++++++++++++++---------------- 9 files changed, 102 insertions(+), 68 deletions(-) diff --git a/examples/debug.tsx b/examples/debug.tsx index 2c8739f3..31e1ec97 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -3,7 +3,7 @@ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; -const addressOptions = [ +const addressOptions1 = [ { label: '福建', value: 'fj', @@ -68,6 +68,19 @@ const addressOptions = [ }, ]; +const addressOptions = [ + { + value: '1', + label: '1', + children: [ + { + value: '1', + label: '1', + }, + ], + }, +]; + const defaultValue = ['fj', 'fuzhou', 'mawei']; const Demo = () => { @@ -94,7 +107,7 @@ const Demo = () => { { + return options || []; + }, [options]); + // =========================== Value ============================ /** * Always pass props value to last value unit: @@ -197,7 +202,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { if (multiple) { - return entity.data.label; + return entity.data.node.label; } return restoreCompatibleValue(entity) @@ -256,11 +261,12 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref @@ -269,8 +275,8 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref void; @@ -98,7 +98,7 @@ export default function Column({ } }} > - {option.label} + {option.title}
); diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index ca397742..624f1776 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -4,22 +4,28 @@ */ import { isLeaf } from '../util'; import * as React from 'react'; -import type { DataNode } from '../interface'; +import type { OptionDataNode } from '../interface'; import Column from './Column'; import type { ColumnProps } from './Column'; export type SearchResultProps = Omit; -function flattenOptions(options: DataNode[]): DataNode[] { - const optionList: DataNode[] = []; +function flattenOptions(options: OptionDataNode[]): OptionDataNode[] { + const optionList: OptionDataNode[] = []; - function dig(list: DataNode[], labelPath: React.ReactNode[] = []) { + function dig(list: OptionDataNode[], labelPath: React.ReactNode[] = []) { (list || []).forEach(option => { - const connectLabelPath = [...labelPath, option.label]; + const connectLabelPath = [...labelPath, option.title]; if (isLeaf(option)) { optionList.push({ - label: connectLabelPath.join(' / '), + title: connectLabelPath.reduce((labelList: React.ReactNode[], currentLabel, index) => { + if (index !== 0) { + labelList.push(' / '); + } + labelList.push(currentLabel); + return list; + }, []), value: option.value, }); } else { diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 4eb9296a..aadc104a 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -1,13 +1,12 @@ import * as React from 'react'; import type { OptionListProps, RefOptionListProps } from 'rc-select/lib/OptionList'; import { SelectContext } from 'rc-tree-select/lib/Context'; -import List from 'rc-virtual-list'; -import type { DataNode } from '../interface'; +import type { OptionDataNode } from '../interface'; import Column from './Column'; import { restoreCompatibleValue } from '../util'; import SearchResult from './SearchResult'; -const RefOptionList = React.forwardRef>( +const RefOptionList = React.forwardRef>( (props, ref) => { const { prefixCls, @@ -21,6 +20,7 @@ const RefOptionList = React.forwardRef { + title: React.ReactNode; + children?: OptionDataNode[]; +} export interface FlattenDataNode { key: React.Key; data: DataNode; diff --git a/src/util.ts b/src/util.ts index 1ecfec71..17fb9b46 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; -import type { CascaderValueType, DataNode } from './interface'; +import type { CascaderValueType, DataNode, OptionDataNode } from './interface'; export function restoreCompatibleValue(entity: FlattenDataNode): { path: CascaderValueType; @@ -11,8 +11,8 @@ export function restoreCompatibleValue(entity: FlattenDataNode): { let current = entity; while (current) { - path.unshift(current.data.value); - options.unshift(current.data as DataNode); + path.unshift(current.data.node.value); + options.unshift(current.data.node as DataNode); current = current.parent; } @@ -20,7 +20,7 @@ export function restoreCompatibleValue(entity: FlattenDataNode): { return { path, options }; } -export function isLeaf(option: DataNode) { +export function isLeaf(option: DataNode | OptionDataNode) { const { isLeaf: leaf, children } = option; return leaf !== undefined ? leaf : !children?.length; } diff --git a/tests/index.spec.js b/tests/index.spec.js index c202cf5b..4f086188 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -255,7 +255,7 @@ describe('Cascader', () => { let menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.clickOption(0,0); + wrapper.clickOption(0, 0); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); @@ -321,47 +321,52 @@ describe('Cascader', () => { expect(menus.length).toBe(1); }); - // it('should be unselectable when option is disabled', () => { - // const newAddressOptions = [...addressOptions]; - // newAddressOptions[0] = { - // ...newAddressOptions[0], - // disabled: true, - // }; - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // expect(menu1Items.length).toBe(3); - // expect(selectedValue).toBeFalsy(); + it('should be unselectable when option is disabled', () => { + const newAddressOptions = [...addressOptions]; + newAddressOptions[0] = { + ...newAddressOptions[0], + disabled: true, + }; + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + expect(menu1Items.length).toBe(3); + expect(selectedValue).toBeFalsy(); - // menu1Items.at(0).simulate('click'); - // expect( - // wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-disabled'), - // ).toBe(true); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // }); + menu1Items.at(0).simulate('click'); + expect( + wrapper.find('.rc-cascader-menu-item').first().hasClass('rc-cascader-menu-item-disabled'), + ).toBe(true); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + }); - // it('should have correct active menu items', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - // expect(activeMenuItems.length).toBe(2); - // expect(activeMenuItems.at(0).text()).toBe('1'); - // expect(activeMenuItems.at(1).text()).toBe('2'); - // const menus = wrapper.find('.rc-cascader-menu'); - // const activeMenuItemsInMenu1 = menus.at(0).find('.rc-cascader-menu-item-active'); - // expect(activeMenuItemsInMenu1.length).toBe(1); - // }); + it('should have correct active menu items', () => { + // FIXME: Nest value support same key which is not same as TreeSelect design + // const wrapper = mount( + // + // + // , + // ); + // wrapper.find('input').simulate('click'); + // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + // expect(activeMenuItems.length).toBe(2); + // expect(activeMenuItems.at(0).text()).toBe('1'); + // expect(activeMenuItems.at(1).text()).toBe('2'); + // const menus = wrapper.find('.rc-cascader-menu'); + // const activeMenuItemsInMenu1 = menus.at(0).find('.rc-cascader-menu-item-active'); + // expect(activeMenuItemsInMenu1.length).toBe(1); + }); // // https://github.com/ant-design/ant-design/issues/5666 // it('should have not change active value when value is not changed', () => { From 9296a4d0279aa1a72551b5722e84deb88c8eab7c Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 17:18:15 +0800 Subject: [PATCH 21/54] chore: Parse value on first time --- src/Cascader.tsx | 4 ++-- src/OptionList/index.tsx | 1 - src/util.ts | 26 ++++++++++++++++++++++++++ tests/index.spec.js | 18 +++++++++--------- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 7d9753ec..ffb67b2e 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -8,7 +8,7 @@ import type { RefSelectProps } from 'rc-select/lib/generate'; import OptionList from './OptionList'; import type { CascaderValueType, DataNode } from './interface'; import CascaderContext from './context'; -import { restoreCompatibleValue } from './util'; +import { convertOptions, restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; /** @@ -171,7 +171,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { - return options || []; + return convertOptions(options); }, [options]); // =========================== Value ============================ diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index aadc104a..0b6e27d5 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -20,7 +20,6 @@ const RefOptionList = React.forwardRef { + const newValue = [parentValue, option.value].join(split); + const cloneOption = { + ...option, + value: newValue, + }; + + if (cloneOption.children) { + cloneOption.children = injectValue(cloneOption.children, newValue); + } + + return cloneOption; + }); + } + + return injectValue(options); +} diff --git a/tests/index.spec.js b/tests/index.spec.js index 4f086188..8cdc4c78 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -349,15 +349,15 @@ describe('Cascader', () => { it('should have correct active menu items', () => { // FIXME: Nest value support same key which is not same as TreeSelect design - // const wrapper = mount( - // - // - // , - // ); + const wrapper = mount( + + + , + ); // wrapper.find('input').simulate('click'); // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); // expect(activeMenuItems.length).toBe(2); From cc1f7a01ee2a83c331822e8bbabd554f63a5c628 Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 18:36:33 +0800 Subject: [PATCH 22/54] chore: Internal value parser --- src/Cascader.tsx | 8 ++++---- src/interface.ts | 4 ++++ src/util.ts | 22 ++++++++++++++++------ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index ffb67b2e..f89391d8 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -8,7 +8,7 @@ import type { RefSelectProps } from 'rc-select/lib/generate'; import OptionList from './OptionList'; import type { CascaderValueType, DataNode } from './interface'; import CascaderContext from './context'; -import { convertOptions, restoreCompatibleValue } from './util'; +import { connectValue, convertOptions, restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; /** @@ -177,8 +177,8 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref ['little'] - * - multiple: [['light', 'little'], ['bamboo']] => ['little', 'bamboo'] + * - single: ['light', 'little'] => ['light__little'] + * - multiple: [['light', 'little'], ['bamboo']] => ['light__little', 'bamboo'] */ const parseToInternalValue = ( propValue?: CascaderValueType | CascaderValueType[], @@ -188,7 +188,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref pathValue[pathValue.length - 1]); + return propValueList.map(connectValue); }; const [internalValue, setInternalValue] = React.useState(() => diff --git a/src/interface.ts b/src/interface.ts index d3f1d128..29c51e8e 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -10,6 +10,10 @@ export interface DataNode { isLeaf?: boolean; } +export interface InternalDataNode extends DataNode { + node: DataNode; +} + export interface OptionDataNode extends Omit { title: React.ReactNode; children?: OptionDataNode[]; diff --git a/src/util.ts b/src/util.ts index fc7e79b2..7473f122 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,7 @@ import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; -import type { CascaderValueType, DataNode, OptionDataNode } from './interface'; +import type { CascaderValueType, DataNode, InternalDataNode, OptionDataNode } from './interface'; + +const VALUE_SPLIT = '__RC_CASCADER_SPLIT__'; export function restoreCompatibleValue(entity: FlattenDataNode): { path: CascaderValueType; @@ -25,19 +27,27 @@ export function isLeaf(option: DataNode | OptionDataNode) { return leaf !== undefined ? leaf : !children?.length; } +/** + * We will connect path value to a string. e.g. + * ['little', 'bamboo'] => 'little__bamboo' + */ +export function connectValue(value: React.Key[]) { + return (value || []).join(VALUE_SPLIT); +} + /** * Fill options with fully value by path to avoid nest entity with same value. + * Which means we need another round to get origin node back! * This is slow perf on large list. We should abandon same value in nest in future. */ -export function convertOptions(options: DataNode[]) { - const split = '__RC_CASCADER_SPLIT__'; - - function injectValue(list: DataNode[], parentValue = '') { +export function convertOptions(options: DataNode[]): InternalDataNode[] { + function injectValue(list: DataNode[], parentValue = ''): InternalDataNode[] { return (list || []).map(option => { - const newValue = [parentValue, option.value].join(split); + const newValue = parentValue ? connectValue([parentValue, option.value]) : option.value; const cloneOption = { ...option, value: newValue, + node: option, }; if (cloneOption.children) { From c727febda35223f0fa2ccd6cae695acc9715d1e3 Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 19 Aug 2021 18:59:30 +0800 Subject: [PATCH 23/54] fix: proxy should keep the nest value as path key --- src/Cascader.tsx | 8 +++++--- src/interface.ts | 2 +- src/util.ts | 10 +++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Cascader.tsx b/src/Cascader.tsx index f89391d8..c6520224 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -224,9 +224,11 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref entity); valueEntities.forEach(entity => { - const { path, options: valueOptions } = restoreCompatibleValue(entity); - pathList.push(path); - optionsList.push(valueOptions); + const { options: valueOptions } = restoreCompatibleValue(entity); + const originOptions = valueOptions.map(option => option.node); + + pathList.push(originOptions.map(opt => opt.value)); + optionsList.push(originOptions); }); // Fill state diff --git a/src/interface.ts b/src/interface.ts index 29c51e8e..75fd55a3 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -14,7 +14,7 @@ export interface InternalDataNode extends DataNode { node: DataNode; } -export interface OptionDataNode extends Omit { +export interface OptionDataNode extends Omit { title: React.ReactNode; children?: OptionDataNode[]; } diff --git a/src/util.ts b/src/util.ts index 7473f122..09080d3a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -3,18 +3,22 @@ import type { CascaderValueType, DataNode, InternalDataNode, OptionDataNode } fr const VALUE_SPLIT = '__RC_CASCADER_SPLIT__'; +/** + * Convert entity back to path & options + * @returns + */ export function restoreCompatibleValue(entity: FlattenDataNode): { path: CascaderValueType; - options: DataNode[]; + options: InternalDataNode[]; } { const path: CascaderValueType = []; - const options: DataNode[] = []; + const options: InternalDataNode[] = []; let current = entity; while (current) { path.unshift(current.data.node.value); - options.unshift(current.data.node as DataNode); + options.unshift(current.data.node as any as InternalDataNode); current = current.parent; } From ae5ab7ff28d4f6be91c1c56ad52e56313985ae1d Mon Sep 17 00:00:00 2001 From: zombiej Date: Fri, 20 Aug 2021 12:03:57 +0800 Subject: [PATCH 24/54] fix: hover & selecOnChange logic --- examples/change-on-select.jsx | 4 +- src/OptionList/index.tsx | 5 +- src/util.ts | 2 + tests/index.spec.js | 143 ++++++++++++++++------------------ 4 files changed, 76 insertions(+), 78 deletions(-) diff --git a/examples/change-on-select.jsx b/examples/change-on-select.jsx index ec808259..cc950b3a 100644 --- a/examples/change-on-select.jsx +++ b/examples/change-on-select.jsx @@ -71,10 +71,10 @@ class Demo extends React.Component { render() { return ( console.log('loadData')} > diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 0b6e27d5..6df228e4 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -5,6 +5,7 @@ import type { OptionDataNode } from '../interface'; import Column from './Column'; import { restoreCompatibleValue } from '../util'; import SearchResult from './SearchResult'; +import CascaderContext from '../context'; const RefOptionList = React.forwardRef>( (props, ref) => { @@ -21,6 +22,7 @@ const RefOptionList = React.forwardRef new Set(checkedKeys), [checkedKeys]); @@ -55,7 +57,7 @@ const RefOptionList = React.forwardRef { onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); - if (!multiple && isLeaf) { + if (!multiple && (isLeaf || (changeOnSelect && expandTrigger === 'hover'))) { onToggleOpen(false); } }; @@ -103,6 +105,7 @@ const RefOptionList = React.forwardRef 'little__bamboo' + * + * zombieJ: It's better to deprecate the same key in the nest tree. Maybe next major version. */ export function connectValue(value: React.Key[]) { return (value || []).join(VALUE_SPLIT); diff --git a/tests/index.spec.js b/tests/index.spec.js index 8cdc4c78..0ee75a0f 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -348,7 +348,6 @@ describe('Cascader', () => { }); it('should have correct active menu items', () => { - // FIXME: Nest value support same key which is not same as TreeSelect design const wrapper = mount( { , ); - // wrapper.find('input').simulate('click'); - // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - // expect(activeMenuItems.length).toBe(2); - // expect(activeMenuItems.at(0).text()).toBe('1'); - // expect(activeMenuItems.at(1).text()).toBe('2'); - // const menus = wrapper.find('.rc-cascader-menu'); - // const activeMenuItemsInMenu1 = menus.at(0).find('.rc-cascader-menu-item-active'); - // expect(activeMenuItemsInMenu1.length).toBe(1); + wrapper.find('input').simulate('click'); + const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + expect(activeMenuItems.length).toBe(2); + expect(activeMenuItems.at(0).text()).toBe('1'); + expect(activeMenuItems.at(1).text()).toBe('2'); + const menus = wrapper.find('.rc-cascader-menu'); + const activeMenuItemsInMenu1 = menus.at(0).find('.rc-cascader-menu-item-active'); + expect(activeMenuItemsInMenu1.length).toBe(1); }); - // // https://github.com/ant-design/ant-design/issues/5666 - // it('should have not change active value when value is not changed', () => { - // class Demo extends React.Component { - // state = { - // value: [], - // }; - - // componentDidMount() { - // this.timeout = setTimeout(() => { - // this.setState({ - // value: [], - // }); - // }, 10); - // } - - // componentWillUnmount() { - // clearTimeout(this.timeout); - // } - - // getPopupDOMNode() { - // return this.cascader.getPopupDOMNode(); - // } - - // render() { - // return ( - // { - // this.cascader = node; - // }} - // > - // - // - // ); - // } - // } - // const wrapper = mount(); - // wrapper.find('input').simulate('click'); - // let menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // menu1Items.at(0).simulate('click'); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); + // https://github.com/ant-design/ant-design/issues/5666 + it('should have not change active value when value is not changed', () => { + class Demo extends React.Component { + state = { + value: [], + }; + + componentDidMount() { + this.timeout = setTimeout(() => { + this.setState({ + value: [], + }); + }, 10); + } + + componentWillUnmount() { + clearTimeout(this.timeout); + } + + render() { + return ( + + + + ); + } + } + const wrapper = mount(); + wrapper.find('input').simulate('click'); + let menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); - // jest.runAllTimers(); - // wrapper.update(); - // menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(2); - // }); + wrapper.clickOption(0, 0); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); - // // https://github.com/ant-design/ant-design/issues/7480 - // it('should call onChange on click when expandTrigger=hover with changeOnSelect', () => { - // const wrapper = mount( - // - // - // , - // ); + jest.runAllTimers(); + wrapper.update(); + menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(2); + }); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // expect(menu1Items.length).toBe(3); - // menu1Items.at(0).simulate('click'); - // expect(selectedValue[0]).toBe('fj'); - // expect(wrapper.isOpen()).toBeFalsy(); - // }); + // https://github.com/ant-design/ant-design/issues/7480 + it('should call onChange on click when expandTrigger=hover with changeOnSelect', () => { + const wrapper = mount( + + + , + ); + + wrapper.find('input').simulate('click'); + const menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + expect(menu1Items.length).toBe(3); + + wrapper.clickOption(0, 0); + expect(selectedValue[0]).toBe('fj'); + expect(wrapper.isOpen()).toBeFalsy(); + }); // it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { // const wrapper = mount( From 037c8491b0af236bc4f648930da66f8a88551e9e Mon Sep 17 00:00:00 2001 From: zombiej Date: Fri, 20 Aug 2021 15:37:13 +0800 Subject: [PATCH 25/54] fix: fieldNames match --- examples/debug.tsx | 2 +- src/Cascader.tsx | 60 +++++++++++++++--------------- src/OptionList/index.tsx | 4 +- src/context.ts | 3 +- src/interface.ts | 6 +++ src/util.ts | 27 +++++++++----- tests/enzyme.ts | 14 +++++++ tests/fieldNames.spec.tsx | 41 ++++++++++++++++++++ tests/index.spec.js | 78 +++++++++++++++++++-------------------- 9 files changed, 153 insertions(+), 82 deletions(-) create mode 100644 tests/enzyme.ts create mode 100644 tests/fieldNames.spec.tsx diff --git a/examples/debug.tsx b/examples/debug.tsx index 31e1ec97..1afa6d13 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -112,7 +112,7 @@ const Demo = () => { multiple={multiple} allowClear defaultValue={multiple ? [defaultValue] : defaultValue} - showSearch + // showSearch /> ); diff --git a/src/Cascader.tsx b/src/Cascader.tsx index c6520224..9e6079a8 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -4,9 +4,10 @@ import warning from 'rc-util/lib/warning'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import generate from 'rc-tree-select/lib/generate'; import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; +import { fillFieldNames } from 'rc-tree-select/lib/utils/valueUtil'; import type { RefSelectProps } from 'rc-select/lib/generate'; import OptionList from './OptionList'; -import type { CascaderValueType, DataNode } from './interface'; +import type { CascaderValueType, DataNode, FieldNames } from './interface'; import CascaderContext from './context'; import { connectValue, convertOptions, restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; @@ -31,12 +32,6 @@ const RefCascader = generate({ }); // ====================================== Wrap ====================================== -export interface FieldNames { - value?: string | number; - label?: string; - children?: string; -} - export interface ShowSearchType { filter?: (inputValue: string, path: CascaderValueType, names: FieldNames) => boolean; render?: ( @@ -145,11 +140,14 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref fillFieldNames(fieldNames), [fieldNames]); const context = React.useMemo( - () => ({ changeOnSelect, expandTrigger }), - [changeOnSelect, expandTrigger], + () => ({ changeOnSelect, expandTrigger, fieldNames: mergedFieldNames }), + [changeOnSelect, expandTrigger, mergedFieldNames], ); // ============================ Ref ============================= @@ -171,8 +169,8 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { - return convertOptions(options); - }, [options]); + return convertOptions(options, mergedFieldNames); + }, [options, mergedFieldNames]); // =========================== Value ============================ /** @@ -201,12 +199,14 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { + const { label: fieldLabel } = mergedFieldNames; + if (multiple) { - return entity.data.node.label; + return entity.data.node[fieldLabel]; } - return restoreCompatibleValue(entity) - .options.map(opt => opt.label) + return restoreCompatibleValue(entity, mergedFieldNames) + .options.map(opt => opt[fieldLabel]) .join('>'); }; @@ -215,27 +215,27 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref entity); + const valueEntities = valueList.map(getEntityByValue).filter(entity => entity); - valueEntities.forEach(entity => { - const { options: valueOptions } = restoreCompatibleValue(entity); - const originOptions = valueOptions.map(option => option.node); + valueEntities.forEach(entity => { + const { options: valueOptions } = restoreCompatibleValue(entity, mergedFieldNames); + const originOptions = valueOptions.map(option => option.node); - pathList.push(originOptions.map(opt => opt.value)); - optionsList.push(originOptions); - }); + pathList.push(originOptions.map(opt => opt[mergedFieldNames.value])); + optionsList.push(originOptions); + }); - // Fill state - if (value === undefined) { - setInternalValue(valueList); - } + // Fill state + if (value === undefined) { + setInternalValue(valueList); + } + if (onChange) { if (multiple) { (onChange as OnMultipleChange)(pathList, optionsList); } else { diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 6df228e4..0cce007a 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -22,7 +22,7 @@ const RefOptionList = React.forwardRef new Set(checkedKeys), [checkedKeys]); @@ -41,7 +41,7 @@ const RefOptionList = React.forwardRef + Required> >({ changeOnSelect: false, expandTrigger: 'click', + fieldNames: null, }); export default CascaderContext; diff --git a/src/interface.ts b/src/interface.ts index 75fd55a3..90b9d2af 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -1,5 +1,11 @@ import type * as React from 'react'; +export interface FieldNames { + value?: string; + label?: string; + children?: string; +} + export type CascaderValueType = React.Key[]; export interface DataNode { diff --git a/src/util.ts b/src/util.ts index 2fecc948..40855522 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,11 @@ import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; -import type { CascaderValueType, DataNode, InternalDataNode, OptionDataNode } from './interface'; +import type { + CascaderValueType, + DataNode, + FieldNames, + InternalDataNode, + OptionDataNode, +} from './interface'; const VALUE_SPLIT = '__RC_CASCADER_SPLIT__'; @@ -7,7 +13,10 @@ const VALUE_SPLIT = '__RC_CASCADER_SPLIT__'; * Convert entity back to path & options * @returns */ -export function restoreCompatibleValue(entity: FlattenDataNode): { +export function restoreCompatibleValue( + entity: FlattenDataNode, + fieldNames: FieldNames, +): { path: CascaderValueType; options: InternalDataNode[]; } { @@ -17,7 +26,7 @@ export function restoreCompatibleValue(entity: FlattenDataNode): { let current = entity; while (current) { - path.unshift(current.data.node.value); + path.unshift(current.data.node[fieldNames.value]); options.unshift(current.data.node as any as InternalDataNode); current = current.parent; @@ -34,7 +43,7 @@ export function isLeaf(option: DataNode | OptionDataNode) { /** * We will connect path value to a string. e.g. * ['little', 'bamboo'] => 'little__bamboo' - * + * * zombieJ: It's better to deprecate the same key in the nest tree. Maybe next major version. */ export function connectValue(value: React.Key[]) { @@ -46,18 +55,18 @@ export function connectValue(value: React.Key[]) { * Which means we need another round to get origin node back! * This is slow perf on large list. We should abandon same value in nest in future. */ -export function convertOptions(options: DataNode[]): InternalDataNode[] { +export function convertOptions(options: DataNode[], { value: fieldValue, children: fieldChildren }: FieldNames): InternalDataNode[] { function injectValue(list: DataNode[], parentValue = ''): InternalDataNode[] { return (list || []).map(option => { - const newValue = parentValue ? connectValue([parentValue, option.value]) : option.value; + const newValue = parentValue ? connectValue([parentValue, option[fieldValue]]) : option[fieldValue]; const cloneOption = { ...option, - value: newValue, + [fieldValue]: newValue, node: option, }; - if (cloneOption.children) { - cloneOption.children = injectValue(cloneOption.children, newValue); + if (cloneOption[fieldChildren]) { + cloneOption[fieldChildren] = injectValue(cloneOption[fieldChildren], newValue); } return cloneOption; diff --git a/tests/enzyme.ts b/tests/enzyme.ts new file mode 100644 index 00000000..5a06d838 --- /dev/null +++ b/tests/enzyme.ts @@ -0,0 +1,14 @@ +import { mount as originMount } from 'enzyme'; +import type { ReactWrapper as OriginReactWrapper } from 'enzyme'; + +type ReplaceReturnType any, TNewReturn> = ( + ...a: Parameters +) => TNewReturn; + +interface ReactWrapper extends OriginReactWrapper { + isOpen: () => boolean; +} + +type Mount = ReplaceReturnType; + +export const mount = originMount as Mount; diff --git a/tests/fieldNames.spec.tsx b/tests/fieldNames.spec.tsx new file mode 100644 index 00000000..3f6a3a91 --- /dev/null +++ b/tests/fieldNames.spec.tsx @@ -0,0 +1,41 @@ +/* eslint-disable react/jsx-no-bind */ + +import React from 'react'; +import { mount } from './enzyme'; +import Cascader from '../src'; + +describe('Cascader.FieldNames', () => { + it('customize', () => { + const options = [ + { + customTitle: 'Light', + customValue: 'light', + }, + { + customTitle: 'Bamboo', + customValue: 'bamboo', + customChildren: [ + { + customTitle: 'Little', + customValue: 'little', + }, + ], + }, + ]; + + const wrapper = mount( + , + ); + + // Open + wrapper.find('.rc-cascader-selector').simulate('mousedown'); + expect(wrapper.isOpen()).toBeTruthy(); + + // Check values + expect(wrapper.find('.rc-cascader-menu')).toHaveLength(1); + expect(wrapper.find('.rc-cascader-menu').at(0).find('.rc-cascader-menu-item')).toHaveLength(2); + }); +}); diff --git a/tests/index.spec.js b/tests/index.spec.js index 0ee75a0f..1ac9db83 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -431,24 +431,24 @@ describe('Cascader', () => { expect(wrapper.isOpen()).toBeFalsy(); }); - // it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { - // const wrapper = mount( - // - // - // , - // ); + it('should not call onChange on hover when expandTrigger=hover with changeOnSelect', () => { + const wrapper = mount( + + + , + ); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(1); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // expect(menu1Items.length).toBe(3); - // menu1Items.at(0).simulate('mouseEnter'); - // jest.runAllTimers(); - // wrapper.update(); - // expect(selectedValue).toBeFalsy(); - // expect(wrapper.isOpen()).toBeTruthy(); - // }); + wrapper.find('input').simulate('click'); + const menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(1); + const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + expect(menu1Items.length).toBe(3); + menu1Items.at(0).simulate('mouseEnter'); + jest.runAllTimers(); + wrapper.update(); + expect(selectedValue).toBeFalsy(); + expect(wrapper.isOpen()).toBeTruthy(); + }); // it('should has default fieldName when props not exist labelField and valueField and childrenField', () => { // const wrapper = mount( @@ -462,28 +462,28 @@ describe('Cascader', () => { // expect(props.fieldNames.children).toBe('children'); // }); - // it('should support custom fieldNames', () => { - // const wrapper = mount( - // - // - // , - // ); - // const props = wrapper.props(); - // expect(props.fieldNames.label).toBe('name'); - // expect(props.fieldNames.value).toBe('code'); - // expect(props.fieldNames.children).toBe('nodes'); - // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - // expect(activeMenuItems.length).toBe(3); - // expect(activeMenuItems.at(0).text()).toBe('福建'); - // expect(activeMenuItems.at(1).text()).toBe('福州'); - // expect(activeMenuItems.at(2).text()).toBe('马尾'); - // }); + it('should support custom fieldNames', () => { + const wrapper = mount( + + + , + ); + const props = wrapper.props(); + expect(props.fieldNames.label).toBe('name'); + expect(props.fieldNames.value).toBe('code'); + expect(props.fieldNames.children).toBe('nodes'); + const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + expect(activeMenuItems.length).toBe(3); + expect(activeMenuItems.at(0).text()).toBe('福建'); + expect(activeMenuItems.at(1).text()).toBe('福州'); + expect(activeMenuItems.at(2).text()).toBe('马尾'); + }); // it('should works and show warning message when use typo prop name: filedNames', () => { // // eslint-disable-next-line no-console From 8f2b4f3904540f3021e3877b3a075409eef36c22 Mon Sep 17 00:00:00 2001 From: zombiej Date: Fri, 20 Aug 2021 16:00:46 +0800 Subject: [PATCH 26/54] test: for fieldNames --- tests/enzyme.ts | 1 + tests/fieldNames.spec.tsx | 87 +++++++++++++++++++++++++++++---------- tests/index.spec.js | 11 ----- 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/tests/enzyme.ts b/tests/enzyme.ts index 5a06d838..a3203e09 100644 --- a/tests/enzyme.ts +++ b/tests/enzyme.ts @@ -7,6 +7,7 @@ type ReplaceReturnType any, TNewReturn> = ( interface ReactWrapper extends OriginReactWrapper { isOpen: () => boolean; + clickOption: (menuIndex: number, itemIndex: number) => ReactWrapper; } type Mount = ReplaceReturnType; diff --git a/tests/fieldNames.spec.tsx b/tests/fieldNames.spec.tsx index 3f6a3a91..e3e0ec27 100644 --- a/tests/fieldNames.spec.tsx +++ b/tests/fieldNames.spec.tsx @@ -5,29 +5,39 @@ import { mount } from './enzyme'; import Cascader from '../src'; describe('Cascader.FieldNames', () => { - it('customize', () => { - const options = [ - { - customTitle: 'Light', - customValue: 'light', - }, - { - customTitle: 'Bamboo', - customValue: 'bamboo', - customChildren: [ - { - customTitle: 'Little', - customValue: 'little', - }, - ], - }, - ]; + const options = [ + { + customTitle: 'Light', + customValue: 'light', + }, + { + customTitle: 'Bamboo', + customValue: 'bamboo', + customChildren: [ + { + customTitle: 'Little', + customValue: 'little', + customChildren: [ + { + customTitle: 'Toy', + customValue: 'toy', + }, + ], + }, + ], + }, + ] as any; + + const fieldNames = { + label: 'customTitle', + value: 'customValue', + children: 'customChildren', + }; + it('customize', () => { + const onChange = jest.fn(); const wrapper = mount( - , + , ); // Open @@ -37,5 +47,40 @@ describe('Cascader.FieldNames', () => { // Check values expect(wrapper.find('.rc-cascader-menu')).toHaveLength(1); expect(wrapper.find('.rc-cascader-menu').at(0).find('.rc-cascader-menu-item')).toHaveLength(2); + + // Click Bamboo + wrapper.clickOption(0, 1); + expect(wrapper.find('.rc-cascader-menu')).toHaveLength(2); + expect(wrapper.find('.rc-cascader-menu').at(1).find('.rc-cascader-menu-item')).toHaveLength(1); + + // Click Little & Toy + wrapper.clickOption(1, 0); + wrapper.clickOption(2, 0); + + expect(onChange).toHaveBeenCalledWith( + ['bamboo', 'little', 'toy'], + [ + expect.objectContaining({ customTitle: 'Bamboo', customValue: 'bamboo' }), + expect.objectContaining({ customTitle: 'Little', customValue: 'little' }), + expect.objectContaining({ customTitle: 'Toy', customValue: 'toy' }), + ], + ); + }); + + it('defaultValue', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('.rc-cascader-menu')).toHaveLength(3); + expect(wrapper.find('.rc-cascader-menu-item-active')).toHaveLength(3); + expect(wrapper.find('.rc-cascader-menu-item-active').at(0).text()).toEqual('Bamboo'); + expect(wrapper.find('.rc-cascader-menu-item-active').at(1).text()).toEqual('Little'); + expect(wrapper.find('.rc-cascader-menu-item-active').at(2).text()).toEqual('Toy'); }); }); diff --git a/tests/index.spec.js b/tests/index.spec.js index 1ac9db83..2c2d9699 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -450,17 +450,6 @@ describe('Cascader', () => { expect(wrapper.isOpen()).toBeTruthy(); }); - // it('should has default fieldName when props not exist labelField and valueField and childrenField', () => { - // const wrapper = mount( - // - // - // , - // ); - // const props = wrapper.props(); - // expect(props.fieldNames.label).toBe('label'); - // expect(props.fieldNames.value).toBe('value'); - // expect(props.fieldNames.children).toBe('children'); - // }); it('should support custom fieldNames', () => { const wrapper = mount( From 51079711156f7ad5314ad4855c25ff51aeb7e0bc Mon Sep 17 00:00:00 2001 From: zombiej Date: Fri, 20 Aug 2021 16:41:09 +0800 Subject: [PATCH 27/54] chore: add warning test --- assets/list.less | 9 +++++ src/Cascader.tsx | 10 +++--- src/OptionList/Column.tsx | 5 ++- src/context.ts | 3 +- tests/fieldNames.spec.tsx | 1 + tests/index.spec.js | 69 +++++++++------------------------------ 6 files changed, 35 insertions(+), 62 deletions(-) diff --git a/assets/list.less b/assets/list.less index ff86e412..8cbf17f3 100644 --- a/assets/list.less +++ b/assets/list.less @@ -24,6 +24,8 @@ &-item { display: flex; flex-wrap: nowrap; + padding-right: 20px; + position: relative; &-active { background: rgba(0, 255, 0, 0.1); @@ -65,6 +67,13 @@ &-content { flex: auto; } + + &-expand-icon { + position: absolute; + right: 4px; + top: 50%; + transform: translateY(-50%); + } } } } diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 9e6079a8..bbedd6ba 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -23,7 +23,6 @@ import useUpdateEffect from './hooks/useUpdateEffect'; * Deprecated: * - popupVisible * - onPopupVisibleChange - * - filedNames */ const RefCascader = generate({ @@ -63,8 +62,6 @@ interface BaseCascaderProps allowClear?: boolean; disabled?: boolean; - /** @deprecated Typo. Please use `fieldNames` instead. */ - filedNames?: FieldNames; fieldNames?: FieldNames; // Search @@ -93,7 +90,7 @@ interface BaseCascaderProps // builtinPlacements?: BuildInPlacements; // loadData?: (selectOptions: DataNode[]) => void; - // expandIcon?: React.ReactNode; + expandIcon?: React.ReactNode; // loadingIcon?: React.ReactNode; } @@ -137,6 +134,7 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref fillFieldNames(fieldNames), [fieldNames]); const context = React.useMemo( - () => ({ changeOnSelect, expandTrigger, fieldNames: mergedFieldNames }), - [changeOnSelect, expandTrigger, mergedFieldNames], + () => ({ changeOnSelect, expandTrigger, fieldNames: mergedFieldNames, expandIcon }), + [changeOnSelect, expandTrigger, mergedFieldNames, expandIcon], ); // ============================ Ref ============================= diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 1e71bbaa..1b9dfbb7 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -32,7 +32,7 @@ export default function Column({ const menuItemPrefixCls = `${prefixCls}-menu-item`; const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`; - const { changeOnSelect, expandTrigger } = React.useContext(CascaderContext); + const { changeOnSelect, expandTrigger, expandIcon } = React.useContext(CascaderContext); return (
    @@ -100,6 +100,9 @@ export default function Column({ > {option.title}
+ {expandIcon && !isMergedLeaf && ( +
{expandIcon}
+ )} ); })} diff --git a/src/context.ts b/src/context.ts index 81d79d06..7286a0f7 100644 --- a/src/context.ts +++ b/src/context.ts @@ -2,11 +2,12 @@ import * as React from 'react'; import type { CascaderProps } from './Cascader'; const CascaderContext = React.createContext< - Required> + Required> >({ changeOnSelect: false, expandTrigger: 'click', fieldNames: null, + expandIcon: null, }); export default CascaderContext; diff --git a/tests/fieldNames.spec.tsx b/tests/fieldNames.spec.tsx index e3e0ec27..87274a20 100644 --- a/tests/fieldNames.spec.tsx +++ b/tests/fieldNames.spec.tsx @@ -73,6 +73,7 @@ describe('Cascader.FieldNames', () => { options={options} fieldNames={fieldNames} defaultValue={['bamboo', 'little', 'toy']} + expandIcon="" open />, ); diff --git a/tests/index.spec.js b/tests/index.spec.js index 2c2d9699..3753c342 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -1,6 +1,7 @@ /* eslint-disable react/jsx-no-bind */ import React from 'react'; +import { resetWarned } from 'rc-util/lib/warning'; import { mount } from 'enzyme'; import Cascader from '..'; import { @@ -349,11 +350,7 @@ describe('Cascader', () => { it('should have correct active menu items', () => { const wrapper = mount( - + , ); @@ -388,10 +385,7 @@ describe('Cascader', () => { render() { return ( - + ); @@ -450,54 +444,21 @@ describe('Cascader', () => { expect(wrapper.isOpen()).toBeTruthy(); }); + it('warning popupVisible & onPopupVisibleChange', () => { + resetWarned(); + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const onPopupVisibleChange = jest.fn(); + mount(); - it('should support custom fieldNames', () => { - const wrapper = mount( - - - , + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: `onPopupVisibleChange` is deprecated. Please use `onDropdownVisibleChange` instead.', + ); + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: `popupVisible` is deprecated. Please use `open` instead.', ); - const props = wrapper.props(); - expect(props.fieldNames.label).toBe('name'); - expect(props.fieldNames.value).toBe('code'); - expect(props.fieldNames.children).toBe('nodes'); - const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - expect(activeMenuItems.length).toBe(3); - expect(activeMenuItems.at(0).text()).toBe('福建'); - expect(activeMenuItems.at(1).text()).toBe('福州'); - expect(activeMenuItems.at(2).text()).toBe('马尾'); - }); - // it('should works and show warning message when use typo prop name: filedNames', () => { - // // eslint-disable-next-line no-console - // console.error = jest.fn(); - // const wrapper = mount( - // - // - // , - // ); - // // eslint-disable-next-line no-console - // expect(console.error).toHaveBeenCalled(); - // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - // expect(activeMenuItems.length).toBe(3); - // expect(activeMenuItems.at(0).text()).toBe('福建'); - // expect(activeMenuItems.at(1).text()).toBe('福州'); - // expect(activeMenuItems.at(2).text()).toBe('马尾'); - // // eslint-disable-next-line no-console - // console.error.mockClear(); - // }); + errorSpy.mockRestore(); + }); // it('should support custom expand icon(text icon)', () => { // const wrapper = mount( From 1003459e35067f1bfae4ce1d12a2f0aa519dc87b Mon Sep 17 00:00:00 2001 From: zombiej Date: Fri, 20 Aug 2021 16:46:44 +0800 Subject: [PATCH 28/54] fix: dbl click should close changeOnSelect --- src/OptionList/Column.tsx | 13 ++++++-- src/OptionList/index.tsx | 1 + tests/enzyme.ts | 2 +- tests/index.spec.js | 64 +++++++++++++++++++-------------------- tests/setup.js | 4 +-- 5 files changed, 46 insertions(+), 38 deletions(-) diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 1b9dfbb7..6244459c 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -13,6 +13,7 @@ export interface ColumnProps { openKey?: React.Key; onSelect: (value: React.Key, isLeaf: boolean) => void; onOpen: (index: number, value: React.Key) => void; + onToggleOpen: (open: boolean) => void; checkedSet: Set; halfCheckedSet: Set; } @@ -25,6 +26,7 @@ export default function Column({ openKey, onSelect, onOpen, + onToggleOpen, checkedSet, halfCheckedSet, }: ColumnProps) { @@ -44,7 +46,7 @@ export default function Column({ const checked = checkedSet.has(value); // >>>>> Open - const triggerOpen = () => { + const triggerOpenPath = () => { if (!disabled && !isMergedLeaf) { onOpen(index, value); } @@ -70,7 +72,7 @@ export default function Column({ aria-checked={checked} onMouseEnter={() => { if (expandTrigger) { - triggerOpen(); + triggerOpenPath(); } }} > @@ -92,11 +94,16 @@ export default function Column({
{ - triggerOpen(); + triggerOpenPath(); if (!multiple || isMergedLeaf) { triggerSelect(); } }} + onDoubleClick={() => { + if (changeOnSelect) { + onToggleOpen(false); + } + }} > {option.title}
diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 0cce007a..93643b2f 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -88,6 +88,7 @@ const RefOptionList = React.forwardRef any, TNewReturn> = ( interface ReactWrapper extends OriginReactWrapper { isOpen: () => boolean; - clickOption: (menuIndex: number, itemIndex: number) => ReactWrapper; + clickOption: (menuIndex: number, itemIndex: number, type?: 'click' | 'doubleClick') => ReactWrapper; } type Mount = ReplaceReturnType; diff --git a/tests/index.spec.js b/tests/index.spec.js index 3753c342..dac1656e 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -460,40 +460,40 @@ describe('Cascader', () => { errorSpy.mockRestore(); }); - // it('should support custom expand icon(text icon)', () => { - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // expect(menus.length).toBe(3); - // const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); - // expect(activeMenuItems.length).toBe(3); - // expect(activeMenuItems.at(0).text()).toBe('福建=>'); - // expect(activeMenuItems.at(1).text()).toBe('福州=>'); - // expect(activeMenuItems.at(2).text()).toBe('马尾'); - // }); + it('should support custom expand icon(text icon)', () => { + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + const menus = wrapper.find('.rc-cascader-menu'); + expect(menus.length).toBe(3); + const activeMenuItems = wrapper.find('.rc-cascader-menu-item-active'); + expect(activeMenuItems.length).toBe(3); + expect(activeMenuItems.at(0).text()).toBe('福建=>'); + expect(activeMenuItems.at(1).text()).toBe('福州=>'); + expect(activeMenuItems.at(2).text()).toBe('马尾'); + }); - // it('should close popup on double click when changeOnSelect is set', () => { - // const wrapper = mount( - // - // - // , - // ); + it('should close popup on double click when changeOnSelect is set', () => { + const wrapper = mount( + + + , + ); - // expect(wrapper.isOpen()).toBeFalsy(); - // wrapper.find('input').simulate('click'); - // expect(wrapper.isOpen()).toBeTruthy(); - // wrapper.find('li').at(0).simulate('doubleClick'); - // expect(wrapper.isOpen()).toBeFalsy(); - // }); + expect(wrapper.isOpen()).toBeFalsy(); + wrapper.find('input').simulate('click'); + expect(wrapper.isOpen()).toBeTruthy(); + wrapper.clickOption(0, 0, 'doubleClick'); + expect(wrapper.isOpen()).toBeFalsy(); + }); // // https://github.com/ant-design/ant-design/issues/9084 // it('should trigger loadData when expandTrigger is hover', () => { diff --git a/tests/setup.js b/tests/setup.js index 4d53cc86..7e01e9c0 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -13,11 +13,11 @@ Object.assign(Enzyme.ReactWrapper.prototype, { isOpen() { return !!this.find('Trigger').props().popupVisible; }, - clickOption(menuIndex, itemIndex) { + clickOption(menuIndex, itemIndex, type = 'click') { const menu = this.find('ul.rc-cascader-menu').at(menuIndex); const itemList = menu.find('li.rc-cascader-menu-item'); - itemList.at(itemIndex).find('div').first().simulate('click'); + itemList.at(itemIndex).find('div').first().simulate(type); return this; }, From ffafefe7047613c5535016755327f1ba26afa704 Mon Sep 17 00:00:00 2001 From: zombiej Date: Fri, 20 Aug 2021 18:16:07 +0800 Subject: [PATCH 29/54] chore: tmp of loadData --- examples/dynamic-options.jsx | 5 +++-- src/Cascader.tsx | 27 +++++++++++++++++++-------- src/OptionList/Column.tsx | 12 ++++++++++-- src/OptionList/SearchResult.tsx | 1 + src/OptionList/index.tsx | 33 ++++++++++++++++++++++++++++++--- src/context.ts | 9 ++++++++- src/util.ts | 16 ++++++++++++---- 7 files changed, 83 insertions(+), 20 deletions(-) diff --git a/examples/dynamic-options.jsx b/examples/dynamic-options.jsx index 2420286f..5957a6ec 100644 --- a/examples/dynamic-options.jsx +++ b/examples/dynamic-options.jsx @@ -23,13 +23,14 @@ class Demo extends React.Component { }; onChange = (value, selectedOptions) => { - console.log(value, selectedOptions); + console.log('OnChange:', value, selectedOptions); this.setState({ inputValue: selectedOptions.map(o => o.label).join(', '), }); }; loadData = selectedOptions => { + console.log('onLoad:', selectedOptions); const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; // 动态加载下级数据 @@ -49,7 +50,7 @@ class Demo extends React.Component { // eslint-disable-next-line react/no-access-state-in-setstate options: [...this.state.options], }); - }, 1000); + }, 3000); }; render() { diff --git a/src/Cascader.tsx b/src/Cascader.tsx index bbedd6ba..d746df1d 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -7,7 +7,7 @@ import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; import { fillFieldNames } from 'rc-tree-select/lib/utils/valueUtil'; import type { RefSelectProps } from 'rc-select/lib/generate'; import OptionList from './OptionList'; -import type { CascaderValueType, DataNode, FieldNames } from './interface'; +import type { CascaderValueType, DataNode, FieldNames, InternalDataNode } from './interface'; import CascaderContext from './context'; import { connectValue, convertOptions, restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; @@ -88,10 +88,10 @@ interface BaseCascaderProps // dropdownMenuColumnStyle?: React.CSSProperties; // dropdownRender?: (menu: React.ReactElement) => React.ReactElement; // builtinPlacements?: BuildInPlacements; - // loadData?: (selectOptions: DataNode[]) => void; + loadData?: (selectOptions: DataNode[]) => void; expandIcon?: React.ReactNode; - // loadingIcon?: React.ReactNode; + loadingIcon?: React.ReactNode; } type OnSingleChange = (value: CascaderValueType, selectOptions: DataNode[]) => void; @@ -135,6 +135,9 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref fillFieldNames(fieldNames), [fieldNames]); - const context = React.useMemo( - () => ({ changeOnSelect, expandTrigger, fieldNames: mergedFieldNames, expandIcon }), - [changeOnSelect, expandTrigger, mergedFieldNames, expandIcon], - ); - // ============================ Ref ============================= const cascaderRef = React.useRef(); @@ -260,6 +258,19 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref ({ + changeOnSelect, + expandTrigger, + fieldNames: mergedFieldNames, + expandIcon, + loadingIcon, + loadData, + }), + [changeOnSelect, expandTrigger, mergedFieldNames, expandIcon, loadingIcon, loadData], + ); + // =========================== Render =========================== const dropdownStyle: React.CSSProperties = mergedSearch || !mergedOptions.length diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 6244459c..3cb22197 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -16,6 +16,7 @@ export interface ColumnProps { onToggleOpen: (open: boolean) => void; checkedSet: Set; halfCheckedSet: Set; + loadingKeys: React.Key[]; } export default function Column({ @@ -29,12 +30,14 @@ export default function Column({ onToggleOpen, checkedSet, halfCheckedSet, + loadingKeys, }: ColumnProps) { const menuPrefixCls = `${prefixCls}-menu`; const menuItemPrefixCls = `${prefixCls}-menu-item`; const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`; - const { changeOnSelect, expandTrigger, expandIcon } = React.useContext(CascaderContext); + const { changeOnSelect, expandTrigger, expandIcon, loadingIcon } = + React.useContext(CascaderContext); return (
    @@ -42,6 +45,8 @@ export default function Column({ const { disabled, value } = option; const isMergedLeaf = isLeaf(option); + const isLoading = loadingKeys.includes(value); + // >>>>> checked const checked = checkedSet.has(value); @@ -107,9 +112,12 @@ export default function Column({ > {option.title} - {expandIcon && !isMergedLeaf && ( + {!isLoading && expandIcon && !isMergedLeaf && (
    {expandIcon}
    )} + {isLoading && loadingIcon && ( +
    {loadingIcon}
    + )} ); })} diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index 624f1776..a4c65141 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -27,6 +27,7 @@ function flattenOptions(options: OptionDataNode[]): OptionDataNode[] { return list; }, []), value: option.value, + node: option.node, }); } else { dig(option.children, connectLabelPath); diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 93643b2f..b7c178bf 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; -import type { OptionListProps, RefOptionListProps } from 'rc-select/lib/OptionList'; +import type { + OptionListProps as SelectOptionListProps, + RefOptionListProps, +} from 'rc-select/lib/OptionList'; import { SelectContext } from 'rc-tree-select/lib/Context'; import type { OptionDataNode } from '../interface'; import Column from './Column'; @@ -7,7 +10,7 @@ import { restoreCompatibleValue } from '../util'; import SearchResult from './SearchResult'; import CascaderContext from '../context'; -const RefOptionList = React.forwardRef>( +const RefOptionList = React.forwardRef>( (props, ref) => { const { prefixCls, @@ -22,7 +25,27 @@ const RefOptionList = React.forwardRef { + if (!loadData) { + return; + } + + const entity = flattenOptions.find(flattenOption => flattenOption.data.value === pathValue); + if (entity) { + const { options: optionList } = restoreCompatibleValue(entity as any, fieldNames); + const rawOptionList = optionList.map(opt => opt.node); + + setLoadingKeys(keys => [...keys, optionList[optionList.length - 1].value]); + + loadData(rawOptionList); + } + }; // ========================== Values ========================== const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]); @@ -52,6 +75,9 @@ const RefOptionList = React.forwardRef { const nextOpenPath = [...openPath.slice(0, index), pathValue]; setOpenPath(nextOpenPath); + + // Trigger loadData + internalLoadData(pathValue); }; const onPathSelect = (pathValue: React.Key, isLeaf: boolean) => { @@ -91,6 +117,7 @@ const RefOptionList = React.forwardRef>>>> Search diff --git a/src/context.ts b/src/context.ts index 7286a0f7..bf1f8f47 100644 --- a/src/context.ts +++ b/src/context.ts @@ -2,12 +2,19 @@ import * as React from 'react'; import type { CascaderProps } from './Cascader'; const CascaderContext = React.createContext< - Required> + Required< + Pick< + CascaderProps, + 'changeOnSelect' | 'expandTrigger' | 'fieldNames' | 'expandIcon' | 'loadingIcon' | 'loadData' + > + > >({ changeOnSelect: false, expandTrigger: 'click', fieldNames: null, expandIcon: null, + loadingIcon: null, + loadData: null, }); export default CascaderContext; diff --git a/src/util.ts b/src/util.ts index 40855522..0ce18638 100644 --- a/src/util.ts +++ b/src/util.ts @@ -35,8 +35,11 @@ export function restoreCompatibleValue( return { path, options }; } -export function isLeaf(option: DataNode | OptionDataNode) { - const { isLeaf: leaf, children } = option; +export function isLeaf(option: OptionDataNode) { + const { + children, + node: { isLeaf: leaf }, + } = option; return leaf !== undefined ? leaf : !children?.length; } @@ -55,10 +58,15 @@ export function connectValue(value: React.Key[]) { * Which means we need another round to get origin node back! * This is slow perf on large list. We should abandon same value in nest in future. */ -export function convertOptions(options: DataNode[], { value: fieldValue, children: fieldChildren }: FieldNames): InternalDataNode[] { +export function convertOptions( + options: DataNode[], + { value: fieldValue, children: fieldChildren }: FieldNames, +): InternalDataNode[] { function injectValue(list: DataNode[], parentValue = ''): InternalDataNode[] { return (list || []).map(option => { - const newValue = parentValue ? connectValue([parentValue, option[fieldValue]]) : option[fieldValue]; + const newValue = parentValue + ? connectValue([parentValue, option[fieldValue]]) + : option[fieldValue]; const cloneOption = { ...option, [fieldValue]: newValue, From a367ceed92e601e3f1e58b96697d4efda58a08b0 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 23 Aug 2021 11:00:32 +0800 Subject: [PATCH 30/54] chore: Back of loadData --- src/OptionList/index.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index b7c178bf..de7e1f67 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -47,6 +47,18 @@ const RefOptionList = React.forwardRef { + if (loadingKeys.length) { + loadingKeys.forEach(loadingKey => { + const option = flattenOptions.find(opt => opt.value === loadingKey); + if (option.data.children) { + setLoadingKeys(keys => keys.filter(key => key !== loadingKey)); + } + }); + } + }, [flattenOptions, loadingKeys]); + // ========================== Values ========================== const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]); const halfCheckedSet = React.useMemo(() => new Set(halfCheckedKeys), [halfCheckedKeys]); From 8d9abba450efb2add0ac56ae41874977945dbae9 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 23 Aug 2021 11:17:41 +0800 Subject: [PATCH 31/54] fix: empty node isLeaf check logic --- src/util.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util.ts b/src/util.ts index 0ce18638..92f5c4de 100644 --- a/src/util.ts +++ b/src/util.ts @@ -36,10 +36,10 @@ export function restoreCompatibleValue( } export function isLeaf(option: OptionDataNode) { - const { - children, - node: { isLeaf: leaf }, - } = option; + const { children, node } = option; + + const leaf = node?.isLeaf; + return leaf !== undefined ? leaf : !children?.length; } From 3372f2d9074f5ba159456c57f395967f79d5cb1e Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 23 Aug 2021 12:12:24 +0800 Subject: [PATCH 32/54] test: all pass of index --- examples/debug.tsx | 3 +- src/Cascader.tsx | 15 +- src/OptionList/Column.tsx | 11 +- src/interface.ts | 4 +- tests/__snapshots__/index.spec.js.snap | 313 +++++++------------------ tests/index.spec.js | 192 +++++++-------- 6 files changed, 200 insertions(+), 338 deletions(-) diff --git a/examples/debug.tsx b/examples/debug.tsx index 1afa6d13..af732b8b 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -7,6 +7,7 @@ const addressOptions1 = [ { label: '福建', value: 'fj', + title: '测试标题', children: [ { label: '福州', @@ -111,7 +112,7 @@ const Demo = () => { onChange={onChange} multiple={multiple} allowClear - defaultValue={multiple ? [defaultValue] : defaultValue} + // defaultValue={multiple ? [defaultValue] : defaultValue} // showSearch /> diff --git a/src/Cascader.tsx b/src/Cascader.tsx index d746df1d..6b08ce06 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -7,7 +7,7 @@ import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; import { fillFieldNames } from 'rc-tree-select/lib/utils/valueUtil'; import type { RefSelectProps } from 'rc-select/lib/generate'; import OptionList from './OptionList'; -import type { CascaderValueType, DataNode, FieldNames, InternalDataNode } from './interface'; +import type { CascaderValueType, DataNode, FieldNames } from './interface'; import CascaderContext from './context'; import { connectValue, convertOptions, restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; @@ -19,6 +19,9 @@ import useUpdateEffect from './hooks/useUpdateEffect'; * * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API. * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version. + * + * Update: + * - dropdown class change to `rc-cascader-dropdown` * * Deprecated: * - popupVisible @@ -51,7 +54,7 @@ export interface ShowSearchType { interface BaseCascaderProps extends Pick, - Omit, 'value' | 'defaultValue' | 'onChange'> { + Omit, 'value' | 'defaultValue' | 'onChange'> { options?: DataNode[]; children?: React.ReactElement; @@ -150,8 +153,12 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref(); React.useImperativeHandle(ref, () => ({ - focus: cascaderRef.current.focus, - blur: cascaderRef.current.blur, + focus: () => { + cascaderRef.current.focus(); + }, + blur: () => { + cascaderRef.current.blur(); + }, })); const getEntityByValue = (val: React.Key): FlattenDataNode => diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 3cb22197..61417e9c 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -42,7 +42,7 @@ export default function Column({ return (
      {options.map(option => { - const { disabled, value } = option; + const { disabled, value, node } = option; const isMergedLeaf = isLeaf(option); const isLoading = loadingKeys.includes(value); @@ -64,6 +64,14 @@ export default function Column({ } }; + // >>>>> Title + let title: string; + if (typeof node?.title === 'string') { + title = node.title; + } else if (typeof option.title === 'string') { + title = option.title; + } + // >>>>> Render return (
    • { if (expandTrigger) { diff --git a/src/interface.ts b/src/interface.ts index 90b9d2af..a740349a 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -10,6 +10,8 @@ export type CascaderValueType = React.Key[]; export interface DataNode { label: React.ReactNode; + /** Customize hover title */ + title?: string; value: string; disabled?: boolean; children?: DataNode[]; @@ -20,7 +22,7 @@ export interface InternalDataNode extends DataNode { node: DataNode; } -export interface OptionDataNode extends Omit { +export interface OptionDataNode extends Omit { title: React.ReactNode; children?: OptionDataNode[]; } diff --git a/tests/__snapshots__/index.spec.js.snap b/tests/__snapshots__/index.spec.js.snap index f0cd3253..e5f19d37 100644 --- a/tests/__snapshots__/index.spec.js.snap +++ b/tests/__snapshots__/index.spec.js.snap @@ -2,279 +2,136 @@ exports[`Cascader should not show title when title is falsy 1`] = ` `; exports[`Cascader should render custom dropdown correctly 1`] = `
      -
      - +
      -
      +
      +
      > - +
    - -
    - - Hello, DropdownRender - +
    + + Hello, DropdownRender + + `; diff --git a/tests/index.spec.js b/tests/index.spec.js index dac1656e..788aa3f2 100644 --- a/tests/index.spec.js +++ b/tests/index.spec.js @@ -495,107 +495,93 @@ describe('Cascader', () => { expect(wrapper.isOpen()).toBeFalsy(); }); - // // https://github.com/ant-design/ant-design/issues/9084 - // it('should trigger loadData when expandTrigger is hover', () => { - // const options = [ - // { - // value: 'zhejiang', - // label: 'Zhejiang', - // isLeaf: false, - // }, - // { - // value: 'jiangsu', - // label: 'Jiangsu', - // isLeaf: false, - // }, - // ]; - // const loadData = jest.fn(); - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // menu1Items.at(0).simulate('mouseEnter'); - // jest.runAllTimers(); - // expect(loadData).toHaveBeenCalled(); - // }); - - // // https://github.com/ant-design/ant-design/issues/9793 - // it('should not trigger onBlur and onFocus when select item', () => { - // const onFocus = jest.fn(); - // const onBlur = jest.fn(); - // const wrapper = mount( - // - // - // , - // ); - // wrapper.find('input').simulate('focus'); - // wrapper.find('input').simulate('click'); - // const menus = wrapper.find('.rc-cascader-menu'); - // const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); - // menu1Items.at(0).simulate('mouseDown'); - // menu1Items.at(0).simulate('click'); - // jest.runAllTimers(); - // expect(onFocus).toHaveBeenCalledTimes(1); - // expect(onBlur).toHaveBeenCalledTimes(0); - // }); - - // // https://github.com/ant-design/ant-design/issues/18713 - // it('should not show title when title is falsy', () => { - // const options = [ - // { - // value: '1', - // label: '1', - // title: '', - // }, - // { - // value: '2', - // label: '2', - // title: undefined, - // }, - // { - // value: '3', - // label: '3', - // }, - // { - // value: '4', - // label: '4', - // title: 'title', - // }, - // ]; - // const wrapper = mount( - // - // - // , - // ); - // const menus = wrapper.find('.rc-cascader-menu'); - // expect(menus).toMatchSnapshot(); - // }); - - // it('should render custom dropdown correctly', () => { - // const wrapper = mount( - // ( - //
    - // {menus} - //
    - // Hello, DropdownRender - //
    - // )} - // > - // - //
    , - // ); - // const customDropdown = wrapper.find('.custom-dropdown'); - // expect(customDropdown.length).toBe(1); - // const customDropdownContent = wrapper.find('.custom-dropdown-content'); - // expect(customDropdownContent.length).toBe(1); - // const menus = wrapper.find('.rc-cascader-menus'); - // expect(menus).toMatchSnapshot(); - // }); + // https://github.com/ant-design/ant-design/issues/9084 + it('should trigger loadData when expandTrigger is hover', () => { + const options = [ + { + value: 'zhejiang', + label: 'Zhejiang', + isLeaf: false, + }, + { + value: 'jiangsu', + label: 'Jiangsu', + isLeaf: false, + }, + ]; + const loadData = jest.fn(); + const wrapper = mount( + + + , + ); + wrapper.find('input').simulate('click'); + const menus = wrapper.find('.rc-cascader-menu'); + const menu1Items = menus.at(0).find('.rc-cascader-menu-item'); + menu1Items.at(0).simulate('mouseEnter'); + jest.runAllTimers(); + expect(loadData).toHaveBeenCalled(); + }); + + // https://github.com/ant-design/ant-design/issues/9793 + it('should not trigger onBlur and onFocus when select item', () => { + // This function is handled by `rc-select` instead + }); + + // https://github.com/ant-design/ant-design/issues/18713 + it('should not show title when title is falsy', () => { + const options = [ + { + value: '1', + label: '1', + title: '', + }, + { + value: '2', + label: '2', + title: undefined, + }, + { + value: '3', + label: '3', + }, + { + value: '4', + label: '4', + title: 'title', + }, + ]; + const wrapper = mount( + + + , + ); + const menus = wrapper.find('.rc-cascader-menu'); + expect(menus.render()).toMatchSnapshot(); + }); + + it('should render custom dropdown correctly', () => { + const wrapper = mount( + ( +
    + {menus} +
    + Hello, DropdownRender +
    + )} + > + +
    , + ); + + const customDropdown = wrapper.find('.custom-dropdown'); + expect(customDropdown.length).toBe(1); + const customDropdownContent = wrapper.find('.custom-dropdown-content'); + expect(customDropdownContent.length).toBe(1); + const menus = wrapper.find('.rc-cascader-dropdown'); + expect(menus.render()).toMatchSnapshot(); + }); }); From c490faba15d2f0210a1140fb72d40b95dd1933c7 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 23 Aug 2021 20:08:24 +0800 Subject: [PATCH 33/54] feat: Add filter --- examples/animation.jsx | 2 +- examples/debug.tsx | 6 ++- src/Cascader.tsx | 84 ++++++++++++++++++++------------- src/OptionList/Column.tsx | 3 +- src/OptionList/SearchResult.tsx | 84 +++++++++++++++++++-------------- src/OptionList/index.tsx | 35 +++++++++----- src/context.ts | 15 +++++- src/hooks/useSearchConfig.ts | 28 +++++++++++ src/interface.ts | 18 +++++++ 9 files changed, 190 insertions(+), 85 deletions(-) create mode 100644 src/hooks/useSearchConfig.ts diff --git a/examples/animation.jsx b/examples/animation.jsx index 1b3eceb8..ff512aa5 100644 --- a/examples/animation.jsx +++ b/examples/animation.jsx @@ -70,7 +70,7 @@ class Demo extends React.Component { render() { return ( - + ); diff --git a/examples/debug.tsx b/examples/debug.tsx index af732b8b..467e74e7 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -54,6 +54,10 @@ const addressOptions1 = [ value: 'haidian', disabled: true, }, + { + label: 'TEST', + value: 'test', + }, ], }, { @@ -113,7 +117,7 @@ const Demo = () => { multiple={multiple} allowClear // defaultValue={multiple ? [defaultValue] : defaultValue} - // showSearch + showSearch /> ); diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 6b08ce06..f264d426 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -1,16 +1,17 @@ import * as React from 'react'; -import type { TriggerProps } from 'rc-trigger'; import warning from 'rc-util/lib/warning'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; +import type { TreeSelectProps } from 'rc-tree-select'; import generate from 'rc-tree-select/lib/generate'; import type { FlattenDataNode } from 'rc-tree-select/lib/interface'; import { fillFieldNames } from 'rc-tree-select/lib/utils/valueUtil'; import type { RefSelectProps } from 'rc-select/lib/generate'; import OptionList from './OptionList'; -import type { CascaderValueType, DataNode, FieldNames } from './interface'; +import type { CascaderValueType, DataNode, FieldNames, ShowSearchType } from './interface'; import CascaderContext from './context'; import { connectValue, convertOptions, restoreCompatibleValue } from './util'; import useUpdateEffect from './hooks/useUpdateEffect'; +import useSearchConfig from './hooks/useSearchConfig'; /** * `rc-cascader` is much like `rc-tree-select` but API is very different. @@ -19,13 +20,16 @@ import useUpdateEffect from './hooks/useUpdateEffect'; * * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API. * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version. - * + * * Update: * - dropdown class change to `rc-cascader-dropdown` * * Deprecated: * - popupVisible * - onPopupVisibleChange + * + * Removed: + * - builtinPlacements: Handle by select */ const RefCascader = generate({ @@ -34,27 +38,27 @@ const RefCascader = generate({ }); // ====================================== Wrap ====================================== -export interface ShowSearchType { - filter?: (inputValue: string, path: CascaderValueType, names: FieldNames) => boolean; - render?: ( - inputValue: string, - path: CascaderValueType, - prefixCls: string | undefined, - names: FieldNames, - ) => React.ReactNode; - sort?: ( - a: CascaderValueType, - b: CascaderValueType, - inputValue: string, - names: FieldNames, - ) => number; - matchInputWidth?: boolean; - limit?: number | false; -} - interface BaseCascaderProps - extends Pick, - Omit, 'value' | 'defaultValue' | 'onChange'> { + extends Omit< + TreeSelectProps, + | 'value' + | 'defaultValue' + | 'filterTreeNode' + | 'labelInValue' + | 'loadData' + | 'showCheckedStrategy' + | 'showSearch' + | 'treeCheckable' + | 'treeCheckStrictly' + | 'treeDataSimpleMode' + | 'treeNodeFilterProp' + | 'treeNodeLabelProp' + | 'treeDefaultExpandAll' + | 'treeDefaultExpandedKeys' + | 'treeExpandedKeys' + | 'treeIcon' + | 'onChange' + > { options?: DataNode[]; children?: React.ReactElement; @@ -75,7 +79,6 @@ interface BaseCascaderProps // Open /** @deprecated Use `open` instead */ popupVisible?: boolean; - open?: boolean; /** @deprecated Use `onDropdownVisibleChange` instead */ onPopupVisibleChange?: (open: boolean) => void; @@ -84,13 +87,7 @@ interface BaseCascaderProps // Trigger expandTrigger?: 'hover' | 'click'; - // transitionName?: string; - // popupClassName?: string; - // popupPlacement?: string; - // prefixCls?: string; - // dropdownMenuColumnStyle?: React.CSSProperties; - // dropdownRender?: (menu: React.ReactElement) => React.ReactElement; - // builtinPlacements?: BuildInPlacements; + dropdownMenuColumnStyle?: React.CSSProperties; loadData?: (selectOptions: DataNode[]) => void; expandIcon?: React.ReactNode; @@ -135,12 +132,14 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref { return convertOptions(options, mergedFieldNames); @@ -274,13 +275,27 @@ const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref true} + showSearch={mergedShowSearch} onSearch={setMergedSearch} labelRender={labelRender} {...{ diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 61417e9c..c6e1fd5b 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -36,7 +36,7 @@ export default function Column({ const menuItemPrefixCls = `${prefixCls}-menu-item`; const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`; - const { changeOnSelect, expandTrigger, expandIcon, loadingIcon } = + const { changeOnSelect, expandTrigger, expandIcon, loadingIcon, dropdownMenuColumnStyle } = React.useContext(CascaderContext); return ( @@ -81,6 +81,7 @@ export default function Column({ [`${menuItemPrefixCls}-selected`]: !multiple && checked, [`${menuItemPrefixCls}-disabled`]: disabled, })} + style={dropdownMenuColumnStyle} role="menuitemcheckbox" title={title} aria-checked={checked} diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index a4c65141..5166f418 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -2,48 +2,62 @@ * Search result UI is different with origin menu list. * We use single component to handle this. */ -import { isLeaf } from '../util'; import * as React from 'react'; -import type { OptionDataNode } from '../interface'; +import { isLeaf, restoreCompatibleValue } from '../util'; +import type { OptionListProps as SelectOptionListProps } from 'rc-select/lib/OptionList'; +import type { FieldNames, OptionDataNode, ShowSearchType } from '../interface'; import Column from './Column'; import type { ColumnProps } from './Column'; -export type SearchResultProps = Omit; - -function flattenOptions(options: OptionDataNode[]): OptionDataNode[] { - const optionList: OptionDataNode[] = []; - - function dig(list: OptionDataNode[], labelPath: React.ReactNode[] = []) { - (list || []).forEach(option => { - const connectLabelPath = [...labelPath, option.title]; - - if (isLeaf(option)) { - optionList.push({ - title: connectLabelPath.reduce((labelList: React.ReactNode[], currentLabel, index) => { - if (index !== 0) { - labelList.push(' / '); - } - labelList.push(currentLabel); - return list; - }, []), - value: option.value, - node: option.node, - }); - } else { - dig(option.children, connectLabelPath); - } - }); - } - - dig(options); - - return optionList; +export interface SearchResultProps extends Omit { + flattenOptions: SelectOptionListProps['flattenOptions']; + fieldNames: FieldNames; + search: string; + searchConfig: ShowSearchType; + changeOnSelect: boolean; + empty: OptionDataNode[]; } export default function SearchResult(props: SearchResultProps) { - const { options } = props; + const { flattenOptions, fieldNames, search, searchConfig, changeOnSelect, empty } = props; + + // Do filter + const filteredEntityList = React.useMemo(() => { + // Normalize list + let normalizeList = flattenOptions.map(option => { + const { options } = restoreCompatibleValue(option as any, fieldNames); + const originOptionList = options.map(opt => opt.node); + return { + option, + originOptionList, + }; + }); - const optionList = React.useMemo(() => flattenOptions(options), [options]); + // Not keep parent node when !changeOnSelect + if (!changeOnSelect) { + normalizeList = normalizeList.filter(({ option }) => !option.data.children?.length); + } + + // Filter + const filteredList = normalizeList.filter(({ originOptionList }) => { + return searchConfig.filter(search, originOptionList, fieldNames); + }); + + return filteredList; + }, [flattenOptions, fieldNames, search, searchConfig, changeOnSelect]); + + // Wrap with connected label + const options = React.useMemo(() => { + return filteredEntityList.map(({ option, originOptionList }) => { + const title = originOptionList.map(opt => opt[fieldNames.label]).join(' / '); + return { + ...option.data, + title, + }; + }); + }, [filteredEntityList, fieldNames]); - return ; + return ( + + ); } diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index de7e1f67..2e4605e9 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -25,7 +25,7 @@ const RefOptionList = React.forwardRef>>>> Empty + const emptyList = [ + { + title: notFoundContent, + value: '__EMPTY__', + disabled: true, + node: null, + }, + ]; + // >>>>> Search if (searchValue) { - return ; + return ( + + ); } // >>>>> Columns - const firstLevelOptions = options.length - ? options - : [ - { - title: notFoundContent, - value: '__EMPTY__', - disabled: true, - node: null, - }, - ]; + const firstLevelOptions = options.length ? options : emptyList; const columnNodes: React.ReactElement[] = [ + | 'changeOnSelect' + | 'expandTrigger' + | 'fieldNames' + | 'expandIcon' + | 'loadingIcon' + | 'loadData' + | 'dropdownMenuColumnStyle' + > & { + search: ShowSearchType; + } > >({ changeOnSelect: false, @@ -15,6 +24,8 @@ const CascaderContext = React.createContext< expandIcon: null, loadingIcon: null, loadData: null, + dropdownMenuColumnStyle: null, + search: null, }); export default CascaderContext; diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts new file mode 100644 index 00000000..8fef29a4 --- /dev/null +++ b/src/hooks/useSearchConfig.ts @@ -0,0 +1,28 @@ +import React from 'react'; +import type { ShowSearchType } from '../interface'; +import type { CascaderProps } from '../Cascader'; + +// Convert `showSearch` to unique config +export default function useSearchConfig(showSearch?: CascaderProps['showSearch']) { + return React.useMemo<[boolean, ShowSearchType]>(() => { + if (!showSearch) { + return [false, {}]; + } + + let searchConfig: ShowSearchType = { + matchInputWidth: true, + filter: (search, options, { label }) => { + return options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase())); + }, + }; + + if (showSearch && typeof showSearch === 'object') { + searchConfig = { + ...searchConfig, + ...showSearch, + }; + } + + return [true, searchConfig]; + }, [showSearch]); +} diff --git a/src/interface.ts b/src/interface.ts index a740349a..b327d313 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -32,3 +32,21 @@ export interface FlattenDataNode { path: React.Key[]; parent?: FlattenDataNode; } + +export interface ShowSearchType { + filter?: (inputValue: string, options: DataNode[], fieldNames: FieldNames) => boolean; + render?: ( + inputValue: string, + path: CascaderValueType, + prefixCls: string | undefined, + names: FieldNames, + ) => React.ReactNode; + sort?: ( + a: CascaderValueType, + b: CascaderValueType, + inputValue: string, + names: FieldNames, + ) => number; + matchInputWidth?: boolean; + limit?: number | false; +} From 2d3cf590326e548a08bceeb2707a13c07a278447 Mon Sep 17 00:00:00 2001 From: zombiej Date: Tue, 24 Aug 2021 11:12:28 +0800 Subject: [PATCH 34/54] chore: Search on client side --- src/OptionList/SearchResult.tsx | 27 +++++++++++++++++++++------ src/hooks/useSearchConfig.ts | 3 --- src/interface.ts | 6 +++--- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index 5166f418..237e3200 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -9,7 +9,14 @@ import type { FieldNames, OptionDataNode, ShowSearchType } from '../interface'; import Column from './Column'; import type { ColumnProps } from './Column'; +const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) => + options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase())); + +const defaultRender: ShowSearchType['render'] = (inputValue, path, prefixCls, fieldNames) => + path.map(opt => opt[fieldNames.label]).join(' / '); + export interface SearchResultProps extends Omit { + prefixCls: string; flattenOptions: SelectOptionListProps['flattenOptions']; fieldNames: FieldNames; search: string; @@ -19,8 +26,14 @@ export interface SearchResultProps extends Omit { // Normalize list @@ -35,28 +48,30 @@ export default function SearchResult(props: SearchResultProps) { // Not keep parent node when !changeOnSelect if (!changeOnSelect) { - normalizeList = normalizeList.filter(({ option }) => !option.data.children?.length); + normalizeList = normalizeList.filter(({ option }) => isLeaf(option.data)); } // Filter const filteredList = normalizeList.filter(({ originOptionList }) => { - return searchConfig.filter(search, originOptionList, fieldNames); + return filterOption(search, originOptionList, fieldNames); }); return filteredList; - }, [flattenOptions, fieldNames, search, searchConfig, changeOnSelect]); + }, [flattenOptions, fieldNames, search, filterOption, changeOnSelect]); + // ======================== Generate Options ======================== // Wrap with connected label const options = React.useMemo(() => { return filteredEntityList.map(({ option, originOptionList }) => { - const title = originOptionList.map(opt => opt[fieldNames.label]).join(' / '); + const title = renderOption(search, originOptionList, prefixCls, fieldNames); return { ...option.data, title, }; }); - }, [filteredEntityList, fieldNames]); + }, [search, renderOption, filteredEntityList, fieldNames, prefixCls]); + // ============================= Render ============================= return ( ); diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index 8fef29a4..fdc3d0f1 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -11,9 +11,6 @@ export default function useSearchConfig(showSearch?: CascaderProps['showSearch'] let searchConfig: ShowSearchType = { matchInputWidth: true, - filter: (search, options, { label }) => { - return options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase())); - }, }; if (showSearch && typeof showSearch === 'object') { diff --git a/src/interface.ts b/src/interface.ts index b327d313..740e750b 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -37,15 +37,15 @@ export interface ShowSearchType { filter?: (inputValue: string, options: DataNode[], fieldNames: FieldNames) => boolean; render?: ( inputValue: string, - path: CascaderValueType, + path: DataNode[], prefixCls: string | undefined, - names: FieldNames, + fieldNames: FieldNames, ) => React.ReactNode; sort?: ( a: CascaderValueType, b: CascaderValueType, inputValue: string, - names: FieldNames, + fieldNames: FieldNames, ) => number; matchInputWidth?: boolean; limit?: number | false; From 2f3c93039c0561fd6f0e42200c7283a3ee6ce829 Mon Sep 17 00:00:00 2001 From: zombiej Date: Tue, 24 Aug 2021 11:24:13 +0800 Subject: [PATCH 35/54] chore: add search.sort func --- src/OptionList/SearchResult.tsx | 25 +++++++++++++++++++++---- src/hooks/useSearchConfig.ts | 1 + src/interface.ts | 6 +++--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index 237e3200..d248b21a 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -52,12 +52,29 @@ export default function SearchResult(props: SearchResultProps) { } // Filter - const filteredList = normalizeList.filter(({ originOptionList }) => { - return filterOption(search, originOptionList, fieldNames); - }); + const filteredList: typeof normalizeList = []; + for (let i = 0; i < normalizeList.length; i += 1) { + // Perf saving if enabled + if (!searchConfig.sort && filteredList.length > searchConfig.limit) { + break; + } + + // Do filter + const optGrp = normalizeList[i]; + if (filterOption(search, optGrp.originOptionList, fieldNames)) { + filteredList.push(optGrp); + } + } + + // Sort: When searchConfig.sort is enabled. We have to filter all the list + if (searchConfig.sort) { + filteredList.sort((a, b) => { + return searchConfig.sort(a.originOptionList, b.originOptionList, search, fieldNames); + }); + } return filteredList; - }, [flattenOptions, fieldNames, search, filterOption, changeOnSelect]); + }, [flattenOptions, fieldNames, search, filterOption, changeOnSelect, searchConfig]); // ======================== Generate Options ======================== // Wrap with connected label diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index fdc3d0f1..f544a016 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -11,6 +11,7 @@ export default function useSearchConfig(showSearch?: CascaderProps['showSearch'] let searchConfig: ShowSearchType = { matchInputWidth: true, + limit: 50, }; if (showSearch && typeof showSearch === 'object') { diff --git a/src/interface.ts b/src/interface.ts index 740e750b..351be96e 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -38,12 +38,12 @@ export interface ShowSearchType { render?: ( inputValue: string, path: DataNode[], - prefixCls: string | undefined, + prefixCls: string, fieldNames: FieldNames, ) => React.ReactNode; sort?: ( - a: CascaderValueType, - b: CascaderValueType, + a: DataNode[], + b: DataNode[], inputValue: string, fieldNames: FieldNames, ) => number; From 42ad1b78fb7cba6c295b9c9678067a1aae0f998b Mon Sep 17 00:00:00 2001 From: zombiej Date: Tue, 24 Aug 2021 17:18:39 +0800 Subject: [PATCH 36/54] test: render of searchConfig --- src/OptionList/SearchResult.tsx | 2 +- tests/enzyme.ts | 2 +- tests/search.spec.tsx | 134 ++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 tests/search.spec.tsx diff --git a/src/OptionList/SearchResult.tsx b/src/OptionList/SearchResult.tsx index d248b21a..b2e8d85c 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/OptionList/SearchResult.tsx @@ -55,7 +55,7 @@ export default function SearchResult(props: SearchResultProps) { const filteredList: typeof normalizeList = []; for (let i = 0; i < normalizeList.length; i += 1) { // Perf saving if enabled - if (!searchConfig.sort && filteredList.length > searchConfig.limit) { + if (!searchConfig.sort && filteredList.length >= searchConfig.limit) { break; } diff --git a/tests/enzyme.ts b/tests/enzyme.ts index d26dbdca..273fb0df 100644 --- a/tests/enzyme.ts +++ b/tests/enzyme.ts @@ -5,7 +5,7 @@ type ReplaceReturnType any, TNewReturn> = ( ...a: Parameters ) => TNewReturn; -interface ReactWrapper extends OriginReactWrapper { +export interface ReactWrapper extends OriginReactWrapper { isOpen: () => boolean; clickOption: (menuIndex: number, itemIndex: number, type?: 'click' | 'doubleClick') => ReactWrapper; } diff --git a/tests/search.spec.tsx b/tests/search.spec.tsx new file mode 100644 index 00000000..f0cf734d --- /dev/null +++ b/tests/search.spec.tsx @@ -0,0 +1,134 @@ +/* eslint-disable @typescript-eslint/consistent-type-imports */ + +import React from 'react'; +import { mount, ReactWrapper } from './enzyme'; +import Cascader from '../src'; + +describe('Cascader.Search', () => { + function doSearch(wrapper: ReactWrapper, search: string) { + wrapper.find('input').simulate('change', { + target: { + value: search, + }, + }); + } + + const options = [ + { + label: 'Label Light', + value: 'light', + }, + { + label: 'Label Bamboo', + value: 'bamboo', + children: [ + { + label: 'Label Little', + value: 'little', + children: [ + { + label: 'Toy Fish', + value: 'fish', + }, + { + label: 'Toy Cards', + value: 'cards', + }, + ], + }, + ], + }, + ] as any; + + it('default search', () => { + const wrapper = mount(); + + // Leaf + doSearch(wrapper, 'toy'); + let itemList = wrapper.find('div.rc-cascader-menu-item-content'); + expect(itemList).toHaveLength(2); + expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); + expect(itemList.at(1).text()).toEqual('Label Bamboo / Label Little / Toy Cards'); + + // Parent + doSearch(wrapper, 'Label Little'); + itemList = wrapper.find('div.rc-cascader-menu-item-content'); + expect(itemList).toHaveLength(2); + expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); + expect(itemList.at(1).text()).toEqual('Label Bamboo / Label Little / Toy Cards'); + }); + + it('changeOnSelect', () => { + const wrapper = mount(); + + // Leaf + doSearch(wrapper, 'Label Little'); + const itemList = wrapper.find('div.rc-cascader-menu-item-content'); + expect(itemList).toHaveLength(3); + expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little'); + expect(itemList.at(1).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); + expect(itemList.at(2).text()).toEqual('Label Bamboo / Label Little / Toy Cards'); + }); + + it('sort', () => { + const wrapper = mount( + { + const finalA = pathA[pathA.length - 1]; + const finalB = pathB[pathB.length - 1]; + + if (finalA.value < finalB.value) { + return -1; + } + return 1; + }, + }} + />, + ); + + doSearch(wrapper, 'toy'); + const itemList = wrapper.find('div.rc-cascader-menu-item-content'); + expect(itemList).toHaveLength(2); + expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little / Toy Cards'); + expect(itemList.at(1).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); + }); + + it('limit', () => { + const wrapper = mount( + , + ); + + doSearch(wrapper, 'toy'); + const itemList = wrapper.find('div.rc-cascader-menu-item-content'); + expect(itemList).toHaveLength(1); + expect(itemList.at(0).text()).toEqual('Label Bamboo / Label Little / Toy Fish'); + }); + + it('render', () => { + const wrapper = mount( + + `${prefixCls}-${inputValue}-${optList.map(opt => opt.value).join('~')}`, + }} + />, + ); + + doSearch(wrapper, 'toy'); + const itemList = wrapper.find('div.rc-cascader-menu-item-content'); + expect(itemList).toHaveLength(2); + expect(itemList.at(0).text()).toEqual('rc-cascader-toy-bamboo~little~fish'); + expect(itemList.at(1).text()).toEqual('rc-cascader-toy-bamboo~little~cards'); + }); +}); From c788ff657549bd31689141c87633996bab3812d3 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 11:25:50 +0800 Subject: [PATCH 37/54] refactor: Move SearchResult into hooks --- examples/debug.tsx | 4 +- src/OptionList/Column.tsx | 1 + src/OptionList/index.tsx | 384 ++++++++++-------- src/hooks/useSearchConfig.ts | 2 +- .../useSearchResult.ts} | 44 +- 5 files changed, 245 insertions(+), 190 deletions(-) rename src/{OptionList/SearchResult.tsx => hooks/useSearchResult.ts} (63%) diff --git a/examples/debug.tsx b/examples/debug.tsx index 467e74e7..46ea3c1b 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -86,7 +86,7 @@ const addressOptions = [ }, ]; -const defaultValue = ['fj', 'fuzhou', 'mawei']; +const defaultValue = ['fj', 'fuzhou']; const Demo = () => { const [multiple, setMultiple] = React.useState(false); @@ -116,7 +116,7 @@ const Demo = () => { onChange={onChange} multiple={multiple} allowClear - // defaultValue={multiple ? [defaultValue] : defaultValue} + defaultValue={multiple ? [defaultValue] : defaultValue} showSearch /> diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index c6e1fd5b..93a5b88c 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -39,6 +39,7 @@ export default function Column({ const { changeOnSelect, expandTrigger, expandIcon, loadingIcon, dropdownMenuColumnStyle } = React.useContext(CascaderContext); + // ============================ Render ============================ return (
      {options.map(option => { diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 2e4605e9..6dd4d897 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -1,4 +1,6 @@ +/* eslint-disable default-case */ import * as React from 'react'; +import KeyCode from 'rc-util/lib/KeyCode'; import type { OptionListProps as SelectOptionListProps, RefOptionListProps, @@ -7,193 +9,251 @@ import { SelectContext } from 'rc-tree-select/lib/Context'; import type { OptionDataNode } from '../interface'; import Column from './Column'; import { restoreCompatibleValue } from '../util'; -import SearchResult from './SearchResult'; import CascaderContext from '../context'; +import useSearchResult from '../hooks/useSearchResult'; -const RefOptionList = React.forwardRef>( - (props, ref) => { - const { - prefixCls, - options, - onSelect, - multiple, - open, - flattenOptions, - searchValue, - onToggleOpen, - notFoundContent, - } = props; - - const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); - const { changeOnSelect, expandTrigger, fieldNames, loadData, search } = - React.useContext(CascaderContext); - - // ========================= loadData ========================= - const [loadingKeys, setLoadingKeys] = React.useState([]); - - const internalLoadData = (pathValue: React.Key) => { - if (!loadData) { - return; - } +type OptionListProps = SelectOptionListProps; +export type FlattenOptions = OptionListProps['flattenOptions']; - const entity = flattenOptions.find(flattenOption => flattenOption.data.value === pathValue); - if (entity) { - const { options: optionList } = restoreCompatibleValue(entity as any, fieldNames); - const rawOptionList = optionList.map(opt => opt.node); +const RefOptionList = React.forwardRef((props, ref) => { + const { + prefixCls, + options, + onSelect, + multiple, + open, + flattenOptions, + searchValue, + onToggleOpen, + notFoundContent, + } = props; - setLoadingKeys(keys => [...keys, optionList[optionList.length - 1].value]); + const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); + const { changeOnSelect, expandTrigger, fieldNames, loadData, search } = + React.useContext(CascaderContext); - loadData(rawOptionList); - } - }; - - // zombieJ: This is bad. We should make this same as `rc-tree` to use Promise instead. - React.useEffect(() => { - if (loadingKeys.length) { - loadingKeys.forEach(loadingKey => { - const option = flattenOptions.find(opt => opt.value === loadingKey); - if (option.data.children) { - setLoadingKeys(keys => keys.filter(key => key !== loadingKey)); - } - }); - } - }, [flattenOptions, loadingKeys]); + // ========================= loadData ========================= + const [loadingKeys, setLoadingKeys] = React.useState([]); - // ========================== Values ========================== - const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]); - const halfCheckedSet = React.useMemo(() => new Set(halfCheckedKeys), [halfCheckedKeys]); + const internalLoadData = (pathValue: React.Key) => { + if (!loadData) { + return; + } - // =========================== Open =========================== - const [openPath, setOpenPath] = React.useState([]); + const entity = flattenOptions.find(flattenOption => flattenOption.data.value === pathValue); + if (entity) { + const { options: optionList } = restoreCompatibleValue(entity as any, fieldNames); + const rawOptionList = optionList.map(opt => opt.node); - React.useEffect(() => { - if (open) { - let nextOpenPath: React.Key[] = []; + setLoadingKeys(keys => [...keys, optionList[optionList.length - 1].value]); - if (!multiple && checkedKeys.length) { - const entity = flattenOptions.find( - flattenOption => flattenOption.data.value === checkedKeys[0], - ); + loadData(rawOptionList); + } + }; - if (entity) { - nextOpenPath = restoreCompatibleValue(entity as any, fieldNames).path; - } + // zombieJ: This is bad. We should make this same as `rc-tree` to use Promise instead. + React.useEffect(() => { + if (loadingKeys.length) { + loadingKeys.forEach(loadingKey => { + const option = flattenOptions.find(opt => opt.value === loadingKey); + if (option.data.children) { + setLoadingKeys(keys => keys.filter(key => key !== loadingKey)); } - setOpenPath(nextOpenPath); - } - }, [open]); + }); + } + }, [flattenOptions, loadingKeys]); - // =========================== Path =========================== - const onPathOpen = (index: number, pathValue: React.Key) => { - const nextOpenPath = [...openPath.slice(0, index), pathValue]; - setOpenPath(nextOpenPath); + // ========================== Values ========================== + const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]); + const halfCheckedSet = React.useMemo(() => new Set(halfCheckedKeys), [halfCheckedKeys]); + + // =========================== Open =========================== + const [openPath, setOpenPath] = React.useState([]); - // Trigger loadData - internalLoadData(pathValue); - }; + React.useEffect(() => { + if (open) { + let nextOpenPath: React.Key[] = []; - const onPathSelect = (pathValue: React.Key, isLeaf: boolean) => { - onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); + if (!multiple && checkedKeys.length) { + const entity = flattenOptions.find( + flattenOption => flattenOption.data.value === checkedKeys[0], + ); - if (!multiple && (isLeaf || (changeOnSelect && expandTrigger === 'hover'))) { - onToggleOpen(false); + if (entity) { + nextOpenPath = restoreCompatibleValue(entity as any, fieldNames).path; + } } - }; + setOpenPath(nextOpenPath); + } + }, [open]); - const getPathList = (pathList: React.Key[]) => { - let currentOptions = options; + // =========================== Path =========================== + const onPathOpen = (index: number, pathValue: React.Key) => { + const nextOpenPath = [...openPath.slice(0, index), pathValue]; + setOpenPath(nextOpenPath); - for (let i = 0; i < pathList.length; i += 1) { - currentOptions = (currentOptions || []).find( - option => option.value === pathList[i], - ).children; - } + // Trigger loadData + internalLoadData(pathValue); + }; - return currentOptions; - }; - - // ========================= Keyboard ========================= - React.useImperativeHandle(ref, () => ({ - // scrollTo: treeRef.current?.scrollTo, - onKeyDown: event => { - const { which } = event; - }, - onKeyUp: () => {}, - })); - - // ========================== Render ========================== - const columnProps = { - ...props, - onOpen: onPathOpen, - onSelect: onPathSelect, - onToggleOpen, - checkedSet, - halfCheckedSet, - loadingKeys, - }; - - // >>>>> Empty - const emptyList = [ - { - title: notFoundContent, - value: '__EMPTY__', - disabled: true, - node: null, - }, - ]; - - // >>>>> Search - if (searchValue) { - return ( - - ); + const onPathSelect = (pathValue: React.Key, isLeaf: boolean) => { + onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); + + if (!multiple && (isLeaf || (changeOnSelect && expandTrigger === 'hover'))) { + onToggleOpen(false); } + }; + + const getPathList = (pathList: React.Key[]) => { + let currentOptions = options; - // >>>>> Columns - const firstLevelOptions = options.length ? options : emptyList; + for (let i = 0; i < pathList.length; i += 1) { + currentOptions = (currentOptions || []).find(option => option.value === pathList[i]).children; + } + + return currentOptions; + }; + + // ========================== Search ========================== + const searchOptions = useSearchResult({ + ...props, + fieldNames, + changeOnSelect, + searchConfig: search, + }); + + // ========================== Column ========================== + const optionColumns = React.useMemo(() => { + if (searchValue) { + return [ + { + options: searchOptions, + }, + ]; + } - const columnNodes: React.ReactElement[] = [ - , - ]; + const rawOptionColumns: { + options: OptionDataNode[]; + }[] = []; - openPath.forEach((_, index) => { - const mergedIndex = index + 1; - const subOptions = getPathList(openPath.slice(0, mergedIndex)); + for (let i = 0; i <= openPath.length; i += 1) { + const subOptions = getPathList(openPath.slice(0, i)); if (subOptions) { - columnNodes.push( - , - ); + rawOptionColumns.push({ + options: subOptions, + }); + } else { + break; + } + } + + return rawOptionColumns; + }, [searchValue, searchOptions, openPath]); + + // ========================= Keyboard ========================= + const activeColumnIndex = Math.max(openPath.length - 1, 0); + const lastActiveOptionValue = openPath[openPath.length - 1]; + + const getEnabledActiveIndex = (offset: number) => { + const currentOptions = optionColumns[activeColumnIndex]?.options || []; + const activeOptionIndex = currentOptions.findIndex(opt => opt.value === lastActiveOptionValue); + + const len = currentOptions.length; + + for (let i = 0; i < len; i += 1) { + const current = (activeOptionIndex + i * offset + len) % len; + + if (!currentOptions[current].disabled) { + return current; + } + } + + return -1; + }; + + React.useImperativeHandle(ref, () => ({ + // scrollTo: treeRef.current?.scrollTo, + onKeyDown: event => { + const { which } = event; + + switch (which) { + case KeyCode.UP: + case KeyCode.DOWN: { + let offset = 0; + if (which === KeyCode.UP) { + offset = -1; + } else if (which === KeyCode.DOWN) { + offset = 1; + } + + if (offset !== 0) { + const nextActiveIndex = getEnabledActiveIndex(offset); + console.log('~~~>', nextActiveIndex); + } + + break; + } } - }); - - // >>>>> Render - return ( - <> -
      {columnNodes}
      - - ); - }, -); + }, + onKeyUp: () => {}, + })); + + // ========================== Render ========================== + const columnProps = { + ...props, + onOpen: onPathOpen, + onSelect: onPathSelect, + onToggleOpen, + checkedSet, + halfCheckedSet, + loadingKeys, + }; + + // >>>>> Empty + const emptyList: OptionDataNode[] = [ + { + title: notFoundContent, + value: '__EMPTY__', + disabled: true, + node: null, + }, + ]; + + // // >>>>> Search + // if (searchValue) { + // return ( + // + // ); + // } + + // >>>>> Columns + const mergedOptionColumns = options.length ? optionColumns : [{ options: emptyList }]; + + const columnNodes: React.ReactElement[] = mergedOptionColumns.map((col, index) => ( + + )); + + // >>>>> Render + return ( + <> +
      {columnNodes}
      + + ); +}); export default RefOptionList; diff --git a/src/hooks/useSearchConfig.ts b/src/hooks/useSearchConfig.ts index f544a016..e27b754b 100644 --- a/src/hooks/useSearchConfig.ts +++ b/src/hooks/useSearchConfig.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import * as React from 'react'; import type { ShowSearchType } from '../interface'; import type { CascaderProps } from '../Cascader'; diff --git a/src/OptionList/SearchResult.tsx b/src/hooks/useSearchResult.ts similarity index 63% rename from src/OptionList/SearchResult.tsx rename to src/hooks/useSearchResult.ts index b2e8d85c..55172d44 100644 --- a/src/OptionList/SearchResult.tsx +++ b/src/hooks/useSearchResult.ts @@ -1,13 +1,7 @@ -/** - * Search result UI is different with origin menu list. - * We use single component to handle this. - */ import * as React from 'react'; +import type { FlattenOptions } from '../OptionList'; +import type { FieldNames, ShowSearchType } from '../interface'; import { isLeaf, restoreCompatibleValue } from '../util'; -import type { OptionListProps as SelectOptionListProps } from 'rc-select/lib/OptionList'; -import type { FieldNames, OptionDataNode, ShowSearchType } from '../interface'; -import Column from './Column'; -import type { ColumnProps } from './Column'; const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) => options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase())); @@ -15,19 +9,18 @@ const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) => const defaultRender: ShowSearchType['render'] = (inputValue, path, prefixCls, fieldNames) => path.map(opt => opt[fieldNames.label]).join(' / '); -export interface SearchResultProps extends Omit { +export interface SearchResultConfig { prefixCls: string; - flattenOptions: SelectOptionListProps['flattenOptions']; - fieldNames: FieldNames; - search: string; searchConfig: ShowSearchType; + flattenOptions: FlattenOptions; + fieldNames: FieldNames; changeOnSelect: boolean; - empty: OptionDataNode[]; + searchValue: string; } -export default function SearchResult(props: SearchResultProps) { - const { flattenOptions, fieldNames, search, searchConfig, changeOnSelect, empty, prefixCls } = - props; +export default function useSearchResult(resultConfig: SearchResultConfig) { + const { searchConfig, flattenOptions, fieldNames, changeOnSelect, searchValue, prefixCls } = + resultConfig; // ============================== MISC ============================== const filterOption = searchConfig.filter || defaultFilter; @@ -36,6 +29,10 @@ export default function SearchResult(props: SearchResultProps) { // ============================= Filter ============================= // Do filter const filteredEntityList = React.useMemo(() => { + if (!searchValue) { + return []; + } + // Normalize list let normalizeList = flattenOptions.map(option => { const { options } = restoreCompatibleValue(option as any, fieldNames); @@ -61,7 +58,7 @@ export default function SearchResult(props: SearchResultProps) { // Do filter const optGrp = normalizeList[i]; - if (filterOption(search, optGrp.originOptionList, fieldNames)) { + if (filterOption(searchValue, optGrp.originOptionList, fieldNames)) { filteredList.push(optGrp); } } @@ -69,27 +66,24 @@ export default function SearchResult(props: SearchResultProps) { // Sort: When searchConfig.sort is enabled. We have to filter all the list if (searchConfig.sort) { filteredList.sort((a, b) => { - return searchConfig.sort(a.originOptionList, b.originOptionList, search, fieldNames); + return searchConfig.sort(a.originOptionList, b.originOptionList, searchValue, fieldNames); }); } return filteredList; - }, [flattenOptions, fieldNames, search, filterOption, changeOnSelect, searchConfig]); + }, [flattenOptions, fieldNames, searchValue, filterOption, changeOnSelect, searchConfig]); // ======================== Generate Options ======================== // Wrap with connected label const options = React.useMemo(() => { return filteredEntityList.map(({ option, originOptionList }) => { - const title = renderOption(search, originOptionList, prefixCls, fieldNames); + const title = renderOption(searchValue, originOptionList, prefixCls, fieldNames); return { ...option.data, title, }; }); - }, [search, renderOption, filteredEntityList, fieldNames, prefixCls]); + }, [searchValue, renderOption, filteredEntityList, fieldNames, prefixCls]); - // ============================= Render ============================= - return ( - - ); + return options; } From ef2a48af49531041e1f2d3469a1b9a7db248b5ce Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 11:34:47 +0800 Subject: [PATCH 38/54] chore:key to up & down --- src/OptionList/index.tsx | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 6dd4d897..fa64a49c 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -155,21 +155,22 @@ const RefOptionList = React.forwardRef((pro const activeColumnIndex = Math.max(openPath.length - 1, 0); const lastActiveOptionValue = openPath[openPath.length - 1]; - const getEnabledActiveIndex = (offset: number) => { + const getActiveOption = (offset: number) => { const currentOptions = optionColumns[activeColumnIndex]?.options || []; const activeOptionIndex = currentOptions.findIndex(opt => opt.value === lastActiveOptionValue); const len = currentOptions.length; - for (let i = 0; i < len; i += 1) { + for (let i = 1; i < len; i += 1) { const current = (activeOptionIndex + i * offset + len) % len; + const option = currentOptions[current]; - if (!currentOptions[current].disabled) { - return current; + if (!option.disabled) { + return option; } } - return -1; + return null; }; React.useImperativeHandle(ref, () => ({ @@ -188,8 +189,8 @@ const RefOptionList = React.forwardRef((pro } if (offset !== 0) { - const nextActiveIndex = getEnabledActiveIndex(offset); - console.log('~~~>', nextActiveIndex); + const nextActiveOption = getActiveOption(offset); + onPathOpen(activeColumnIndex, nextActiveOption.value); } break; @@ -220,21 +221,6 @@ const RefOptionList = React.forwardRef((pro }, ]; - // // >>>>> Search - // if (searchValue) { - // return ( - // - // ); - // } - // >>>>> Columns const mergedOptionColumns = options.length ? optionColumns : [{ options: emptyList }]; From 357b3d99a77a41e0acc767d5d1f440455bbb1731 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 11:48:39 +0800 Subject: [PATCH 39/54] chore: simple key navigation --- src/OptionList/index.tsx | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index fa64a49c..e7c249e1 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -152,16 +152,14 @@ const RefOptionList = React.forwardRef((pro }, [searchValue, searchOptions, openPath]); // ========================= Keyboard ========================= - const activeColumnIndex = Math.max(openPath.length - 1, 0); - const lastActiveOptionValue = openPath[openPath.length - 1]; - - const getActiveOption = (offset: number) => { + const getActiveOption = (activeColumnIndex: number, offset: number) => { + const pathActiveValue = openPath[activeColumnIndex]; const currentOptions = optionColumns[activeColumnIndex]?.options || []; - const activeOptionIndex = currentOptions.findIndex(opt => opt.value === lastActiveOptionValue); + const activeOptionIndex = currentOptions.findIndex(opt => opt.value === pathActiveValue); const len = currentOptions.length; - for (let i = 1; i < len; i += 1) { + for (let i = 1; i <= len; i += 1) { const current = (activeOptionIndex + i * offset + len) % len; const option = currentOptions[current]; @@ -189,12 +187,29 @@ const RefOptionList = React.forwardRef((pro } if (offset !== 0) { - const nextActiveOption = getActiveOption(offset); - onPathOpen(activeColumnIndex, nextActiveOption.value); + const activeColumnIndex = Math.max(openPath.length - 1, 0); + const nextActiveOption = getActiveOption(activeColumnIndex, offset); + if (nextActiveOption) { + onPathOpen(activeColumnIndex, nextActiveOption.value); + } } break; } + + case KeyCode.LEFT: { + setOpenPath(path => path.slice(0, -1)); + break; + } + + case KeyCode.RIGHT: { + const nextColumnIndex = openPath.length; + const nextActiveOption = getActiveOption(nextColumnIndex, 1); + if (nextActiveOption) { + onPathOpen(nextColumnIndex, nextActiveOption.value); + } + break; + } } }, onKeyUp: () => {}, From 9acba2807f79ae01cac585d595c22e6c72f99b86 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 13:30:56 +0800 Subject: [PATCH 40/54] chore: rm selected style --- assets/list.less | 8 ++++---- src/OptionList/Column.tsx | 2 +- src/OptionList/index.tsx | 31 ++++++++++++++++++++++++++++--- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/assets/list.less b/assets/list.less index 8cbf17f3..fea43b0e 100644 --- a/assets/list.less +++ b/assets/list.less @@ -27,12 +27,12 @@ padding-right: 20px; position: relative; - &-active { - background: rgba(0, 255, 0, 0.1); + &-selected { + background: rgba(0, 0, 255, 0.05); } - &-selected { - background: rgba(0, 0, 255, 0.1); + &-active { + background: rgba(0, 255, 0, 0.1); } &-disabled { diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index 93a5b88c..3b918591 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -79,7 +79,7 @@ export default function Column({ key={value} className={classNames(menuItemPrefixCls, { [`${menuItemPrefixCls}-active`]: openKey === value, - [`${menuItemPrefixCls}-selected`]: !multiple && checked, + // [`${menuItemPrefixCls}-selected`]: !multiple && checked, [`${menuItemPrefixCls}-disabled`]: disabled, })} style={dropdownMenuColumnStyle} diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index e7c249e1..97335d5a 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -8,7 +8,7 @@ import type { import { SelectContext } from 'rc-tree-select/lib/Context'; import type { OptionDataNode } from '../interface'; import Column from './Column'; -import { restoreCompatibleValue } from '../util'; +import { isLeaf, restoreCompatibleValue } from '../util'; import CascaderContext from '../context'; import useSearchResult from '../hooks/useSearchResult'; @@ -96,10 +96,10 @@ const RefOptionList = React.forwardRef((pro internalLoadData(pathValue); }; - const onPathSelect = (pathValue: React.Key, isLeaf: boolean) => { + const onPathSelect = (pathValue: React.Key, leaf: boolean) => { onSelect(pathValue, { selected: !checkedSet.has(pathValue) }); - if (!multiple && (isLeaf || (changeOnSelect && expandTrigger === 'hover'))) { + if (!multiple && (leaf || (changeOnSelect && expandTrigger === 'hover'))) { onToggleOpen(false); } }; @@ -177,6 +177,7 @@ const RefOptionList = React.forwardRef((pro const { which } = event; switch (which) { + // >>> Arrow keys case KeyCode.UP: case KeyCode.DOWN: { let offset = 0; @@ -210,6 +211,30 @@ const RefOptionList = React.forwardRef((pro } break; } + + // >>> Select + case KeyCode.ENTER: { + const lastValue = openPath[openPath.length - 1]; + const option = optionColumns[openPath.length - 1].options?.find( + opt => opt.value === lastValue, + ); + onPathSelect(lastValue, isLeaf(option)); + + // Close for changeOnSelect + if (changeOnSelect) { + onToggleOpen(false); + } + break; + } + + // >>> Close + case KeyCode.ESC: { + onToggleOpen(false); + + if (open) { + event.stopPropagation(); + } + } } }, onKeyUp: () => {}, From cf44e1cc3fdc0fd0cc11932475861e9cdcecdff2 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 15:31:14 +0800 Subject: [PATCH 41/54] chore: path selection --- src/OptionList/index.tsx | 11 +++++-- tests/keyboard.spec.js | 62 ++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 97335d5a..5f70bdb4 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -218,7 +218,12 @@ const RefOptionList = React.forwardRef((pro const option = optionColumns[openPath.length - 1].options?.find( opt => opt.value === lastValue, ); - onPathSelect(lastValue, isLeaf(option)); + + const leaf = isLeaf(option); + + if (multiple || changeOnSelect || leaf) { + onPathSelect(lastValue, leaf); + } // Close for changeOnSelect if (changeOnSelect) { @@ -262,7 +267,9 @@ const RefOptionList = React.forwardRef((pro ]; // >>>>> Columns - const mergedOptionColumns = options.length ? optionColumns : [{ options: emptyList }]; + const mergedOptionColumns = optionColumns[0]?.options?.length + ? optionColumns + : [{ options: emptyList }]; const columnNodes: React.ReactElement[] = mergedOptionColumns.map((col, index) => ( { +describe('Cascader.Keyboard', () => { let wrapper; let selectedValue; let menus; @@ -25,37 +25,37 @@ describe('Cascader', () => { menus = null; }); - [['space', KeyCode.SPACE], ['enter', KeyCode.ENTER]].forEach(([name, keyCode]) => { - it(`${name} to open`, () => { - wrapper.find('input').simulate('keyDown', { keyCode }); - expect(wrapper.find('.rc-cascader-menus-hidden').length).toBe(0); + [['space', KeyCode.SPACE], ['enter', KeyCode.ENTER]].forEach(([name, which]) => { + it.only(`${name} to open`, () => { + wrapper.find('input').simulate('keyDown', { which }); + expect(wrapper.isOpen()).toBeTruthy(); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.ESC }); - expect(wrapper.find('.rc-cascader-menus-hidden').length).toBe(1); + wrapper.find('input').simulate('keyDown', { which: KeyCode.ESC }); + expect(wrapper.isOpen()).toBeFalsy(); }); }); it('should have keyboard support', () => { - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); menus = wrapper.find('.rc-cascader-menu'); - expect(wrapper.find('.rc-cascader-menus-hidden').length).toBe(0); + expect(wrapper.isOpen()).toBeTruthy(); expect(menus.length).toBe(1); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.RIGHT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.RIGHT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.LEFT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.QUESTION_MARK }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.QUESTION_MARK }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(3); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.LEFT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); expect( @@ -64,7 +64,7 @@ describe('Cascader', () => { .at(0) .text(), ).toBe(addressOptions[0].label); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); expect( @@ -73,9 +73,9 @@ describe('Cascader', () => { .at(0) .text(), ).toBe(addressOptions[1].label); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.RIGHT }); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.RIGHT }); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.ENTER }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); expect( wrapper .find('.rc-cascader-menus') @@ -86,43 +86,43 @@ describe('Cascader', () => { }); it('should have close menu when press some keys', () => { - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect( wrapper .find('.rc-cascader-menus') .hostNodes() .hasClass('rc-cascader-menus-hidden'), ).toBe(false); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.LEFT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); expect( wrapper .find('.rc-cascader-menus') .hostNodes() .hasClass('rc-cascader-menus-hidden'), ).toBe(true); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect( wrapper .find('.rc-cascader-menus') .hostNodes() .hasClass('rc-cascader-menus-hidden'), ).toBe(false); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.BACKSPACE }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.BACKSPACE }); expect( wrapper .find('.rc-cascader-menus') .hostNodes() .hasClass('rc-cascader-menus-hidden'), ).toBe(true); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect( wrapper .find('.rc-cascader-menus') .hostNodes() .hasClass('rc-cascader-menus-hidden'), ).toBe(false); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.RIGHT }); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.ESC }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.ESC }); expect( wrapper .find('.rc-cascader-menus') @@ -139,11 +139,11 @@ describe('Cascader', () => { , ); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect(wrapper.state().popupVisible).toBeTruthy(); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.ESC }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.ESC }); expect(wrapper.state().popupVisible).toBeFalsy(); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.ENTER }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); expect(onKeyDown).toHaveBeenCalledTimes(3); }); @@ -154,9 +154,9 @@ describe('Cascader', () => { {}} /> , ); - wrapper.find('input').simulate('keyDown', { keyCode: KeyCode.DOWN }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); menus = wrapper.find('.rc-cascader-menu'); - expect(wrapper.find('.rc-cascader-menus-hidden').length).toBe(0); + expect(wrapper.isOpen()).toBeTruthy(); expect(menus.length).toBe(0); }); }); From 48fec4fbbedd2065af45ef607c38683fa33ab64a Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 16:53:00 +0800 Subject: [PATCH 42/54] feat: rtl keyboard --- assets/list.less | 18 ++++++++++ examples/debug.tsx | 1 + src/OptionList/index.tsx | 38 ++++++++++++++++---- tests/keyboard.spec.js | 75 ++++++++++++---------------------------- 4 files changed, 74 insertions(+), 58 deletions(-) diff --git a/assets/list.less b/assets/list.less index fea43b0e..f1a9873b 100644 --- a/assets/list.less +++ b/assets/list.less @@ -76,4 +76,22 @@ } } } + + // ====================== RTL ====================== + &-rtl { + direction: rtl; + + .@{select-prefix}-menu { + flex: none; + margin: 0; + padding: 0; + list-style: none; + border-left: none; + border-right: 1px solid blue; + + &:first-child { + border-right: 0; + } + } + } } diff --git a/examples/debug.tsx b/examples/debug.tsx index 46ea3c1b..eb084cc9 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -118,6 +118,7 @@ const Demo = () => { allowClear defaultValue={multiple ? [defaultValue] : defaultValue} showSearch + direction="rtl" /> ); diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 5f70bdb4..c1e1965f 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -1,5 +1,6 @@ /* eslint-disable default-case */ import * as React from 'react'; +import classNames from 'classnames'; import KeyCode from 'rc-util/lib/KeyCode'; import type { OptionListProps as SelectOptionListProps, @@ -26,8 +27,11 @@ const RefOptionList = React.forwardRef((pro searchValue, onToggleOpen, notFoundContent, + direction, } = props; + const rtl = direction === 'rtl'; + const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext); const { changeOnSelect, expandTrigger, fieldNames, loadData, search } = React.useContext(CascaderContext); @@ -171,6 +175,18 @@ const RefOptionList = React.forwardRef((pro return null; }; + const prevColumn = () => { + setOpenPath(path => path.slice(0, -1)); + }; + + const nextColumn = () => { + const nextColumnIndex = openPath.length; + const nextActiveOption = getActiveOption(nextColumnIndex, 1); + if (nextActiveOption) { + onPathOpen(nextColumnIndex, nextActiveOption.value); + } + }; + React.useImperativeHandle(ref, () => ({ // scrollTo: treeRef.current?.scrollTo, onKeyDown: event => { @@ -199,15 +215,19 @@ const RefOptionList = React.forwardRef((pro } case KeyCode.LEFT: { - setOpenPath(path => path.slice(0, -1)); + if (rtl) { + nextColumn(); + } else { + prevColumn(); + } break; } case KeyCode.RIGHT: { - const nextColumnIndex = openPath.length; - const nextActiveOption = getActiveOption(nextColumnIndex, 1); - if (nextActiveOption) { - onPathOpen(nextColumnIndex, nextActiveOption.value); + if (rtl) { + prevColumn(); + } else { + nextColumn(); } break; } @@ -284,7 +304,13 @@ const RefOptionList = React.forwardRef((pro // >>>>> Render return ( <> -
      {columnNodes}
      +
      + {columnNodes} +
      ); }); diff --git a/tests/keyboard.spec.js b/tests/keyboard.spec.js index 9f0e0a7f..a7f80c6f 100644 --- a/tests/keyboard.spec.js +++ b/tests/keyboard.spec.js @@ -13,10 +13,9 @@ describe('Cascader.Keyboard', () => { }; beforeEach(() => { + // TODO: should also handle keyboard wrapper = mount( - - - , + , ); }); @@ -25,8 +24,13 @@ describe('Cascader.Keyboard', () => { menus = null; }); - [['space', KeyCode.SPACE], ['enter', KeyCode.ENTER]].forEach(([name, which]) => { - it.only(`${name} to open`, () => { + [ + // Space + ['space', KeyCode.SPACE], + // Enter + ['enter', KeyCode.ENTER], + ].forEach(([name, which]) => { + it(`${name} to open`, () => { wrapper.find('input').simulate('keyDown', { which }); expect(wrapper.isOpen()).toBeTruthy(); @@ -58,76 +62,43 @@ describe('Cascader.Keyboard', () => { wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); - expect( - wrapper - .find('.rc-cascader-menu-item-active') - .at(0) - .text(), - ).toBe(addressOptions[0].label); + expect(wrapper.find('.rc-cascader-menu-item-active').at(0).text()).toBe( + addressOptions[0].label, + ); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); menus = wrapper.find('.rc-cascader-menu'); expect(menus.length).toBe(2); - expect( - wrapper - .find('.rc-cascader-menu-item-active') - .at(0) - .text(), - ).toBe(addressOptions[1].label); + expect(wrapper.find('.rc-cascader-menu-item-active').at(0).text()).toBe( + addressOptions[1].label, + ); wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); - expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), - ).toBe(true); + expect(wrapper.isOpen()).toBeFalsy(); expect(selectedValue).toEqual(['zj', 'hangzhou', 'yuhang']); }); - it('should have close menu when press some keys', () => { + it.only('should have close menu when press some keys', () => { wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); - expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), - ).toBe(false); + expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); - expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), - ).toBe(true); + expect(wrapper.isOpen()).toBeFalsy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), + wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), ).toBe(false); wrapper.find('input').simulate('keyDown', { which: KeyCode.BACKSPACE }); expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), + wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), ).toBe(true); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), + wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), ).toBe(false); wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); wrapper.find('input').simulate('keyDown', { which: KeyCode.ESC }); expect( - wrapper - .find('.rc-cascader-menus') - .hostNodes() - .hasClass('rc-cascader-menus-hidden'), + wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), ).toBe(true); }); From 8d431edb27f9cd92a1655104ca07184f3c18184d Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 16:57:27 +0800 Subject: [PATCH 43/54] chore: rtl support --- examples/debug.tsx | 2 +- src/Cascader.tsx | 1 + src/OptionList/index.tsx | 5 ++++- tests/keyboard.spec.js | 16 ++++------------ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/examples/debug.tsx b/examples/debug.tsx index eb084cc9..98d49517 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -118,7 +118,7 @@ const Demo = () => { allowClear defaultValue={multiple ? [defaultValue] : defaultValue} showSearch - direction="rtl" + // direction="rtl" /> ); diff --git a/src/Cascader.tsx b/src/Cascader.tsx index f264d426..1086397a 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -23,6 +23,7 @@ import useSearchConfig from './hooks/useSearchConfig'; * * Update: * - dropdown class change to `rc-cascader-dropdown` + * - direction rtl keyboard * * Deprecated: * - popupVisible diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index c1e1965f..cfde915d 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -176,7 +176,10 @@ const RefOptionList = React.forwardRef((pro }; const prevColumn = () => { - setOpenPath(path => path.slice(0, -1)); + if (openPath.length === 1) { + onToggleOpen(false); + } + setOpenPath(openPath.slice(0, -1)); }; const nextColumn = () => { diff --git a/tests/keyboard.spec.js b/tests/keyboard.spec.js index a7f80c6f..14258128 100644 --- a/tests/keyboard.spec.js +++ b/tests/keyboard.spec.js @@ -84,22 +84,14 @@ describe('Cascader.Keyboard', () => { wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); expect(wrapper.isOpen()).toBeFalsy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); - expect( - wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), - ).toBe(false); + expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.BACKSPACE }); - expect( - wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), - ).toBe(true); + expect(wrapper.isOpen()).toBeFalsy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); - expect( - wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), - ).toBe(false); + expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); wrapper.find('input').simulate('keyDown', { which: KeyCode.ESC }); - expect( - wrapper.find('.rc-cascader-menus').hostNodes().hasClass('rc-cascader-menus-hidden'), - ).toBe(true); + expect(wrapper.isOpen()).toBeFalsy(); }); it('should call the Cascader onKeyDown callback in all cases', () => { From d3e82f3ac4ba660550f144e62bba76d7aba82c41 Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 18:51:08 +0800 Subject: [PATCH 44/54] test: update of onKeyDown --- src/OptionList/index.tsx | 7 ++++++- tests/keyboard.spec.js | 22 +++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index cfde915d..76dfbdc2 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -176,7 +176,7 @@ const RefOptionList = React.forwardRef((pro }; const prevColumn = () => { - if (openPath.length === 1) { + if (openPath.length <= 1) { onToggleOpen(false); } setOpenPath(openPath.slice(0, -1)); @@ -235,6 +235,11 @@ const RefOptionList = React.forwardRef((pro break; } + case KeyCode.BACKSPACE: { + prevColumn(); + break; + } + // >>> Select case KeyCode.ENTER: { const lastValue = openPath[openPath.length - 1]; diff --git a/tests/keyboard.spec.js b/tests/keyboard.spec.js index 14258128..f7e70f58 100644 --- a/tests/keyboard.spec.js +++ b/tests/keyboard.spec.js @@ -14,9 +14,7 @@ describe('Cascader.Keyboard', () => { beforeEach(() => { // TODO: should also handle keyboard - wrapper = mount( - , - ); + wrapper = mount(); }); afterEach(() => { @@ -78,7 +76,7 @@ describe('Cascader.Keyboard', () => { expect(selectedValue).toEqual(['zj', 'hangzhou', 'yuhang']); }); - it.only('should have close menu when press some keys', () => { + it('should have close menu when press some keys', () => { wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); @@ -98,28 +96,26 @@ describe('Cascader.Keyboard', () => { const onKeyDown = jest.fn(); wrapper = mount( - - - , + , ); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); - expect(wrapper.state().popupVisible).toBeTruthy(); + expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.ESC }); - expect(wrapper.state().popupVisible).toBeFalsy(); + expect(wrapper.isOpen()).toBeFalsy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); expect(onKeyDown).toHaveBeenCalledTimes(3); }); - it('should not handle keyDown events when children specify the onKeyDown', () => { + // TODO: This is strange that we need check on this + it.skip('should not handle keyDown events when children specify the onKeyDown', () => { wrapper = mount( {}} /> , ); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); - menus = wrapper.find('.rc-cascader-menu'); - expect(wrapper.isOpen()).toBeTruthy(); - expect(menus.length).toBe(0); + expect(wrapper.isOpen()).toBeFalsy(); }); }); From 63fc4b64062447db16f1470a6311c91efc85c6ce Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 21:37:49 +0800 Subject: [PATCH 45/54] test: rtl test --- coverage/clover.xml | 319 +++++ coverage/coverage-final.json | 11 + coverage/lcov-report/block-navigation.js | 79 ++ coverage/lcov-report/index.html | 140 +++ coverage/lcov-report/prettify.js | 2 + coverage/lcov-report/sort-arrow-sprite.png | Bin 0 -> 209 bytes coverage/lcov-report/sorter.js | 170 +++ coverage/lcov-report/src/Cascader.tsx.html | 1087 +++++++++++++++++ .../src/OptionList/Column.tsx.html | 490 ++++++++ .../lcov-report/src/OptionList/index.html | 125 ++ .../lcov-report/src/OptionList/index.tsx.html | 1057 ++++++++++++++++ coverage/lcov-report/src/context.ts.html | 172 +++ coverage/lcov-report/src/hooks/index.html | 140 +++ .../src/hooks/useSearchConfig.ts.html | 157 +++ .../src/hooks/useSearchResult.ts.html | 346 ++++++ .../src/hooks/useUpdateEffect.ts.html | 133 ++ coverage/lcov-report/src/index.html | 170 +++ coverage/lcov-report/src/index.tsx.html | 85 ++ coverage/lcov-report/src/interface.ts.html | 235 ++++ coverage/lcov-report/src/util.ts.html | 334 +++++ coverage/lcov.info | 688 +++++++++++ examples/debug.tsx | 4 +- tests/keyboard.spec.js | 19 +- 23 files changed, 5961 insertions(+), 2 deletions(-) create mode 100644 coverage/clover.xml create mode 100644 coverage/coverage-final.json create mode 100644 coverage/lcov-report/block-navigation.js create mode 100644 coverage/lcov-report/index.html create mode 100644 coverage/lcov-report/prettify.js create mode 100644 coverage/lcov-report/sort-arrow-sprite.png create mode 100644 coverage/lcov-report/sorter.js create mode 100644 coverage/lcov-report/src/Cascader.tsx.html create mode 100644 coverage/lcov-report/src/OptionList/Column.tsx.html create mode 100644 coverage/lcov-report/src/OptionList/index.html create mode 100644 coverage/lcov-report/src/OptionList/index.tsx.html create mode 100644 coverage/lcov-report/src/context.ts.html create mode 100644 coverage/lcov-report/src/hooks/index.html create mode 100644 coverage/lcov-report/src/hooks/useSearchConfig.ts.html create mode 100644 coverage/lcov-report/src/hooks/useSearchResult.ts.html create mode 100644 coverage/lcov-report/src/hooks/useUpdateEffect.ts.html create mode 100644 coverage/lcov-report/src/index.html create mode 100644 coverage/lcov-report/src/index.tsx.html create mode 100644 coverage/lcov-report/src/interface.ts.html create mode 100644 coverage/lcov-report/src/util.ts.html create mode 100644 coverage/lcov.info diff --git a/coverage/clover.xml b/coverage/clover.xml new file mode 100644 index 00000000..728edfc9 --- /dev/null +++ b/coverage/clover.xml @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/coverage/coverage-final.json b/coverage/coverage-final.json new file mode 100644 index 00000000..3a055f2d --- /dev/null +++ b/coverage/coverage-final.json @@ -0,0 +1,11 @@ +{"/Users/jilin/projects/antd/rc-cascader/src/Cascader.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/Cascader.tsx","statementMap":{"0":{"start":{"line":36,"column":20},"end":{"line":39,"column":2}},"1":{"start":{"line":120,"column":17},"end":{"line":332,"column":2}},"2":{"start":{"line":146,"column":6},"end":{"line":146,"column":11}},"3":{"start":{"line":148,"column":35},"end":{"line":148,"column":44}},"4":{"start":{"line":150,"column":27},"end":{"line":150,"column":88}},"5":{"start":{"line":150,"column":47},"end":{"line":150,"column":73}},"6":{"start":{"line":153,"column":22},"end":{"line":153,"column":52}},"7":{"start":{"line":155,"column":2},"end":{"line":162,"column":6}},"8":{"start":{"line":155,"column":40},"end":{"line":162,"column":3}},"9":{"start":{"line":157,"column":6},"end":{"line":157,"column":34}},"10":{"start":{"line":160,"column":6},"end":{"line":160,"column":33}},"11":{"start":{"line":164,"column":27},"end":{"line":165,"column":54}},"12":{"start":{"line":165,"column":4},"end":{"line":165,"column":54}},"13":{"start":{"line":168,"column":42},"end":{"line":171,"column":4}},"14":{"start":{"line":173,"column":43},"end":{"line":173,"column":70}},"15":{"start":{"line":176,"column":24},"end":{"line":178,"column":33}},"16":{"start":{"line":177,"column":4},"end":{"line":177,"column":53}},"17":{"start":{"line":186,"column":31},"end":{"line":195,"column":3}},"18":{"start":{"line":189,"column":45},"end":{"line":189,"column":47}},"19":{"start":{"line":190,"column":4},"end":{"line":192,"column":5}},"20":{"start":{"line":191,"column":6},"end":{"line":191,"column":82}},"21":{"start":{"line":194,"column":4},"end":{"line":194,"column":43}},"22":{"start":{"line":197,"column":44},"end":{"line":199,"column":3}},"23":{"start":{"line":198,"column":4},"end":{"line":198,"column":47}},"24":{"start":{"line":201,"column":2},"end":{"line":203,"column":14}},"25":{"start":{"line":202,"column":4},"end":{"line":202,"column":50}},"26":{"start":{"line":206,"column":22},"end":{"line":216,"column":3}},"27":{"start":{"line":207,"column":34},"end":{"line":207,"column":50}},"28":{"start":{"line":209,"column":4},"end":{"line":211,"column":5}},"29":{"start":{"line":210,"column":6},"end":{"line":210,"column":42}},"30":{"start":{"line":213,"column":4},"end":{"line":215,"column":17}},"31":{"start":{"line":214,"column":26},"end":{"line":214,"column":41}},"32":{"start":{"line":219,"column":27},"end":{"line":252,"column":3}},"33":{"start":{"line":221,"column":4},"end":{"line":221,"column":24}},"34":{"start":{"line":223,"column":22},"end":{"line":223,"column":71}},"35":{"start":{"line":225,"column":42},"end":{"line":225,"column":44}},"36":{"start":{"line":226,"column":38},"end":{"line":226,"column":40}},"37":{"start":{"line":228,"column":26},"end":{"line":228,"column":82}},"38":{"start":{"line":228,"column":75},"end":{"line":228,"column":81}},"39":{"start":{"line":230,"column":4},"end":{"line":236,"column":7}},"40":{"start":{"line":231,"column":40},"end":{"line":231,"column":88}},"41":{"start":{"line":232,"column":28},"end":{"line":232,"column":67}},"42":{"start":{"line":232,"column":55},"end":{"line":232,"column":66}},"43":{"start":{"line":234,"column":6},"end":{"line":234,"column":75}},"44":{"start":{"line":234,"column":45},"end":{"line":234,"column":72}},"45":{"start":{"line":235,"column":6},"end":{"line":235,"column":38}},"46":{"start":{"line":239,"column":4},"end":{"line":241,"column":5}},"47":{"start":{"line":240,"column":6},"end":{"line":240,"column":34}},"48":{"start":{"line":243,"column":4},"end":{"line":251,"column":5}},"49":{"start":{"line":244,"column":6},"end":{"line":250,"column":7}},"50":{"start":{"line":245,"column":8},"end":{"line":245,"column":62}},"51":{"start":{"line":249,"column":8},"end":{"line":249,"column":78}},"52":{"start":{"line":255,"column":2},"end":{"line":261,"column":3}},"53":{"start":{"line":256,"column":4},"end":{"line":259,"column":6}},"54":{"start":{"line":260,"column":4},"end":{"line":260,"column":100}},"55":{"start":{"line":263,"column":21},"end":{"line":263,"column":61}},"56":{"start":{"line":265,"column":42},"end":{"line":268,"column":3}},"57":{"start":{"line":266,"column":4},"end":{"line":266,"column":43}},"58":{"start":{"line":267,"column":4},"end":{"line":267,"column":40}},"59":{"start":{"line":271,"column":18},"end":{"line":292,"column":3}},"60":{"start":{"line":272,"column":11},"end":{"line":281,"column":5}},"61":{"start":{"line":297,"column":4},"end":{"line":303,"column":9}},"62":{"start":{"line":305,"column":2},"end":{"line":331,"column":4}},"63":{"start":{"line":322,"column":30},"end":{"line":322,"column":34}},"64":{"start":{"line":327,"column":36},"end":{"line":327,"column":44}},"65":{"start":{"line":334,"column":0},"end":{"line":334,"column":34}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":120,"column":34},"end":{"line":120,"column":35}},"loc":{"start":{"line":120,"column":89},"end":{"line":332,"column":1}},"line":120},"1":{"name":"(anonymous_1)","decl":{"start":{"line":150,"column":41},"end":{"line":150,"column":42}},"loc":{"start":{"line":150,"column":47},"end":{"line":150,"column":73}},"line":150},"2":{"name":"(anonymous_2)","decl":{"start":{"line":155,"column":33},"end":{"line":155,"column":34}},"loc":{"start":{"line":155,"column":40},"end":{"line":162,"column":3}},"line":155},"3":{"name":"(anonymous_3)","decl":{"start":{"line":156,"column":11},"end":{"line":156,"column":12}},"loc":{"start":{"line":156,"column":17},"end":{"line":158,"column":5}},"line":156},"4":{"name":"(anonymous_4)","decl":{"start":{"line":159,"column":10},"end":{"line":159,"column":11}},"loc":{"start":{"line":159,"column":16},"end":{"line":161,"column":5}},"line":159},"5":{"name":"(anonymous_5)","decl":{"start":{"line":164,"column":27},"end":{"line":164,"column":28}},"loc":{"start":{"line":165,"column":4},"end":{"line":165,"column":54}},"line":165},"6":{"name":"(anonymous_6)","decl":{"start":{"line":176,"column":38},"end":{"line":176,"column":39}},"loc":{"start":{"line":176,"column":44},"end":{"line":178,"column":3}},"line":176},"7":{"name":"(anonymous_7)","decl":{"start":{"line":186,"column":31},"end":{"line":186,"column":32}},"loc":{"start":{"line":188,"column":20},"end":{"line":195,"column":3}},"line":188},"8":{"name":"(anonymous_8)","decl":{"start":{"line":197,"column":59},"end":{"line":197,"column":60}},"loc":{"start":{"line":198,"column":4},"end":{"line":198,"column":47}},"line":198},"9":{"name":"(anonymous_9)","decl":{"start":{"line":201,"column":18},"end":{"line":201,"column":19}},"loc":{"start":{"line":201,"column":24},"end":{"line":203,"column":3}},"line":201},"10":{"name":"(anonymous_10)","decl":{"start":{"line":206,"column":22},"end":{"line":206,"column":23}},"loc":{"start":{"line":206,"column":51},"end":{"line":216,"column":3}},"line":206},"11":{"name":"(anonymous_11)","decl":{"start":{"line":214,"column":19},"end":{"line":214,"column":20}},"loc":{"start":{"line":214,"column":26},"end":{"line":214,"column":41}},"line":214},"12":{"name":"(anonymous_12)","decl":{"start":{"line":219,"column":27},"end":{"line":219,"column":28}},"loc":{"start":{"line":219,"column":75},"end":{"line":252,"column":3}},"line":219},"13":{"name":"(anonymous_13)","decl":{"start":{"line":228,"column":65},"end":{"line":228,"column":66}},"loc":{"start":{"line":228,"column":75},"end":{"line":228,"column":81}},"line":228},"14":{"name":"(anonymous_14)","decl":{"start":{"line":230,"column":26},"end":{"line":230,"column":27}},"loc":{"start":{"line":230,"column":36},"end":{"line":236,"column":5}},"line":230},"15":{"name":"(anonymous_15)","decl":{"start":{"line":232,"column":45},"end":{"line":232,"column":46}},"loc":{"start":{"line":232,"column":55},"end":{"line":232,"column":66}},"line":232},"16":{"name":"(anonymous_16)","decl":{"start":{"line":234,"column":38},"end":{"line":234,"column":39}},"loc":{"start":{"line":234,"column":45},"end":{"line":234,"column":72}},"line":234},"17":{"name":"(anonymous_17)","decl":{"start":{"line":265,"column":42},"end":{"line":265,"column":43}},"loc":{"start":{"line":265,"column":68},"end":{"line":268,"column":3}},"line":265},"18":{"name":"(anonymous_18)","decl":{"start":{"line":272,"column":4},"end":{"line":272,"column":5}},"loc":{"start":{"line":272,"column":11},"end":{"line":281,"column":5}},"line":272},"19":{"name":"(anonymous_19)","decl":{"start":{"line":322,"column":24},"end":{"line":322,"column":25}},"loc":{"start":{"line":322,"column":30},"end":{"line":322,"column":34}},"line":322},"20":{"name":"(anonymous_20)","decl":{"start":{"line":327,"column":30},"end":{"line":327,"column":31}},"loc":{"start":{"line":327,"column":36},"end":{"line":327,"column":44}},"line":327}},"branchMap":{"0":{"loc":{"start":{"line":139,"column":4},"end":{"line":139,"column":20}},"type":"default-arg","locations":[{"start":{"line":139,"column":17},"end":{"line":139,"column":20}}],"line":139},"1":{"loc":{"start":{"line":190,"column":4},"end":{"line":192,"column":5}},"type":"if","locations":[{"start":{"line":190,"column":4},"end":{"line":192,"column":5}},{"start":{"line":190,"column":4},"end":{"line":192,"column":5}}],"line":190},"2":{"loc":{"start":{"line":191,"column":23},"end":{"line":191,"column":57}},"type":"cond-expr","locations":[{"start":{"line":191,"column":34},"end":{"line":191,"column":43}},{"start":{"line":191,"column":46},"end":{"line":191,"column":57}}],"line":191},"3":{"loc":{"start":{"line":198,"column":25},"end":{"line":198,"column":46}},"type":"binary-expr","locations":[{"start":{"line":198,"column":25},"end":{"line":198,"column":30}},{"start":{"line":198,"column":34},"end":{"line":198,"column":46}}],"line":198},"4":{"loc":{"start":{"line":209,"column":4},"end":{"line":211,"column":5}},"type":"if","locations":[{"start":{"line":209,"column":4},"end":{"line":211,"column":5}},{"start":{"line":209,"column":4},"end":{"line":211,"column":5}}],"line":209},"5":{"loc":{"start":{"line":223,"column":23},"end":{"line":223,"column":55}},"type":"cond-expr","locations":[{"start":{"line":223,"column":34},"end":{"line":223,"column":42}},{"start":{"line":223,"column":45},"end":{"line":223,"column":55}}],"line":223},"6":{"loc":{"start":{"line":239,"column":4},"end":{"line":241,"column":5}},"type":"if","locations":[{"start":{"line":239,"column":4},"end":{"line":241,"column":5}},{"start":{"line":239,"column":4},"end":{"line":241,"column":5}}],"line":239},"7":{"loc":{"start":{"line":243,"column":4},"end":{"line":251,"column":5}},"type":"if","locations":[{"start":{"line":243,"column":4},"end":{"line":251,"column":5}},{"start":{"line":243,"column":4},"end":{"line":251,"column":5}}],"line":243},"8":{"loc":{"start":{"line":244,"column":6},"end":{"line":250,"column":7}},"type":"if","locations":[{"start":{"line":244,"column":6},"end":{"line":250,"column":7}},{"start":{"line":244,"column":6},"end":{"line":250,"column":7}}],"line":244},"9":{"loc":{"start":{"line":249,"column":37},"end":{"line":249,"column":54}},"type":"binary-expr","locations":[{"start":{"line":249,"column":37},"end":{"line":249,"column":48}},{"start":{"line":249,"column":52},"end":{"line":249,"column":54}}],"line":249},"10":{"loc":{"start":{"line":249,"column":56},"end":{"line":249,"column":76}},"type":"binary-expr","locations":[{"start":{"line":249,"column":56},"end":{"line":249,"column":70}},{"start":{"line":249,"column":74},"end":{"line":249,"column":76}}],"line":249},"11":{"loc":{"start":{"line":255,"column":2},"end":{"line":261,"column":3}},"type":"if","locations":[{"start":{"line":255,"column":2},"end":{"line":261,"column":3}},{"start":{"line":255,"column":2},"end":{"line":261,"column":3}}],"line":255},"12":{"loc":{"start":{"line":263,"column":21},"end":{"line":263,"column":61}},"type":"cond-expr","locations":[{"start":{"line":263,"column":42},"end":{"line":263,"column":46}},{"start":{"line":263,"column":49},"end":{"line":263,"column":61}}],"line":263},"13":{"loc":{"start":{"line":297,"column":4},"end":{"line":303,"column":9}},"type":"cond-expr","locations":[{"start":{"line":300,"column":8},"end":{"line":300,"column":10}},{"start":{"line":301,"column":8},"end":{"line":303,"column":9}}],"line":297},"14":{"loc":{"start":{"line":297,"column":4},"end":{"line":299,"column":25}},"type":"binary-expr","locations":[{"start":{"line":297,"column":5},"end":{"line":297,"column":17}},{"start":{"line":297,"column":21},"end":{"line":297,"column":49}},{"start":{"line":299,"column":4},"end":{"line":299,"column":25}}],"line":297},"15":{"loc":{"start":{"line":310,"column":15},"end":{"line":310,"column":58}},"type":"cond-expr","locations":[{"start":{"line":310,"column":26},"end":{"line":310,"column":39}},{"start":{"line":310,"column":42},"end":{"line":310,"column":58}}],"line":310}},"s":{"0":4,"1":4,"2":51,"3":51,"4":51,"5":35,"6":51,"7":51,"8":0,"9":0,"10":0,"11":51,"12":7,"13":51,"14":51,"15":51,"16":35,"17":51,"18":36,"19":36,"20":9,"21":36,"22":51,"23":35,"24":51,"25":1,"26":51,"27":13,"28":13,"29":0,"30":13,"31":32,"32":51,"33":7,"34":7,"35":7,"36":7,"37":7,"38":7,"39":7,"40":7,"41":7,"42":17,"43":7,"44":17,"45":7,"46":7,"47":6,"48":7,"49":6,"50":0,"51":6,"52":51,"53":51,"54":51,"55":51,"56":51,"57":50,"58":50,"59":51,"60":35,"61":51,"62":51,"63":12,"64":151,"65":4},"f":{"0":51,"1":35,"2":0,"3":0,"4":0,"5":7,"6":35,"7":36,"8":35,"9":1,"10":13,"11":32,"12":7,"13":7,"14":7,"15":17,"16":17,"17":50,"18":35,"19":12,"20":151},"b":{"0":[40],"1":[9,27],"2":[0,9],"3":[35,33],"4":[0,13],"5":[0,7],"6":[6,1],"7":[6,1],"8":[0,6],"9":[6,0],"10":[6,0],"11":[51,0],"12":[12,39],"13":[9,42],"14":[51,6,45],"15":[0,51]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"0943459e78a382e0357268a89b0627ee6ceff367"} +,"/Users/jilin/projects/antd/rc-cascader/src/context.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/context.ts","statementMap":{"0":{"start":{"line":5,"column":24},"end":{"line":29,"column":2}}},"fnMap":{},"branchMap":{},"s":{"0":4},"f":{},"b":{},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"52c49f34245bd46782adce8a6aaabb426b8c46bc"} +,"/Users/jilin/projects/antd/rc-cascader/src/index.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/index.tsx","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"f1bf564db6e30a965dc2d76a1c380f191d85b0bf"} +,"/Users/jilin/projects/antd/rc-cascader/src/interface.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/interface.ts","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"hash":"d79da64d0d4463ae415da4e74750b2f83ea87810"} +,"/Users/jilin/projects/antd/rc-cascader/src/util.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/util.ts","statementMap":{"0":{"start":{"line":10,"column":20},"end":{"line":10,"column":43}},"1":{"start":{"line":23,"column":34},"end":{"line":23,"column":36}},"2":{"start":{"line":24,"column":38},"end":{"line":24,"column":40}},"3":{"start":{"line":26,"column":16},"end":{"line":26,"column":22}},"4":{"start":{"line":28,"column":2},"end":{"line":33,"column":3}},"5":{"start":{"line":29,"column":4},"end":{"line":29,"column":54}},"6":{"start":{"line":30,"column":4},"end":{"line":30,"column":66}},"7":{"start":{"line":32,"column":4},"end":{"line":32,"column":29}},"8":{"start":{"line":35,"column":2},"end":{"line":35,"column":27}},"9":{"start":{"line":39,"column":29},"end":{"line":39,"column":35}},"10":{"start":{"line":41,"column":15},"end":{"line":41,"column":27}},"11":{"start":{"line":43,"column":2},"end":{"line":43,"column":55}},"12":{"start":{"line":53,"column":2},"end":{"line":53,"column":41}},"13":{"start":{"line":66,"column":4},"end":{"line":81,"column":7}},"14":{"start":{"line":67,"column":23},"end":{"line":69,"column":28}},"15":{"start":{"line":70,"column":26},"end":{"line":74,"column":7}},"16":{"start":{"line":76,"column":6},"end":{"line":78,"column":7}},"17":{"start":{"line":77,"column":8},"end":{"line":77,"column":87}},"18":{"start":{"line":80,"column":6},"end":{"line":80,"column":25}},"19":{"start":{"line":84,"column":2},"end":{"line":84,"column":30}}},"fnMap":{"0":{"name":"restoreCompatibleValue","decl":{"start":{"line":16,"column":16},"end":{"line":16,"column":38}},"loc":{"start":{"line":22,"column":2},"end":{"line":36,"column":1}},"line":22},"1":{"name":"isLeaf","decl":{"start":{"line":38,"column":16},"end":{"line":38,"column":22}},"loc":{"start":{"line":38,"column":47},"end":{"line":44,"column":1}},"line":38},"2":{"name":"connectValue","decl":{"start":{"line":52,"column":16},"end":{"line":52,"column":28}},"loc":{"start":{"line":52,"column":49},"end":{"line":54,"column":1}},"line":52},"3":{"name":"convertOptions","decl":{"start":{"line":61,"column":16},"end":{"line":61,"column":30}},"loc":{"start":{"line":64,"column":22},"end":{"line":85,"column":1}},"line":64},"4":{"name":"injectValue","decl":{"start":{"line":65,"column":11},"end":{"line":65,"column":22}},"loc":{"start":{"line":65,"column":79},"end":{"line":82,"column":3}},"line":65},"5":{"name":"(anonymous_5)","decl":{"start":{"line":66,"column":28},"end":{"line":66,"column":29}},"loc":{"start":{"line":66,"column":38},"end":{"line":81,"column":5}},"line":66}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":9},"end":{"line":43,"column":54}},"type":"cond-expr","locations":[{"start":{"line":43,"column":30},"end":{"line":43,"column":34}},{"start":{"line":43,"column":37},"end":{"line":43,"column":54}}],"line":43},"1":{"loc":{"start":{"line":53,"column":10},"end":{"line":53,"column":21}},"type":"binary-expr","locations":[{"start":{"line":53,"column":10},"end":{"line":53,"column":15}},{"start":{"line":53,"column":19},"end":{"line":53,"column":21}}],"line":53},"2":{"loc":{"start":{"line":65,"column":41},"end":{"line":65,"column":57}},"type":"default-arg","locations":[{"start":{"line":65,"column":55},"end":{"line":65,"column":57}}],"line":65},"3":{"loc":{"start":{"line":66,"column":12},"end":{"line":66,"column":22}},"type":"binary-expr","locations":[{"start":{"line":66,"column":12},"end":{"line":66,"column":16}},{"start":{"line":66,"column":20},"end":{"line":66,"column":22}}],"line":66},"4":{"loc":{"start":{"line":67,"column":23},"end":{"line":69,"column":28}},"type":"cond-expr","locations":[{"start":{"line":68,"column":10},"end":{"line":68,"column":57}},{"start":{"line":69,"column":10},"end":{"line":69,"column":28}}],"line":67},"5":{"loc":{"start":{"line":76,"column":6},"end":{"line":78,"column":7}},"type":"if","locations":[{"start":{"line":76,"column":6},"end":{"line":78,"column":7}},{"start":{"line":76,"column":6},"end":{"line":78,"column":7}}],"line":76}},"s":{"0":4,"1":61,"2":61,"3":61,"4":61,"5":133,"6":133,"7":133,"8":61,"9":519,"10":519,"11":519,"12":187,"13":162,"14":267,"15":267,"16":267,"17":127,"18":267,"19":35},"f":{"0":61,"1":519,"2":187,"3":35,"4":162,"5":267},"b":{"0":[6,513],"1":[187,0],"2":[35],"3":[162,1],"4":[178,89],"5":[127,140]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"3553d2b2bf6f9395203010e4e139e2ae0df0c88b"} +,"/Users/jilin/projects/antd/rc-cascader/src/OptionList/Column.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/OptionList/Column.tsx","statementMap":{"0":{"start":{"line":35,"column":24},"end":{"line":35,"column":43}},"1":{"start":{"line":36,"column":28},"end":{"line":36,"column":52}},"2":{"start":{"line":37,"column":28},"end":{"line":37,"column":59}},"3":{"start":{"line":40,"column":4},"end":{"line":40,"column":37}},"4":{"start":{"line":43,"column":2},"end":{"line":136,"column":4}},"5":{"start":{"line":46,"column":42},"end":{"line":46,"column":48}},"6":{"start":{"line":47,"column":29},"end":{"line":47,"column":43}},"7":{"start":{"line":49,"column":26},"end":{"line":49,"column":53}},"8":{"start":{"line":52,"column":24},"end":{"line":52,"column":45}},"9":{"start":{"line":55,"column":32},"end":{"line":59,"column":9}},"10":{"start":{"line":56,"column":10},"end":{"line":58,"column":11}},"11":{"start":{"line":57,"column":12},"end":{"line":57,"column":33}},"12":{"start":{"line":62,"column":30},"end":{"line":66,"column":9}},"13":{"start":{"line":63,"column":10},"end":{"line":65,"column":11}},"14":{"start":{"line":64,"column":12},"end":{"line":64,"column":42}},"15":{"start":{"line":70,"column":8},"end":{"line":74,"column":9}},"16":{"start":{"line":71,"column":10},"end":{"line":71,"column":29}},"17":{"start":{"line":72,"column":15},"end":{"line":74,"column":9}},"18":{"start":{"line":73,"column":10},"end":{"line":73,"column":31}},"19":{"start":{"line":77,"column":8},"end":{"line":133,"column":10}},"20":{"start":{"line":90,"column":14},"end":{"line":92,"column":15}},"21":{"start":{"line":91,"column":16},"end":{"line":91,"column":34}},"22":{"start":{"line":106,"column":18},"end":{"line":106,"column":34}},"23":{"start":{"line":113,"column":16},"end":{"line":113,"column":34}},"24":{"start":{"line":114,"column":16},"end":{"line":116,"column":17}},"25":{"start":{"line":115,"column":18},"end":{"line":115,"column":34}},"26":{"start":{"line":119,"column":16},"end":{"line":121,"column":17}},"27":{"start":{"line":120,"column":18},"end":{"line":120,"column":38}}},"fnMap":{"0":{"name":"Column","decl":{"start":{"line":22,"column":24},"end":{"line":22,"column":30}},"loc":{"start":{"line":34,"column":16},"end":{"line":137,"column":1}},"line":34},"1":{"name":"(anonymous_1)","decl":{"start":{"line":45,"column":19},"end":{"line":45,"column":20}},"loc":{"start":{"line":45,"column":29},"end":{"line":134,"column":7}},"line":45},"2":{"name":"(anonymous_2)","decl":{"start":{"line":55,"column":32},"end":{"line":55,"column":33}},"loc":{"start":{"line":55,"column":38},"end":{"line":59,"column":9}},"line":55},"3":{"name":"(anonymous_3)","decl":{"start":{"line":62,"column":30},"end":{"line":62,"column":31}},"loc":{"start":{"line":62,"column":36},"end":{"line":66,"column":9}},"line":62},"4":{"name":"(anonymous_4)","decl":{"start":{"line":89,"column":26},"end":{"line":89,"column":27}},"loc":{"start":{"line":89,"column":32},"end":{"line":93,"column":13}},"line":89},"5":{"name":"(anonymous_5)","decl":{"start":{"line":105,"column":25},"end":{"line":105,"column":26}},"loc":{"start":{"line":105,"column":31},"end":{"line":107,"column":17}},"line":105},"6":{"name":"(anonymous_6)","decl":{"start":{"line":112,"column":23},"end":{"line":112,"column":24}},"loc":{"start":{"line":112,"column":29},"end":{"line":117,"column":15}},"line":112},"7":{"name":"(anonymous_7)","decl":{"start":{"line":118,"column":29},"end":{"line":118,"column":30}},"loc":{"start":{"line":118,"column":35},"end":{"line":122,"column":15}},"line":118}},"branchMap":{"0":{"loc":{"start":{"line":56,"column":10},"end":{"line":58,"column":11}},"type":"if","locations":[{"start":{"line":56,"column":10},"end":{"line":58,"column":11}},{"start":{"line":56,"column":10},"end":{"line":58,"column":11}}],"line":56},"1":{"loc":{"start":{"line":56,"column":14},"end":{"line":56,"column":40}},"type":"binary-expr","locations":[{"start":{"line":56,"column":14},"end":{"line":56,"column":23}},{"start":{"line":56,"column":27},"end":{"line":56,"column":40}}],"line":56},"2":{"loc":{"start":{"line":63,"column":10},"end":{"line":65,"column":11}},"type":"if","locations":[{"start":{"line":63,"column":10},"end":{"line":65,"column":11}},{"start":{"line":63,"column":10},"end":{"line":65,"column":11}}],"line":63},"3":{"loc":{"start":{"line":63,"column":14},"end":{"line":63,"column":71}},"type":"binary-expr","locations":[{"start":{"line":63,"column":14},"end":{"line":63,"column":23}},{"start":{"line":63,"column":28},"end":{"line":63,"column":40}},{"start":{"line":63,"column":44},"end":{"line":63,"column":58}},{"start":{"line":63,"column":62},"end":{"line":63,"column":70}}],"line":63},"4":{"loc":{"start":{"line":70,"column":8},"end":{"line":74,"column":9}},"type":"if","locations":[{"start":{"line":70,"column":8},"end":{"line":74,"column":9}},{"start":{"line":70,"column":8},"end":{"line":74,"column":9}}],"line":70},"5":{"loc":{"start":{"line":72,"column":15},"end":{"line":74,"column":9}},"type":"if","locations":[{"start":{"line":72,"column":15},"end":{"line":74,"column":9}},{"start":{"line":72,"column":15},"end":{"line":74,"column":9}}],"line":72},"6":{"loc":{"start":{"line":90,"column":14},"end":{"line":92,"column":15}},"type":"if","locations":[{"start":{"line":90,"column":14},"end":{"line":92,"column":15}},{"start":{"line":90,"column":14},"end":{"line":92,"column":15}}],"line":90},"7":{"loc":{"start":{"line":95,"column":13},"end":{"line":109,"column":13}},"type":"binary-expr","locations":[{"start":{"line":95,"column":13},"end":{"line":95,"column":21}},{"start":{"line":96,"column":14},"end":{"line":108,"column":16}}],"line":95},"8":{"loc":{"start":{"line":101,"column":60},"end":{"line":101,"column":97}},"type":"binary-expr","locations":[{"start":{"line":101,"column":60},"end":{"line":101,"column":68}},{"start":{"line":101,"column":72},"end":{"line":101,"column":97}}],"line":101},"9":{"loc":{"start":{"line":114,"column":16},"end":{"line":116,"column":17}},"type":"if","locations":[{"start":{"line":114,"column":16},"end":{"line":116,"column":17}},{"start":{"line":114,"column":16},"end":{"line":116,"column":17}}],"line":114},"10":{"loc":{"start":{"line":114,"column":20},"end":{"line":114,"column":45}},"type":"binary-expr","locations":[{"start":{"line":114,"column":20},"end":{"line":114,"column":29}},{"start":{"line":114,"column":33},"end":{"line":114,"column":45}}],"line":114},"11":{"loc":{"start":{"line":119,"column":16},"end":{"line":121,"column":17}},"type":"if","locations":[{"start":{"line":119,"column":16},"end":{"line":121,"column":17}},{"start":{"line":119,"column":16},"end":{"line":121,"column":17}}],"line":119},"12":{"loc":{"start":{"line":126,"column":13},"end":{"line":128,"column":13}},"type":"binary-expr","locations":[{"start":{"line":126,"column":13},"end":{"line":126,"column":23}},{"start":{"line":126,"column":27},"end":{"line":126,"column":37}},{"start":{"line":126,"column":41},"end":{"line":126,"column":54}},{"start":{"line":127,"column":14},"end":{"line":127,"column":84}}],"line":126},"13":{"loc":{"start":{"line":129,"column":13},"end":{"line":131,"column":13}},"type":"binary-expr","locations":[{"start":{"line":129,"column":13},"end":{"line":129,"column":22}},{"start":{"line":129,"column":26},"end":{"line":129,"column":37}},{"start":{"line":130,"column":14},"end":{"line":130,"column":81}}],"line":129}},"s":{"0":217,"1":217,"2":217,"3":217,"4":217,"5":493,"6":493,"7":493,"8":493,"9":493,"10":19,"11":15,"12":493,"13":15,"14":6,"15":493,"16":4,"17":489,"18":489,"19":493,"20":4,"21":4,"22":0,"23":15,"24":15,"25":15,"26":1,"27":1},"f":{"0":217,"1":493,"2":19,"3":15,"4":4,"5":0,"6":15,"7":1},"b":{"0":[15,4],"1":[19,19],"2":[6,9],"3":[15,15,11,9],"4":[4,489],"5":[489,0],"6":[4,0],"7":[493,0],"8":[0,0],"9":[15,0],"10":[15,0],"11":[1,0],"12":[493,492,346,251],"13":[493,1,0]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"a3166b972e9a5ddccbba40dc4d4fef34dddf6ece"} +,"/Users/jilin/projects/antd/rc-cascader/src/OptionList/index.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/OptionList/index.tsx","statementMap":{"0":{"start":{"line":19,"column":22},"end":{"line":324,"column":2}},"1":{"start":{"line":31,"column":6},"end":{"line":31,"column":11}},"2":{"start":{"line":33,"column":14},"end":{"line":33,"column":33}},"3":{"start":{"line":35,"column":43},"end":{"line":35,"column":74}},"4":{"start":{"line":37,"column":4},"end":{"line":37,"column":37}},"5":{"start":{"line":40,"column":40},"end":{"line":40,"column":58}},"6":{"start":{"line":42,"column":27},"end":{"line":56,"column":3}},"7":{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},"8":{"start":{"line":44,"column":6},"end":{"line":44,"column":13}},"9":{"start":{"line":47,"column":19},"end":{"line":47,"column":95}},"10":{"start":{"line":47,"column":56},"end":{"line":47,"column":94}},"11":{"start":{"line":48,"column":4},"end":{"line":55,"column":5}},"12":{"start":{"line":49,"column":38},"end":{"line":49,"column":87}},"13":{"start":{"line":50,"column":28},"end":{"line":50,"column":59}},"14":{"start":{"line":50,"column":50},"end":{"line":50,"column":58}},"15":{"start":{"line":52,"column":6},"end":{"line":52,"column":81}},"16":{"start":{"line":52,"column":29},"end":{"line":52,"column":79}},"17":{"start":{"line":54,"column":6},"end":{"line":54,"column":30}},"18":{"start":{"line":59,"column":2},"end":{"line":68,"column":36}},"19":{"start":{"line":60,"column":4},"end":{"line":67,"column":5}},"20":{"start":{"line":61,"column":6},"end":{"line":66,"column":9}},"21":{"start":{"line":62,"column":23},"end":{"line":62,"column":75}},"22":{"start":{"line":62,"column":50},"end":{"line":62,"column":74}},"23":{"start":{"line":63,"column":8},"end":{"line":65,"column":9}},"24":{"start":{"line":64,"column":10},"end":{"line":64,"column":73}},"25":{"start":{"line":64,"column":33},"end":{"line":64,"column":71}},"26":{"start":{"line":64,"column":52},"end":{"line":64,"column":70}},"27":{"start":{"line":71,"column":21},"end":{"line":71,"column":77}},"28":{"start":{"line":71,"column":41},"end":{"line":71,"column":61}},"29":{"start":{"line":72,"column":25},"end":{"line":72,"column":89}},"30":{"start":{"line":72,"column":45},"end":{"line":72,"column":69}},"31":{"start":{"line":75,"column":34},"end":{"line":75,"column":65}},"32":{"start":{"line":77,"column":2},"end":{"line":92,"column":13}},"33":{"start":{"line":78,"column":4},"end":{"line":91,"column":5}},"34":{"start":{"line":79,"column":38},"end":{"line":79,"column":40}},"35":{"start":{"line":81,"column":6},"end":{"line":89,"column":7}},"36":{"start":{"line":82,"column":23},"end":{"line":84,"column":9}},"37":{"start":{"line":83,"column":27},"end":{"line":83,"column":70}},"38":{"start":{"line":86,"column":8},"end":{"line":88,"column":9}},"39":{"start":{"line":87,"column":10},"end":{"line":87,"column":80}},"40":{"start":{"line":90,"column":6},"end":{"line":90,"column":32}},"41":{"start":{"line":95,"column":21},"end":{"line":101,"column":3}},"42":{"start":{"line":96,"column":25},"end":{"line":96,"column":65}},"43":{"start":{"line":97,"column":4},"end":{"line":97,"column":30}},"44":{"start":{"line":100,"column":4},"end":{"line":100,"column":32}},"45":{"start":{"line":103,"column":23},"end":{"line":109,"column":3}},"46":{"start":{"line":104,"column":4},"end":{"line":104,"column":66}},"47":{"start":{"line":106,"column":4},"end":{"line":108,"column":5}},"48":{"start":{"line":107,"column":6},"end":{"line":107,"column":26}},"49":{"start":{"line":111,"column":22},"end":{"line":119,"column":3}},"50":{"start":{"line":112,"column":25},"end":{"line":112,"column":32}},"51":{"start":{"line":114,"column":4},"end":{"line":116,"column":5}},"52":{"start":{"line":114,"column":17},"end":{"line":114,"column":18}},"53":{"start":{"line":115,"column":6},"end":{"line":115,"column":100}},"54":{"start":{"line":115,"column":61},"end":{"line":115,"column":89}},"55":{"start":{"line":118,"column":4},"end":{"line":118,"column":26}},"56":{"start":{"line":122,"column":24},"end":{"line":127,"column":4}},"57":{"start":{"line":130,"column":24},"end":{"line":156,"column":44}},"58":{"start":{"line":131,"column":4},"end":{"line":137,"column":5}},"59":{"start":{"line":132,"column":6},"end":{"line":136,"column":8}},"60":{"start":{"line":141,"column":10},"end":{"line":141,"column":12}},"61":{"start":{"line":143,"column":4},"end":{"line":153,"column":5}},"62":{"start":{"line":143,"column":17},"end":{"line":143,"column":18}},"63":{"start":{"line":144,"column":25},"end":{"line":144,"column":58}},"64":{"start":{"line":146,"column":6},"end":{"line":152,"column":7}},"65":{"start":{"line":147,"column":8},"end":{"line":149,"column":11}},"66":{"start":{"line":151,"column":8},"end":{"line":151,"column":14}},"67":{"start":{"line":155,"column":4},"end":{"line":155,"column":28}},"68":{"start":{"line":159,"column":26},"end":{"line":176,"column":3}},"69":{"start":{"line":160,"column":28},"end":{"line":160,"column":55}},"70":{"start":{"line":161,"column":27},"end":{"line":161,"column":74}},"71":{"start":{"line":162,"column":30},"end":{"line":162,"column":92}},"72":{"start":{"line":162,"column":62},"end":{"line":162,"column":91}},"73":{"start":{"line":164,"column":16},"end":{"line":164,"column":37}},"74":{"start":{"line":166,"column":4},"end":{"line":173,"column":5}},"75":{"start":{"line":166,"column":17},"end":{"line":166,"column":18}},"76":{"start":{"line":167,"column":22},"end":{"line":167,"column":66}},"77":{"start":{"line":168,"column":21},"end":{"line":168,"column":44}},"78":{"start":{"line":170,"column":6},"end":{"line":172,"column":7}},"79":{"start":{"line":171,"column":8},"end":{"line":171,"column":22}},"80":{"start":{"line":175,"column":4},"end":{"line":175,"column":16}},"81":{"start":{"line":178,"column":21},"end":{"line":183,"column":3}},"82":{"start":{"line":179,"column":4},"end":{"line":181,"column":5}},"83":{"start":{"line":180,"column":6},"end":{"line":180,"column":26}},"84":{"start":{"line":182,"column":4},"end":{"line":182,"column":39}},"85":{"start":{"line":185,"column":21},"end":{"line":191,"column":3}},"86":{"start":{"line":186,"column":28},"end":{"line":186,"column":43}},"87":{"start":{"line":187,"column":29},"end":{"line":187,"column":64}},"88":{"start":{"line":188,"column":4},"end":{"line":190,"column":5}},"89":{"start":{"line":189,"column":6},"end":{"line":189,"column":58}},"90":{"start":{"line":193,"column":2},"end":{"line":274,"column":6}},"91":{"start":{"line":193,"column":40},"end":{"line":274,"column":3}},"92":{"start":{"line":196,"column":24},"end":{"line":196,"column":29}},"93":{"start":{"line":198,"column":6},"end":{"line":271,"column":7}},"94":{"start":{"line":202,"column":23},"end":{"line":202,"column":24}},"95":{"start":{"line":203,"column":10},"end":{"line":207,"column":11}},"96":{"start":{"line":204,"column":12},"end":{"line":204,"column":24}},"97":{"start":{"line":205,"column":17},"end":{"line":207,"column":11}},"98":{"start":{"line":206,"column":12},"end":{"line":206,"column":23}},"99":{"start":{"line":209,"column":10},"end":{"line":215,"column":11}},"100":{"start":{"line":210,"column":38},"end":{"line":210,"column":70}},"101":{"start":{"line":211,"column":37},"end":{"line":211,"column":79}},"102":{"start":{"line":212,"column":12},"end":{"line":214,"column":13}},"103":{"start":{"line":213,"column":14},"end":{"line":213,"column":68}},"104":{"start":{"line":217,"column":10},"end":{"line":217,"column":16}},"105":{"start":{"line":221,"column":10},"end":{"line":225,"column":11}},"106":{"start":{"line":222,"column":12},"end":{"line":222,"column":25}},"107":{"start":{"line":224,"column":12},"end":{"line":224,"column":25}},"108":{"start":{"line":226,"column":10},"end":{"line":226,"column":16}},"109":{"start":{"line":230,"column":10},"end":{"line":234,"column":11}},"110":{"start":{"line":231,"column":12},"end":{"line":231,"column":25}},"111":{"start":{"line":233,"column":12},"end":{"line":233,"column":25}},"112":{"start":{"line":235,"column":10},"end":{"line":235,"column":16}},"113":{"start":{"line":239,"column":10},"end":{"line":239,"column":23}},"114":{"start":{"line":240,"column":10},"end":{"line":240,"column":16}},"115":{"start":{"line":245,"column":28},"end":{"line":245,"column":57}},"116":{"start":{"line":246,"column":25},"end":{"line":248,"column":11}},"117":{"start":{"line":247,"column":19},"end":{"line":247,"column":42}},"118":{"start":{"line":250,"column":23},"end":{"line":250,"column":37}},"119":{"start":{"line":252,"column":10},"end":{"line":254,"column":11}},"120":{"start":{"line":253,"column":12},"end":{"line":253,"column":42}},"121":{"start":{"line":257,"column":10},"end":{"line":259,"column":11}},"122":{"start":{"line":258,"column":12},"end":{"line":258,"column":32}},"123":{"start":{"line":260,"column":10},"end":{"line":260,"column":16}},"124":{"start":{"line":265,"column":10},"end":{"line":265,"column":30}},"125":{"start":{"line":267,"column":10},"end":{"line":269,"column":11}},"126":{"start":{"line":268,"column":12},"end":{"line":268,"column":36}},"127":{"start":{"line":277,"column":22},"end":{"line":285,"column":3}},"128":{"start":{"line":288,"column":38},"end":{"line":295,"column":3}},"129":{"start":{"line":298,"column":30},"end":{"line":300,"column":30}},"130":{"start":{"line":302,"column":44},"end":{"line":310,"column":4}},"131":{"start":{"line":303,"column":4},"end":{"line":309,"column":6}},"132":{"start":{"line":313,"column":2},"end":{"line":323,"column":4}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":19,"column":76},"end":{"line":19,"column":77}},"loc":{"start":{"line":19,"column":92},"end":{"line":324,"column":1}},"line":19},"1":{"name":"(anonymous_1)","decl":{"start":{"line":42,"column":27},"end":{"line":42,"column":28}},"loc":{"start":{"line":42,"column":53},"end":{"line":56,"column":3}},"line":42},"2":{"name":"(anonymous_2)","decl":{"start":{"line":47,"column":39},"end":{"line":47,"column":40}},"loc":{"start":{"line":47,"column":56},"end":{"line":47,"column":94}},"line":47},"3":{"name":"(anonymous_3)","decl":{"start":{"line":50,"column":43},"end":{"line":50,"column":44}},"loc":{"start":{"line":50,"column":50},"end":{"line":50,"column":58}},"line":50},"4":{"name":"(anonymous_4)","decl":{"start":{"line":52,"column":21},"end":{"line":52,"column":22}},"loc":{"start":{"line":52,"column":29},"end":{"line":52,"column":79}},"line":52},"5":{"name":"(anonymous_5)","decl":{"start":{"line":59,"column":18},"end":{"line":59,"column":19}},"loc":{"start":{"line":59,"column":24},"end":{"line":68,"column":3}},"line":59},"6":{"name":"(anonymous_6)","decl":{"start":{"line":61,"column":26},"end":{"line":61,"column":27}},"loc":{"start":{"line":61,"column":40},"end":{"line":66,"column":7}},"line":61},"7":{"name":"(anonymous_7)","decl":{"start":{"line":62,"column":43},"end":{"line":62,"column":44}},"loc":{"start":{"line":62,"column":50},"end":{"line":62,"column":74}},"line":62},"8":{"name":"(anonymous_8)","decl":{"start":{"line":64,"column":25},"end":{"line":64,"column":26}},"loc":{"start":{"line":64,"column":33},"end":{"line":64,"column":71}},"line":64},"9":{"name":"(anonymous_9)","decl":{"start":{"line":64,"column":45},"end":{"line":64,"column":46}},"loc":{"start":{"line":64,"column":52},"end":{"line":64,"column":70}},"line":64},"10":{"name":"(anonymous_10)","decl":{"start":{"line":71,"column":35},"end":{"line":71,"column":36}},"loc":{"start":{"line":71,"column":41},"end":{"line":71,"column":61}},"line":71},"11":{"name":"(anonymous_11)","decl":{"start":{"line":72,"column":39},"end":{"line":72,"column":40}},"loc":{"start":{"line":72,"column":45},"end":{"line":72,"column":69}},"line":72},"12":{"name":"(anonymous_12)","decl":{"start":{"line":77,"column":18},"end":{"line":77,"column":19}},"loc":{"start":{"line":77,"column":24},"end":{"line":92,"column":3}},"line":77},"13":{"name":"(anonymous_13)","decl":{"start":{"line":83,"column":10},"end":{"line":83,"column":11}},"loc":{"start":{"line":83,"column":27},"end":{"line":83,"column":70}},"line":83},"14":{"name":"(anonymous_14)","decl":{"start":{"line":95,"column":21},"end":{"line":95,"column":22}},"loc":{"start":{"line":95,"column":62},"end":{"line":101,"column":3}},"line":95},"15":{"name":"(anonymous_15)","decl":{"start":{"line":103,"column":23},"end":{"line":103,"column":24}},"loc":{"start":{"line":103,"column":64},"end":{"line":109,"column":3}},"line":103},"16":{"name":"(anonymous_16)","decl":{"start":{"line":111,"column":22},"end":{"line":111,"column":23}},"loc":{"start":{"line":111,"column":49},"end":{"line":119,"column":3}},"line":111},"17":{"name":"(anonymous_17)","decl":{"start":{"line":115,"column":51},"end":{"line":115,"column":52}},"loc":{"start":{"line":115,"column":61},"end":{"line":115,"column":89}},"line":115},"18":{"name":"(anonymous_18)","decl":{"start":{"line":130,"column":38},"end":{"line":130,"column":39}},"loc":{"start":{"line":130,"column":44},"end":{"line":156,"column":3}},"line":130},"19":{"name":"(anonymous_19)","decl":{"start":{"line":159,"column":26},"end":{"line":159,"column":27}},"loc":{"start":{"line":159,"column":73},"end":{"line":176,"column":3}},"line":159},"20":{"name":"(anonymous_20)","decl":{"start":{"line":162,"column":55},"end":{"line":162,"column":56}},"loc":{"start":{"line":162,"column":62},"end":{"line":162,"column":91}},"line":162},"21":{"name":"(anonymous_21)","decl":{"start":{"line":178,"column":21},"end":{"line":178,"column":22}},"loc":{"start":{"line":178,"column":27},"end":{"line":183,"column":3}},"line":178},"22":{"name":"(anonymous_22)","decl":{"start":{"line":185,"column":21},"end":{"line":185,"column":22}},"loc":{"start":{"line":185,"column":27},"end":{"line":191,"column":3}},"line":185},"23":{"name":"(anonymous_23)","decl":{"start":{"line":193,"column":33},"end":{"line":193,"column":34}},"loc":{"start":{"line":193,"column":40},"end":{"line":274,"column":3}},"line":193},"24":{"name":"(anonymous_24)","decl":{"start":{"line":195,"column":15},"end":{"line":195,"column":16}},"loc":{"start":{"line":195,"column":24},"end":{"line":272,"column":5}},"line":195},"25":{"name":"(anonymous_25)","decl":{"start":{"line":247,"column":12},"end":{"line":247,"column":13}},"loc":{"start":{"line":247,"column":19},"end":{"line":247,"column":42}},"line":247},"26":{"name":"(anonymous_26)","decl":{"start":{"line":273,"column":13},"end":{"line":273,"column":14}},"loc":{"start":{"line":273,"column":19},"end":{"line":273,"column":21}},"line":273},"27":{"name":"(anonymous_27)","decl":{"start":{"line":302,"column":68},"end":{"line":302,"column":69}},"loc":{"start":{"line":303,"column":4},"end":{"line":309,"column":6}},"line":303}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},"type":"if","locations":[{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},{"start":{"line":43,"column":4},"end":{"line":45,"column":5}}],"line":43},"1":{"loc":{"start":{"line":48,"column":4},"end":{"line":55,"column":5}},"type":"if","locations":[{"start":{"line":48,"column":4},"end":{"line":55,"column":5}},{"start":{"line":48,"column":4},"end":{"line":55,"column":5}}],"line":48},"2":{"loc":{"start":{"line":60,"column":4},"end":{"line":67,"column":5}},"type":"if","locations":[{"start":{"line":60,"column":4},"end":{"line":67,"column":5}},{"start":{"line":60,"column":4},"end":{"line":67,"column":5}}],"line":60},"3":{"loc":{"start":{"line":63,"column":8},"end":{"line":65,"column":9}},"type":"if","locations":[{"start":{"line":63,"column":8},"end":{"line":65,"column":9}},{"start":{"line":63,"column":8},"end":{"line":65,"column":9}}],"line":63},"4":{"loc":{"start":{"line":78,"column":4},"end":{"line":91,"column":5}},"type":"if","locations":[{"start":{"line":78,"column":4},"end":{"line":91,"column":5}},{"start":{"line":78,"column":4},"end":{"line":91,"column":5}}],"line":78},"5":{"loc":{"start":{"line":81,"column":6},"end":{"line":89,"column":7}},"type":"if","locations":[{"start":{"line":81,"column":6},"end":{"line":89,"column":7}},{"start":{"line":81,"column":6},"end":{"line":89,"column":7}}],"line":81},"6":{"loc":{"start":{"line":81,"column":10},"end":{"line":81,"column":41}},"type":"binary-expr","locations":[{"start":{"line":81,"column":10},"end":{"line":81,"column":19}},{"start":{"line":81,"column":23},"end":{"line":81,"column":41}}],"line":81},"7":{"loc":{"start":{"line":86,"column":8},"end":{"line":88,"column":9}},"type":"if","locations":[{"start":{"line":86,"column":8},"end":{"line":88,"column":9}},{"start":{"line":86,"column":8},"end":{"line":88,"column":9}}],"line":86},"8":{"loc":{"start":{"line":106,"column":4},"end":{"line":108,"column":5}},"type":"if","locations":[{"start":{"line":106,"column":4},"end":{"line":108,"column":5}},{"start":{"line":106,"column":4},"end":{"line":108,"column":5}}],"line":106},"9":{"loc":{"start":{"line":106,"column":8},"end":{"line":106,"column":76}},"type":"binary-expr","locations":[{"start":{"line":106,"column":8},"end":{"line":106,"column":17}},{"start":{"line":106,"column":22},"end":{"line":106,"column":26}},{"start":{"line":106,"column":31},"end":{"line":106,"column":45}},{"start":{"line":106,"column":49},"end":{"line":106,"column":74}}],"line":106},"10":{"loc":{"start":{"line":115,"column":24},"end":{"line":115,"column":44}},"type":"binary-expr","locations":[{"start":{"line":115,"column":24},"end":{"line":115,"column":38}},{"start":{"line":115,"column":42},"end":{"line":115,"column":44}}],"line":115},"11":{"loc":{"start":{"line":131,"column":4},"end":{"line":137,"column":5}},"type":"if","locations":[{"start":{"line":131,"column":4},"end":{"line":137,"column":5}},{"start":{"line":131,"column":4},"end":{"line":137,"column":5}}],"line":131},"12":{"loc":{"start":{"line":146,"column":6},"end":{"line":152,"column":7}},"type":"if","locations":[{"start":{"line":146,"column":6},"end":{"line":152,"column":7}},{"start":{"line":146,"column":6},"end":{"line":152,"column":7}}],"line":146},"13":{"loc":{"start":{"line":161,"column":27},"end":{"line":161,"column":74}},"type":"binary-expr","locations":[{"start":{"line":161,"column":27},"end":{"line":161,"column":68}},{"start":{"line":161,"column":72},"end":{"line":161,"column":74}}],"line":161},"14":{"loc":{"start":{"line":170,"column":6},"end":{"line":172,"column":7}},"type":"if","locations":[{"start":{"line":170,"column":6},"end":{"line":172,"column":7}},{"start":{"line":170,"column":6},"end":{"line":172,"column":7}}],"line":170},"15":{"loc":{"start":{"line":179,"column":4},"end":{"line":181,"column":5}},"type":"if","locations":[{"start":{"line":179,"column":4},"end":{"line":181,"column":5}},{"start":{"line":179,"column":4},"end":{"line":181,"column":5}}],"line":179},"16":{"loc":{"start":{"line":188,"column":4},"end":{"line":190,"column":5}},"type":"if","locations":[{"start":{"line":188,"column":4},"end":{"line":190,"column":5}},{"start":{"line":188,"column":4},"end":{"line":190,"column":5}}],"line":188},"17":{"loc":{"start":{"line":198,"column":6},"end":{"line":271,"column":7}},"type":"switch","locations":[{"start":{"line":200,"column":8},"end":{"line":200,"column":24}},{"start":{"line":201,"column":8},"end":{"line":218,"column":9}},{"start":{"line":220,"column":8},"end":{"line":227,"column":9}},{"start":{"line":229,"column":8},"end":{"line":236,"column":9}},{"start":{"line":238,"column":8},"end":{"line":241,"column":9}},{"start":{"line":244,"column":8},"end":{"line":261,"column":9}},{"start":{"line":264,"column":8},"end":{"line":270,"column":9}}],"line":198},"18":{"loc":{"start":{"line":203,"column":10},"end":{"line":207,"column":11}},"type":"if","locations":[{"start":{"line":203,"column":10},"end":{"line":207,"column":11}},{"start":{"line":203,"column":10},"end":{"line":207,"column":11}}],"line":203},"19":{"loc":{"start":{"line":205,"column":17},"end":{"line":207,"column":11}},"type":"if","locations":[{"start":{"line":205,"column":17},"end":{"line":207,"column":11}},{"start":{"line":205,"column":17},"end":{"line":207,"column":11}}],"line":205},"20":{"loc":{"start":{"line":209,"column":10},"end":{"line":215,"column":11}},"type":"if","locations":[{"start":{"line":209,"column":10},"end":{"line":215,"column":11}},{"start":{"line":209,"column":10},"end":{"line":215,"column":11}}],"line":209},"21":{"loc":{"start":{"line":212,"column":12},"end":{"line":214,"column":13}},"type":"if","locations":[{"start":{"line":212,"column":12},"end":{"line":214,"column":13}},{"start":{"line":212,"column":12},"end":{"line":214,"column":13}}],"line":212},"22":{"loc":{"start":{"line":221,"column":10},"end":{"line":225,"column":11}},"type":"if","locations":[{"start":{"line":221,"column":10},"end":{"line":225,"column":11}},{"start":{"line":221,"column":10},"end":{"line":225,"column":11}}],"line":221},"23":{"loc":{"start":{"line":230,"column":10},"end":{"line":234,"column":11}},"type":"if","locations":[{"start":{"line":230,"column":10},"end":{"line":234,"column":11}},{"start":{"line":230,"column":10},"end":{"line":234,"column":11}}],"line":230},"24":{"loc":{"start":{"line":252,"column":10},"end":{"line":254,"column":11}},"type":"if","locations":[{"start":{"line":252,"column":10},"end":{"line":254,"column":11}},{"start":{"line":252,"column":10},"end":{"line":254,"column":11}}],"line":252},"25":{"loc":{"start":{"line":252,"column":14},"end":{"line":252,"column":48}},"type":"binary-expr","locations":[{"start":{"line":252,"column":14},"end":{"line":252,"column":22}},{"start":{"line":252,"column":26},"end":{"line":252,"column":40}},{"start":{"line":252,"column":44},"end":{"line":252,"column":48}}],"line":252},"26":{"loc":{"start":{"line":257,"column":10},"end":{"line":259,"column":11}},"type":"if","locations":[{"start":{"line":257,"column":10},"end":{"line":259,"column":11}},{"start":{"line":257,"column":10},"end":{"line":259,"column":11}}],"line":257},"27":{"loc":{"start":{"line":267,"column":10},"end":{"line":269,"column":11}},"type":"if","locations":[{"start":{"line":267,"column":10},"end":{"line":269,"column":11}},{"start":{"line":267,"column":10},"end":{"line":269,"column":11}}],"line":267},"28":{"loc":{"start":{"line":298,"column":30},"end":{"line":300,"column":30}},"type":"cond-expr","locations":[{"start":{"line":299,"column":6},"end":{"line":299,"column":19}},{"start":{"line":300,"column":6},"end":{"line":300,"column":30}}],"line":298}},"s":{"0":4,"1":136,"2":136,"3":136,"4":136,"5":136,"6":136,"7":22,"8":21,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":136,"19":40,"20":1,"21":1,"22":1,"23":1,"24":0,"25":0,"26":0,"27":136,"28":39,"29":136,"30":39,"31":136,"32":136,"33":57,"34":40,"35":40,"36":11,"37":35,"38":11,"39":10,"40":40,"41":136,"42":22,"43":22,"44":22,"45":136,"46":7,"47":7,"48":6,"49":136,"50":157,"51":157,"52":157,"53":90,"54":103,"55":157,"56":136,"57":136,"58":105,"59":6,"60":99,"61":99,"62":99,"63":157,"64":157,"65":147,"66":10,"67":99,"68":136,"69":7,"70":7,"71":7,"72":12,"73":7,"74":7,"75":7,"76":7,"77":7,"78":7,"79":7,"80":0,"81":136,"82":4,"83":2,"84":4,"85":136,"86":5,"87":5,"88":5,"89":5,"90":136,"91":136,"92":17,"93":17,"94":2,"95":2,"96":0,"97":2,"98":2,"99":2,"100":2,"101":2,"102":2,"103":2,"104":2,"105":3,"106":0,"107":3,"108":3,"109":5,"110":0,"111":5,"112":5,"113":1,"114":1,"115":1,"116":1,"117":1,"118":1,"119":1,"120":1,"121":1,"122":0,"123":1,"124":4,"125":4,"126":4,"127":136,"128":136,"129":136,"130":136,"131":217,"132":136},"f":{"0":136,"1":22,"2":1,"3":1,"4":1,"5":40,"6":1,"7":1,"8":0,"9":0,"10":39,"11":39,"12":57,"13":35,"14":22,"15":7,"16":157,"17":103,"18":105,"19":7,"20":12,"21":4,"22":5,"23":136,"24":17,"25":1,"26":0,"27":217},"b":{"0":[21,1],"1":[1,0],"2":[1,39],"3":[0,1],"4":[40,17],"5":[11,29],"6":[40,40],"7":[10,1],"8":[6,1],"9":[7,7,2,2],"10":[90,0],"11":[6,99],"12":[147,10],"13":[7,0],"14":[7,0],"15":[2,2],"16":[5,0],"17":[0,2,3,5,1,1,4],"18":[0,2],"19":[2,0],"20":[2,0],"21":[2,0],"22":[0,3],"23":[0,5],"24":[1,0],"25":[1,1,1],"26":[0,1],"27":[4,0],"28":[131,5]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"a24d58c779c7494f0d0efa20f10a71a5b3ff944c"} +,"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchConfig.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchConfig.ts","statementMap":{"0":{"start":{"line":7,"column":2},"end":{"line":25,"column":19}},"1":{"start":{"line":8,"column":4},"end":{"line":10,"column":5}},"2":{"start":{"line":9,"column":6},"end":{"line":9,"column":25}},"3":{"start":{"line":12,"column":39},"end":{"line":15,"column":5}},"4":{"start":{"line":17,"column":4},"end":{"line":22,"column":5}},"5":{"start":{"line":18,"column":6},"end":{"line":21,"column":8}},"6":{"start":{"line":24,"column":4},"end":{"line":24,"column":32}}},"fnMap":{"0":{"name":"useSearchConfig","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":39}},"loc":{"start":{"line":6,"column":82},"end":{"line":26,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":50},"end":{"line":7,"column":51}},"loc":{"start":{"line":7,"column":56},"end":{"line":25,"column":3}},"line":7}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":4},"end":{"line":10,"column":5}},"type":"if","locations":[{"start":{"line":8,"column":4},"end":{"line":10,"column":5}},{"start":{"line":8,"column":4},"end":{"line":10,"column":5}}],"line":8},"1":{"loc":{"start":{"line":17,"column":4},"end":{"line":22,"column":5}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":22,"column":5}},{"start":{"line":17,"column":4},"end":{"line":22,"column":5}}],"line":17},"2":{"loc":{"start":{"line":17,"column":8},"end":{"line":17,"column":52}},"type":"binary-expr","locations":[{"start":{"line":17,"column":8},"end":{"line":17,"column":18}},{"start":{"line":17,"column":22},"end":{"line":17,"column":52}}],"line":17}},"s":{"0":51,"1":35,"2":30,"3":5,"4":5,"5":3,"6":5},"f":{"0":51,"1":35},"b":{"0":[30,5],"1":[3,2],"2":[5,5]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"b7f498d195a1a4fd1389a8a3c1eeb82cc55a66ef"} +,"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchResult.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchResult.ts","statementMap":{"0":{"start":{"line":6,"column":48},"end":{"line":7,"column":86}},"1":{"start":{"line":7,"column":2},"end":{"line":7,"column":86}},"2":{"start":{"line":7,"column":22},"end":{"line":7,"column":85}},"3":{"start":{"line":9,"column":48},"end":{"line":10,"column":52}},"4":{"start":{"line":10,"column":2},"end":{"line":10,"column":52}},"5":{"start":{"line":10,"column":18},"end":{"line":10,"column":39}},"6":{"start":{"line":23,"column":4},"end":{"line":23,"column":16}},"7":{"start":{"line":26,"column":23},"end":{"line":26,"column":59}},"8":{"start":{"line":27,"column":23},"end":{"line":27,"column":59}},"9":{"start":{"line":31,"column":29},"end":{"line":74,"column":91}},"10":{"start":{"line":32,"column":4},"end":{"line":34,"column":5}},"11":{"start":{"line":33,"column":6},"end":{"line":33,"column":16}},"12":{"start":{"line":37,"column":24},"end":{"line":44,"column":6}},"13":{"start":{"line":38,"column":26},"end":{"line":38,"column":75}},"14":{"start":{"line":39,"column":31},"end":{"line":39,"column":59}},"15":{"start":{"line":39,"column":50},"end":{"line":39,"column":58}},"16":{"start":{"line":40,"column":6},"end":{"line":43,"column":8}},"17":{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},"18":{"start":{"line":48,"column":6},"end":{"line":48,"column":80}},"19":{"start":{"line":48,"column":59},"end":{"line":48,"column":78}},"20":{"start":{"line":52,"column":47},"end":{"line":52,"column":49}},"21":{"start":{"line":53,"column":4},"end":{"line":64,"column":5}},"22":{"start":{"line":53,"column":17},"end":{"line":53,"column":18}},"23":{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},"24":{"start":{"line":56,"column":8},"end":{"line":56,"column":14}},"25":{"start":{"line":60,"column":21},"end":{"line":60,"column":37}},"26":{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},"27":{"start":{"line":62,"column":8},"end":{"line":62,"column":34}},"28":{"start":{"line":67,"column":4},"end":{"line":71,"column":5}},"29":{"start":{"line":68,"column":6},"end":{"line":70,"column":9}},"30":{"start":{"line":69,"column":8},"end":{"line":69,"column":98}},"31":{"start":{"line":73,"column":4},"end":{"line":73,"column":24}},"32":{"start":{"line":78,"column":18},"end":{"line":86,"column":76}},"33":{"start":{"line":79,"column":4},"end":{"line":85,"column":7}},"34":{"start":{"line":80,"column":20},"end":{"line":80,"column":86}},"35":{"start":{"line":81,"column":6},"end":{"line":84,"column":8}},"36":{"start":{"line":88,"column":2},"end":{"line":88,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":48},"end":{"line":6,"column":49}},"loc":{"start":{"line":7,"column":2},"end":{"line":7,"column":86}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":15},"end":{"line":7,"column":16}},"loc":{"start":{"line":7,"column":22},"end":{"line":7,"column":85}},"line":7},"2":{"name":"(anonymous_2)","decl":{"start":{"line":9,"column":48},"end":{"line":9,"column":49}},"loc":{"start":{"line":10,"column":2},"end":{"line":10,"column":52}},"line":10},"3":{"name":"(anonymous_3)","decl":{"start":{"line":10,"column":11},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":18},"end":{"line":10,"column":39}},"line":10},"4":{"name":"useSearchResult","decl":{"start":{"line":21,"column":24},"end":{"line":21,"column":39}},"loc":{"start":{"line":21,"column":74},"end":{"line":89,"column":1}},"line":21},"5":{"name":"(anonymous_5)","decl":{"start":{"line":31,"column":43},"end":{"line":31,"column":44}},"loc":{"start":{"line":31,"column":49},"end":{"line":74,"column":3}},"line":31},"6":{"name":"(anonymous_6)","decl":{"start":{"line":37,"column":43},"end":{"line":37,"column":44}},"loc":{"start":{"line":37,"column":53},"end":{"line":44,"column":5}},"line":37},"7":{"name":"(anonymous_7)","decl":{"start":{"line":39,"column":43},"end":{"line":39,"column":44}},"loc":{"start":{"line":39,"column":50},"end":{"line":39,"column":58}},"line":39},"8":{"name":"(anonymous_8)","decl":{"start":{"line":48,"column":43},"end":{"line":48,"column":44}},"loc":{"start":{"line":48,"column":59},"end":{"line":48,"column":78}},"line":48},"9":{"name":"(anonymous_9)","decl":{"start":{"line":68,"column":24},"end":{"line":68,"column":25}},"loc":{"start":{"line":68,"column":34},"end":{"line":70,"column":7}},"line":68},"10":{"name":"(anonymous_10)","decl":{"start":{"line":78,"column":32},"end":{"line":78,"column":33}},"loc":{"start":{"line":78,"column":38},"end":{"line":86,"column":3}},"line":78},"11":{"name":"(anonymous_11)","decl":{"start":{"line":79,"column":34},"end":{"line":79,"column":35}},"loc":{"start":{"line":79,"column":68},"end":{"line":85,"column":5}},"line":79}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":23},"end":{"line":26,"column":59}},"type":"binary-expr","locations":[{"start":{"line":26,"column":23},"end":{"line":26,"column":42}},{"start":{"line":26,"column":46},"end":{"line":26,"column":59}}],"line":26},"1":{"loc":{"start":{"line":27,"column":23},"end":{"line":27,"column":59}},"type":"binary-expr","locations":[{"start":{"line":27,"column":23},"end":{"line":27,"column":42}},{"start":{"line":27,"column":46},"end":{"line":27,"column":59}}],"line":27},"2":{"loc":{"start":{"line":32,"column":4},"end":{"line":34,"column":5}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":34,"column":5}},{"start":{"line":32,"column":4},"end":{"line":34,"column":5}}],"line":32},"3":{"loc":{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},{"start":{"line":47,"column":4},"end":{"line":49,"column":5}}],"line":47},"4":{"loc":{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},"type":"if","locations":[{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},{"start":{"line":55,"column":6},"end":{"line":57,"column":7}}],"line":55},"5":{"loc":{"start":{"line":55,"column":10},"end":{"line":55,"column":73}},"type":"binary-expr","locations":[{"start":{"line":55,"column":10},"end":{"line":55,"column":28}},{"start":{"line":55,"column":32},"end":{"line":55,"column":73}}],"line":55},"6":{"loc":{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},"type":"if","locations":[{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},{"start":{"line":61,"column":6},"end":{"line":63,"column":7}}],"line":61},"7":{"loc":{"start":{"line":67,"column":4},"end":{"line":71,"column":5}},"type":"if","locations":[{"start":{"line":67,"column":4},"end":{"line":71,"column":5}},{"start":{"line":67,"column":4},"end":{"line":71,"column":5}}],"line":67}},"s":{"0":4,"1":19,"2":38,"3":4,"4":10,"5":29,"6":136,"7":136,"8":136,"9":136,"10":39,"11":33,"12":6,"13":30,"14":30,"15":60,"16":30,"17":6,"18":5,"19":25,"20":6,"21":6,"22":6,"23":20,"24":1,"25":19,"26":19,"27":12,"28":6,"29":1,"30":1,"31":6,"32":136,"33":39,"34":12,"35":12,"36":136},"f":{"0":19,"1":38,"2":10,"3":29,"4":136,"5":39,"6":30,"7":60,"8":25,"9":1,"10":39,"11":12},"b":{"0":[136,136],"1":[136,133],"2":[33,6],"3":[5,1],"4":[1,19],"5":[20,17],"6":[12,7],"7":[1,5]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"c8946832dc9d134c9c7486264bfdd797ebbfd0a4"} +,"/Users/jilin/projects/antd/rc-cascader/src/hooks/useUpdateEffect.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/hooks/useUpdateEffect.ts","statementMap":{"0":{"start":{"line":7,"column":21},"end":{"line":7,"column":39}},"1":{"start":{"line":9,"column":2},"end":{"line":17,"column":16}},"2":{"start":{"line":10,"column":4},"end":{"line":13,"column":5}},"3":{"start":{"line":11,"column":6},"end":{"line":11,"column":33}},"4":{"start":{"line":12,"column":6},"end":{"line":12,"column":13}},"5":{"start":{"line":16,"column":4},"end":{"line":16,"column":21}}},"fnMap":{"0":{"name":"useUpdateEffect","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":39}},"loc":{"start":{"line":6,"column":80},"end":{"line":18,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":9,"column":18},"end":{"line":9,"column":19}},"loc":{"start":{"line":9,"column":24},"end":{"line":17,"column":3}},"line":9}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":4},"end":{"line":13,"column":5}},"type":"if","locations":[{"start":{"line":10,"column":4},"end":{"line":13,"column":5}},{"start":{"line":10,"column":4},"end":{"line":13,"column":5}}],"line":10}},"s":{"0":51,"1":51,"2":36,"3":35,"4":35,"5":1},"f":{"0":51,"1":36},"b":{"0":[35,1]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"d54101987d46e9479ee1cea1d2c8df92961e3b0e"} +} diff --git a/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js new file mode 100644 index 00000000..c7ff5a5c --- /dev/null +++ b/coverage/lcov-report/block-navigation.js @@ -0,0 +1,79 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/lcov-report/index.html b/coverage/lcov-report/index.html new file mode 100644 index 00000000..aa627ca0 --- /dev/null +++ b/coverage/lcov-report/index.html @@ -0,0 +1,140 @@ + + + + + + Code coverage report for All files + + + + + + + + +
      +
      +

      All files

      +
      + +
      + 95.3% + Statements + 284/298 +
      + + +
      + 79.52% + Branches + 132/166 +
      + + +
      + 91.14% + Functions + 72/79 +
      + + +
      + 95.99% + Lines + 263/274 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      FileStatementsBranchesFunctionsLines
      src +
      +
      94.25%82/8779.07%34/4388.89%24/2795.12%78/82
      src/OptionList +
      +
      94.41%152/16174.75%74/9988.89%32/3695.24%140/147
      src/hooks +
      +
      100%50/50100%24/24100%16/16100%45/45
      +
      +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/prettify.js b/coverage/lcov-report/prettify.js new file mode 100644 index 00000000..b3225238 --- /dev/null +++ b/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/lcov-report/sort-arrow-sprite.png b/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..03f704a609c6fd0dbfdac63466a7d7c958b5cbf3 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jii$m5978H@?Fn+^JD|Y9yzj{W`447Gxa{7*dM7nnnD-Lb z6^}Hx2)'; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/coverage/lcov-report/src/Cascader.tsx.html b/coverage/lcov-report/src/Cascader.tsx.html new file mode 100644 index 00000000..8a152bf2 --- /dev/null +++ b/coverage/lcov-report/src/Cascader.tsx.html @@ -0,0 +1,1087 @@ + + + + + + Code coverage report for src/Cascader.tsx + + + + + + + + +
      +
      +

      All files / src Cascader.tsx

      +
      + +
      + 92.42% + Statements + 61/66 +
      + + +
      + 75% + Branches + 24/32 +
      + + +
      + 85.71% + Functions + 18/21 +
      + + +
      + 93.44% + Lines + 57/61 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +51x +  +51x +  +51x +  +  +51x +  +51x +  +  +  +  +  +  +  +  +51x +7x +  +  +51x +  +  +  +  +51x +  +  +51x +35x +  +  +  +  +  +  +  +  +51x +  +  +36x +36x +9x +  +  +36x +  +  +51x +35x +  +  +51x +1x +  +  +  +51x +13x +  +13x +  +  +  +13x +32x +  +  +  +  +51x +  +7x +  +7x +  +7x +7x +  +7x +  +7x +7x +17x +  +17x +7x +  +  +  +7x +6x +  +  +7x +6x +  +  +  +  +6x +  +  +  +  +  +51x +51x +  +  +  +51x +  +  +51x +  +51x +50x +50x +  +  +  +51x +35x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +51x +  +  +  +  +  +  +  +51x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +12x +  +  +  +  +151x +  +  +  +  +  +  +4x +  +  + 
      import * as React from 'react';
      +import warning from 'rc-util/lib/warning';
      +import useMergedState from 'rc-util/lib/hooks/useMergedState';
      +import type { TreeSelectProps } from 'rc-tree-select';
      +import generate from 'rc-tree-select/lib/generate';
      +import type { FlattenDataNode } from 'rc-tree-select/lib/interface';
      +import { fillFieldNames } from 'rc-tree-select/lib/utils/valueUtil';
      +import type { RefSelectProps } from 'rc-select/lib/generate';
      +import OptionList from './OptionList';
      +import type { CascaderValueType, DataNode, FieldNames, ShowSearchType } from './interface';
      +import CascaderContext from './context';
      +import { connectValue, convertOptions, restoreCompatibleValue } from './util';
      +import useUpdateEffect from './hooks/useUpdateEffect';
      +import useSearchConfig from './hooks/useSearchConfig';
      + 
      +/**
      + * `rc-cascader` is much like `rc-tree-select` but API is very different.
      + * It's caused that component developer is not same person
      + * and we do not rice the API naming standard at that time.
      + *
      + * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API.
      + * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version.
      + *
      + * Update:
      + * - dropdown class change to `rc-cascader-dropdown`
      + * - direction rtl keyboard
      + *
      + * Deprecated:
      + * - popupVisible
      + * - onPopupVisibleChange
      + *
      + * Removed:
      + * - builtinPlacements: Handle by select
      + */
      + 
      +const RefCascader = generate({
      +  prefixCls: 'rc-cascader',
      +  optionList: OptionList,
      +});
      + 
      +// ====================================== Wrap ======================================
      +interface BaseCascaderProps
      +  extends Omit<
      +    TreeSelectProps,
      +    | 'value'
      +    | 'defaultValue'
      +    | 'filterTreeNode'
      +    | 'labelInValue'
      +    | 'loadData'
      +    | 'showCheckedStrategy'
      +    | 'showSearch'
      +    | 'treeCheckable'
      +    | 'treeCheckStrictly'
      +    | 'treeDataSimpleMode'
      +    | 'treeNodeFilterProp'
      +    | 'treeNodeLabelProp'
      +    | 'treeDefaultExpandAll'
      +    | 'treeDefaultExpandedKeys'
      +    | 'treeExpandedKeys'
      +    | 'treeIcon'
      +    | 'onChange'
      +  > {
      +  options?: DataNode[];
      +  children?: React.ReactElement;
      + 
      +  // Value
      +  value?: CascaderValueType | CascaderValueType[];
      +  defaultValue?: CascaderValueType | CascaderValueType[];
      +  changeOnSelect?: boolean;
      +  allowClear?: boolean;
      +  disabled?: boolean;
      + 
      +  fieldNames?: FieldNames;
      + 
      +  // Search
      +  showSearch?: boolean | ShowSearchType;
      +  searchValue?: string;
      +  onSearch?: (search: string) => void;
      + 
      +  // Open
      +  /** @deprecated Use `open` instead */
      +  popupVisible?: boolean;
      + 
      +  /** @deprecated Use `onDropdownVisibleChange` instead */
      +  onPopupVisibleChange?: (open: boolean) => void;
      +  onDropdownVisibleChange?: (open: boolean) => void;
      + 
      +  // Trigger
      +  expandTrigger?: 'hover' | 'click';
      + 
      +  dropdownMenuColumnStyle?: React.CSSProperties;
      +  loadData?: (selectOptions: DataNode[]) => void;
      + 
      +  expandIcon?: React.ReactNode;
      +  loadingIcon?: React.ReactNode;
      +}
      + 
      +type OnSingleChange = (value: CascaderValueType, selectOptions: DataNode[]) => void;
      +type OnMultipleChange = (value: CascaderValueType[], selectOptions: DataNode[][]) => void;
      + 
      +interface SingleCascaderProps extends BaseCascaderProps {
      +  multiple?: false;
      + 
      +  onChange?: OnSingleChange;
      +}
      + 
      +interface MultipleCascaderProps extends BaseCascaderProps {
      +  multiple: true;
      + 
      +  onChange?: OnMultipleChange;
      +}
      + 
      +export type CascaderProps = SingleCascaderProps | MultipleCascaderProps;
      + 
      +interface CascaderRef {
      +  focus: () => void;
      +  blur: () => void;
      +}
      + 
      +const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref<CascaderRef>) => {
      +  const {
      +    changeOnSelect,
      +    children,
      +    options,
      +    onChange,
      +    value,
      +    defaultValue,
      + 
      +    popupVisible,
      +    open,
      +    onDropdownVisibleChange,
      +    onPopupVisibleChange,
      + 
      +    searchValue,
      +    onSearch,
      +    showSearch,
      + 
      +    expandTrigger,
      +    expandIcon = '>',
      +    loadingIcon,
      + 
      +    loadData,
      +    dropdownMenuColumnStyle,
      + 
      +    ...restProps
      +  } = props;
      + 
      +  const { multiple, fieldNames } = restProps;
      + 
      +  const mergedFieldNames = React.useMemo(() => fillFieldNames(fieldNames), [fieldNames]);
      + 
      +  // ============================ Ref =============================
      +  const cascaderRef = React.useRef<RefSelectProps>();
      + 
      +  React.useImperativeHandle(ref, () => ({
      +    focus: () => {
      +      cascaderRef.current.focus();
      +    },
      +    blur: () => {
      +      cascaderRef.current.blur();
      +    },
      +  }));
      + 
      +  const getEntityByValue = (val: React.Key): FlattenDataNode =>
      +    (cascaderRef.current as any).getEntityByValue(val);
      + 
      +  // =========================== Search ===========================
      +  const [mergedSearch, setMergedSearch] = useMergedState(undefined, {
      +    value: searchValue,
      +    onChange: onSearch,
      +  });
      + 
      +  const [mergedShowSearch, searchConfig] = useSearchConfig(showSearch);
      + 
      +  // ========================== Options ===========================
      +  const mergedOptions = React.useMemo(() => {
      +    return convertOptions(options, mergedFieldNames);
      +  }, [options, mergedFieldNames]);
      + 
      +  // =========================== Value ============================
      +  /**
      +   * Always pass props value to last value unit:
      +   * - single: ['light', 'little'] => ['light__little']
      +   * - multiple: [['light', 'little'], ['bamboo']] => ['light__little', 'bamboo']
      +   */
      +  const parseToInternalValue = (
      +    propValue?: CascaderValueType | CascaderValueType[],
      +  ): React.Key[] => {
      +    let propValueList: CascaderValueType[] = [];
      +    if (propValue) {
      +      propValueList = (multiple ? propValue : [propValue]) as CascaderValueType[];
      +    }
      + 
      +    return propValueList.map(connectValue);
      +  };
      + 
      +  const [internalValue, setInternalValue] = React.useState(() =>
      +    parseToInternalValue(value || defaultValue),
      +  );
      + 
      +  useUpdateEffect(() => {
      +    setInternalValue(parseToInternalValue(value));
      +  }, [value]);
      + 
      +  // =========================== Label ============================
      +  const labelRender = (entity: FlattenDataNode) => {
      +    const { label: fieldLabel } = mergedFieldNames;
      + 
      +    Iif (multiple) {
      +      return entity.data.node[fieldLabel];
      +    }
      + 
      +    return restoreCompatibleValue(entity, mergedFieldNames)
      +      .options.map(opt => opt[fieldLabel])
      +      .join('>');
      +  };
      + 
      +  // =========================== Change ===========================
      +  const onInternalChange = (newValue: any /** Not care current type */) => {
      +    // TODO: Need improve motion experience
      +    setMergedSearch('');
      + 
      +    const valueList = (multiple ? newValue : [newValue]) as React.Key[];
      + 
      +    const pathList: CascaderValueType[] = [];
      +    const optionsList: DataNode[][] = [];
      + 
      +    const valueEntities = valueList.map(getEntityByValue).filter(entity => entity);
      + 
      +    valueEntities.forEach(entity => {
      +      const { options: valueOptions } = restoreCompatibleValue(entity, mergedFieldNames);
      +      const originOptions = valueOptions.map(option => option.node);
      + 
      +      pathList.push(originOptions.map(opt => opt[mergedFieldNames.value]));
      +      optionsList.push(originOptions);
      +    });
      + 
      +    // Fill state
      +    if (value === undefined) {
      +      setInternalValue(valueList);
      +    }
      + 
      +    if (onChange) {
      +      Iif (multiple) {
      +        (onChange as OnMultipleChange)(pathList, optionsList);
      +      } else {
      +        // TODO: This should return null as other component.
      +        // But its a breaking change and we should keep the logic.
      +        (onChange as OnSingleChange)(pathList[0] || [], optionsList[0] || []);
      +      }
      +    }
      +  };
      + 
      +  // ============================ Open ============================
      +  Eif (process.env.NODE_ENV !== 'production') {
      +    warning(
      +      !onPopupVisibleChange,
      +      '`onPopupVisibleChange` is deprecated. Please use `onDropdownVisibleChange` instead.',
      +    );
      +    warning(popupVisible === undefined, '`popupVisible` is deprecated. Please use `open` instead.');
      +  }
      + 
      +  const mergedOpen = open !== undefined ? open : popupVisible;
      + 
      +  const onInternalDropdownVisibleChange = (nextVisible: boolean) => {
      +    onDropdownVisibleChange?.(nextVisible);
      +    onPopupVisibleChange?.(nextVisible);
      +  };
      + 
      +  // ========================== Context ===========================
      +  const context = React.useMemo(
      +    () => ({
      +      changeOnSelect,
      +      expandTrigger,
      +      fieldNames: mergedFieldNames,
      +      expandIcon,
      +      loadingIcon,
      +      loadData,
      +      dropdownMenuColumnStyle,
      +      search: searchConfig,
      +    }),
      +    [
      +      changeOnSelect,
      +      expandTrigger,
      +      mergedFieldNames,
      +      expandIcon,
      +      loadingIcon,
      +      loadData,
      +      dropdownMenuColumnStyle,
      +      searchConfig,
      +    ],
      +  );
      + 
      +  // =========================== Render ===========================
      +  const dropdownStyle: React.CSSProperties =
      +    // Search to match width
      +    (mergedSearch && searchConfig.matchInputWidth) ||
      +    // Empty keep the width
      +    !mergedOptions.length
      +      ? {}
      +      : {
      +          minWidth: 'auto',
      +        };
      + 
      +  return (
      +    <CascaderContext.Provider value={context}>
      +      <RefCascader
      +        ref={cascaderRef}
      +        {...restProps}
      +        value={multiple ? internalValue : internalValue[0]}
      +        dropdownMatchSelectWidth={false}
      +        dropdownStyle={dropdownStyle}
      +        treeData={mergedOptions}
      +        treeCheckable={multiple}
      +        treeNodeFilterProp="label"
      +        onChange={onInternalChange}
      +        showCheckedStrategy={RefCascader.SHOW_PARENT}
      +        open={mergedOpen}
      +        onDropdownVisibleChange={onInternalDropdownVisibleChange}
      +        searchValue={mergedSearch}
      +        // Customize filter logic in OptionList
      +        filterTreeNode={() => true}
      +        showSearch={mergedShowSearch}
      +        onSearch={setMergedSearch}
      +        labelRender={labelRender}
      +        {...{
      +          getRawInputElement: () => children,
      +        }}
      +      />
      +    </CascaderContext.Provider>
      +  );
      +});
      + 
      +Cascader.displayName = 'Cascader';
      + 
      +export default Cascader;
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/OptionList/Column.tsx.html b/coverage/lcov-report/src/OptionList/Column.tsx.html new file mode 100644 index 00000000..82fadb43 --- /dev/null +++ b/coverage/lcov-report/src/OptionList/Column.tsx.html @@ -0,0 +1,490 @@ + + + + + + Code coverage report for src/OptionList/Column.tsx + + + + + + + + +
      +
      +

      All files / src/OptionList Column.tsx

      +
      + +
      + 96.43% + Statements + 27/28 +
      + + +
      + 72.73% + Branches + 24/33 +
      + + +
      + 87.5% + Functions + 7/8 +
      + + +
      + 96.43% + Lines + 27/28 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +217x +217x +217x +  +  +217x +  +  +217x +  +  +493x +493x +  +493x +  +  +493x +  +  +493x +19x +15x +  +  +  +  +493x +15x +6x +  +  +  +  +  +493x +4x +489x +489x +  +  +  +493x +  +  +  +  +  +  +  +  +  +  +  +  +4x +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +15x +15x +15x +  +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
      import * as React from 'react';
      +import classNames from 'classnames';
      +import type { OptionDataNode } from '../interface';
      +import { isLeaf } from '../util';
      +import CascaderContext from '../context';
      + 
      +export interface ColumnProps {
      +  prefixCls: string;
      +  index: number;
      +  multiple?: boolean;
      +  options: OptionDataNode[];
      +  /** Current Column opened item key */
      +  openKey?: React.Key;
      +  onSelect: (value: React.Key, isLeaf: boolean) => void;
      +  onOpen: (index: number, value: React.Key) => void;
      +  onToggleOpen: (open: boolean) => void;
      +  checkedSet: Set<React.Key>;
      +  halfCheckedSet: Set<React.Key>;
      +  loadingKeys: React.Key[];
      +}
      + 
      +export default function Column({
      +  prefixCls,
      +  index,
      +  multiple,
      +  options,
      +  openKey,
      +  onSelect,
      +  onOpen,
      +  onToggleOpen,
      +  checkedSet,
      +  halfCheckedSet,
      +  loadingKeys,
      +}: ColumnProps) {
      +  const menuPrefixCls = `${prefixCls}-menu`;
      +  const menuItemPrefixCls = `${prefixCls}-menu-item`;
      +  const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`;
      + 
      +  const { changeOnSelect, expandTrigger, expandIcon, loadingIcon, dropdownMenuColumnStyle } =
      +    React.useContext(CascaderContext);
      + 
      +  // ============================ Render ============================
      +  return (
      +    <ul className={menuPrefixCls} role="menu">
      +      {options.map(option => {
      +        const { disabled, value, node } = option;
      +        const isMergedLeaf = isLeaf(option);
      + 
      +        const isLoading = loadingKeys.includes(value);
      + 
      +        // >>>>> checked
      +        const checked = checkedSet.has(value);
      + 
      +        // >>>>> Open
      +        const triggerOpenPath = () => {
      +          if (!disabled && !isMergedLeaf) {
      +            onOpen(index, value);
      +          }
      +        };
      + 
      +        // >>>>> Selection
      +        const triggerSelect = () => {
      +          if (!disabled && (isMergedLeaf || changeOnSelect || multiple)) {
      +            onSelect(value, isMergedLeaf);
      +          }
      +        };
      + 
      +        // >>>>> Title
      +        let title: string;
      +        if (typeof node?.title === 'string') {
      +          title = node.title;
      +        } else Eif (typeof option.title === 'string') {
      +          title = option.title;
      +        }
      + 
      +        // >>>>> Render
      +        return (
      +          <li
      +            key={value}
      +            className={classNames(menuItemPrefixCls, {
      +              [`${menuItemPrefixCls}-active`]: openKey === value,
      +              // [`${menuItemPrefixCls}-selected`]: !multiple && checked,
      +              [`${menuItemPrefixCls}-disabled`]: disabled,
      +            })}
      +            style={dropdownMenuColumnStyle}
      +            role="menuitemcheckbox"
      +            title={title}
      +            aria-checked={checked}
      +            onMouseEnter={() => {
      +              Eif (expandTrigger) {
      +                triggerOpenPath();
      +              }
      +            }}
      +          >
      +            {multiple && (
      +              <span
      +                className={classNames(
      +                  checkboxPrefixCls,
      +                  {
      +                    [`${checkboxPrefixCls}-checked`]: checked,
      +                    [`${checkboxPrefixCls}-indeterminate`]: !checked && halfCheckedSet.has(value),
      +                  },
      +                  // (disabled || disableCheckbox) && `${prefixCls}-checkbox-disabled`,
      +                )}
      +                onClick={() => {
      +                  triggerSelect();
      +                }}
      +              />
      +            )}
      +            <div
      +              className={`${menuItemPrefixCls}-content`}
      +              onClick={() => {
      +                triggerOpenPath();
      +                Eif (!multiple || isMergedLeaf) {
      +                  triggerSelect();
      +                }
      +              }}
      +              onDoubleClick={() => {
      +                Eif (changeOnSelect) {
      +                  onToggleOpen(false);
      +                }
      +              }}
      +            >
      +              {option.title}
      +            </div>
      +            {!isLoading && expandIcon && !isMergedLeaf && (
      +              <div className={`${menuItemPrefixCls}-expand-icon`}>{expandIcon}</div>
      +            )}
      +            {isLoading && loadingIcon && (
      +              <div className={`${menuItemPrefixCls}-loading`}>{loadingIcon}</div>
      +            )}
      +          </li>
      +        );
      +      })}
      +    </ul>
      +  );
      +}
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/OptionList/index.html b/coverage/lcov-report/src/OptionList/index.html new file mode 100644 index 00000000..338d319b --- /dev/null +++ b/coverage/lcov-report/src/OptionList/index.html @@ -0,0 +1,125 @@ + + + + + + Code coverage report for src/OptionList + + + + + + + + +
      +
      +

      All files src/OptionList

      +
      + +
      + 94.41% + Statements + 152/161 +
      + + +
      + 74.75% + Branches + 74/99 +
      + + +
      + 88.89% + Functions + 32/36 +
      + + +
      + 95.24% + Lines + 140/147 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      FileStatementsBranchesFunctionsLines
      Column.tsx +
      +
      96.43%27/2872.73%24/3387.5%7/896.43%27/28
      index.tsx +
      +
      93.98%125/13375.76%50/6689.29%25/2894.96%113/119
      +
      +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/OptionList/index.tsx.html b/coverage/lcov-report/src/OptionList/index.tsx.html new file mode 100644 index 00000000..eef75283 --- /dev/null +++ b/coverage/lcov-report/src/OptionList/index.tsx.html @@ -0,0 +1,1057 @@ + + + + + + Code coverage report for src/OptionList/index.tsx + + + + + + + + +
      +
      +

      All files / src/OptionList index.tsx

      +
      + +
      + 93.98% + Statements + 125/133 +
      + + +
      + 75.76% + Branches + 50/66 +
      + + +
      + 89.29% + Functions + 25/28 +
      + + +
      + 94.96% + Lines + 113/119 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +136x +  +136x +  +136x +  +136x +  +  +136x +  +136x +22x +21x +  +  +1x +1x +1x +1x +  +1x +  +1x +  +  +  +  +136x +40x +1x +1x +1x +  +  +  +  +  +  +  +136x +136x +  +  +136x +  +136x +57x +40x +  +40x +11x +35x +  +  +11x +10x +  +  +40x +  +  +  +  +136x +22x +22x +  +  +22x +  +  +136x +7x +  +7x +6x +  +  +  +136x +157x +  +157x +103x +  +  +157x +  +  +  +136x +  +  +  +  +  +  +  +136x +105x +6x +  +  +  +  +  +  +  +  +99x +  +99x +157x +  +157x +147x +  +  +  +10x +  +  +  +99x +  +  +  +136x +7x +7x +12x +  +7x +  +7x +7x +7x +  +7x +7x +  +  +  +  +  +  +136x +4x +2x +  +4x +  +  +136x +5x +5x +5x +5x +  +  +  +136x +  +  +17x +  +17x +  +  +  +2x +2x +  +2x +2x +  +  +2x +2x +2x +2x +2x +  +  +  +2x +  +  +  +3x +  +  +3x +  +3x +  +  +  +5x +  +  +5x +  +5x +  +  +  +1x +1x +  +  +  +  +1x +1x +1x +  +  +1x +  +1x +1x +  +  +  +1x +  +  +1x +  +  +  +  +4x +  +4x +4x +  +  +  +  +  +  +  +  +136x +  +  +  +  +  +  +  +  +  +  +136x +  +  +  +  +  +  +  +  +  +136x +  +  +  +136x +217x +  +  +  +  +  +  +  +  +  +136x +  +  +  +  +  +  +  +  +  +  +  +  +  + 
      /* eslint-disable default-case */
      +import * as React from 'react';
      +import classNames from 'classnames';
      +import KeyCode from 'rc-util/lib/KeyCode';
      +import type {
      +  OptionListProps as SelectOptionListProps,
      +  RefOptionListProps,
      +} from 'rc-select/lib/OptionList';
      +import { SelectContext } from 'rc-tree-select/lib/Context';
      +import type { OptionDataNode } from '../interface';
      +import Column from './Column';
      +import { isLeaf, restoreCompatibleValue } from '../util';
      +import CascaderContext from '../context';
      +import useSearchResult from '../hooks/useSearchResult';
      + 
      +type OptionListProps = SelectOptionListProps<OptionDataNode[]>;
      +export type FlattenOptions = OptionListProps['flattenOptions'];
      + 
      +const RefOptionList = React.forwardRef<RefOptionListProps, OptionListProps>((props, ref) => {
      +  const {
      +    prefixCls,
      +    options,
      +    onSelect,
      +    multiple,
      +    open,
      +    flattenOptions,
      +    searchValue,
      +    onToggleOpen,
      +    notFoundContent,
      +    direction,
      +  } = props;
      + 
      +  const rtl = direction === 'rtl';
      + 
      +  const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext);
      +  const { changeOnSelect, expandTrigger, fieldNames, loadData, search } =
      +    React.useContext(CascaderContext);
      + 
      +  // ========================= loadData =========================
      +  const [loadingKeys, setLoadingKeys] = React.useState([]);
      + 
      +  const internalLoadData = (pathValue: React.Key) => {
      +    if (!loadData) {
      +      return;
      +    }
      + 
      +    const entity = flattenOptions.find(flattenOption => flattenOption.data.value === pathValue);
      +    Eif (entity) {
      +      const { options: optionList } = restoreCompatibleValue(entity as any, fieldNames);
      +      const rawOptionList = optionList.map(opt => opt.node);
      + 
      +      setLoadingKeys(keys => [...keys, optionList[optionList.length - 1].value]);
      + 
      +      loadData(rawOptionList);
      +    }
      +  };
      + 
      +  // zombieJ: This is bad. We should make this same as `rc-tree` to use Promise instead.
      +  React.useEffect(() => {
      +    if (loadingKeys.length) {
      +      loadingKeys.forEach(loadingKey => {
      +        const option = flattenOptions.find(opt => opt.value === loadingKey);
      +        Iif (option.data.children) {
      +          setLoadingKeys(keys => keys.filter(key => key !== loadingKey));
      +        }
      +      });
      +    }
      +  }, [flattenOptions, loadingKeys]);
      + 
      +  // ========================== Values ==========================
      +  const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]);
      +  const halfCheckedSet = React.useMemo(() => new Set(halfCheckedKeys), [halfCheckedKeys]);
      + 
      +  // =========================== Open ===========================
      +  const [openPath, setOpenPath] = React.useState<React.Key[]>([]);
      + 
      +  React.useEffect(() => {
      +    if (open) {
      +      let nextOpenPath: React.Key[] = [];
      + 
      +      if (!multiple && checkedKeys.length) {
      +        const entity = flattenOptions.find(
      +          flattenOption => flattenOption.data.value === checkedKeys[0],
      +        );
      + 
      +        if (entity) {
      +          nextOpenPath = restoreCompatibleValue(entity as any, fieldNames).path;
      +        }
      +      }
      +      setOpenPath(nextOpenPath);
      +    }
      +  }, [open]);
      + 
      +  // =========================== Path ===========================
      +  const onPathOpen = (index: number, pathValue: React.Key) => {
      +    const nextOpenPath = [...openPath.slice(0, index), pathValue];
      +    setOpenPath(nextOpenPath);
      + 
      +    // Trigger loadData
      +    internalLoadData(pathValue);
      +  };
      + 
      +  const onPathSelect = (pathValue: React.Key, leaf: boolean) => {
      +    onSelect(pathValue, { selected: !checkedSet.has(pathValue) });
      + 
      +    if (!multiple && (leaf || (changeOnSelect && expandTrigger === 'hover'))) {
      +      onToggleOpen(false);
      +    }
      +  };
      + 
      +  const getPathList = (pathList: React.Key[]) => {
      +    let currentOptions = options;
      + 
      +    for (let i = 0; i < pathList.length; i += 1) {
      +      currentOptions = (currentOptions || []).find(option => option.value === pathList[i]).children;
      +    }
      + 
      +    return currentOptions;
      +  };
      + 
      +  // ========================== Search ==========================
      +  const searchOptions = useSearchResult({
      +    ...props,
      +    fieldNames,
      +    changeOnSelect,
      +    searchConfig: search,
      +  });
      + 
      +  // ========================== Column ==========================
      +  const optionColumns = React.useMemo(() => {
      +    if (searchValue) {
      +      return [
      +        {
      +          options: searchOptions,
      +        },
      +      ];
      +    }
      + 
      +    const rawOptionColumns: {
      +      options: OptionDataNode[];
      +    }[] = [];
      + 
      +    for (let i = 0; i <= openPath.length; i += 1) {
      +      const subOptions = getPathList(openPath.slice(0, i));
      + 
      +      if (subOptions) {
      +        rawOptionColumns.push({
      +          options: subOptions,
      +        });
      +      } else {
      +        break;
      +      }
      +    }
      + 
      +    return rawOptionColumns;
      +  }, [searchValue, searchOptions, openPath]);
      + 
      +  // ========================= Keyboard =========================
      +  const getActiveOption = (activeColumnIndex: number, offset: number) => {
      +    const pathActiveValue = openPath[activeColumnIndex];
      +    const currentOptions = optionColumns[activeColumnIndex]?.options || [];
      +    const activeOptionIndex = currentOptions.findIndex(opt => opt.value === pathActiveValue);
      + 
      +    const len = currentOptions.length;
      + 
      +    for (let i = 1; i <= len; i += 1) {
      +      const current = (activeOptionIndex + i * offset + len) % len;
      +      const option = currentOptions[current];
      + 
      +      Eif (!option.disabled) {
      +        return option;
      +      }
      +    }
      + 
      +    return null;
      +  };
      + 
      +  const prevColumn = () => {
      +    if (openPath.length <= 1) {
      +      onToggleOpen(false);
      +    }
      +    setOpenPath(openPath.slice(0, -1));
      +  };
      + 
      +  const nextColumn = () => {
      +    const nextColumnIndex = openPath.length;
      +    const nextActiveOption = getActiveOption(nextColumnIndex, 1);
      +    Eif (nextActiveOption) {
      +      onPathOpen(nextColumnIndex, nextActiveOption.value);
      +    }
      +  };
      + 
      +  React.useImperativeHandle(ref, () => ({
      +    // scrollTo: treeRef.current?.scrollTo,
      +    onKeyDown: event => {
      +      const { which } = event;
      + 
      +      switch (which) {
      +        // >>> Arrow keys
      +        case KeyCode.UP:
      +        case KeyCode.DOWN: {
      +          let offset = 0;
      +          Iif (which === KeyCode.UP) {
      +            offset = -1;
      +          } else Eif (which === KeyCode.DOWN) {
      +            offset = 1;
      +          }
      + 
      +          Eif (offset !== 0) {
      +            const activeColumnIndex = Math.max(openPath.length - 1, 0);
      +            const nextActiveOption = getActiveOption(activeColumnIndex, offset);
      +            Eif (nextActiveOption) {
      +              onPathOpen(activeColumnIndex, nextActiveOption.value);
      +            }
      +          }
      + 
      +          break;
      +        }
      + 
      +        case KeyCode.LEFT: {
      +          Iif (rtl) {
      +            nextColumn();
      +          } else {
      +            prevColumn();
      +          }
      +          break;
      +        }
      + 
      +        case KeyCode.RIGHT: {
      +          Iif (rtl) {
      +            prevColumn();
      +          } else {
      +            nextColumn();
      +          }
      +          break;
      +        }
      + 
      +        case KeyCode.BACKSPACE: {
      +          prevColumn();
      +          break;
      +        }
      + 
      +        // >>> Select
      +        case KeyCode.ENTER: {
      +          const lastValue = openPath[openPath.length - 1];
      +          const option = optionColumns[openPath.length - 1].options?.find(
      +            opt => opt.value === lastValue,
      +          );
      + 
      +          const leaf = isLeaf(option);
      + 
      +          Eif (multiple || changeOnSelect || leaf) {
      +            onPathSelect(lastValue, leaf);
      +          }
      + 
      +          // Close for changeOnSelect
      +          Iif (changeOnSelect) {
      +            onToggleOpen(false);
      +          }
      +          break;
      +        }
      + 
      +        // >>> Close
      +        case KeyCode.ESC: {
      +          onToggleOpen(false);
      + 
      +          Eif (open) {
      +            event.stopPropagation();
      +          }
      +        }
      +      }
      +    },
      +    onKeyUp: () => {},
      +  }));
      + 
      +  // ========================== Render ==========================
      +  const columnProps = {
      +    ...props,
      +    onOpen: onPathOpen,
      +    onSelect: onPathSelect,
      +    onToggleOpen,
      +    checkedSet,
      +    halfCheckedSet,
      +    loadingKeys,
      +  };
      + 
      +  // >>>>> Empty
      +  const emptyList: OptionDataNode[] = [
      +    {
      +      title: notFoundContent,
      +      value: '__EMPTY__',
      +      disabled: true,
      +      node: null,
      +    },
      +  ];
      + 
      +  // >>>>> Columns
      +  const mergedOptionColumns = optionColumns[0]?.options?.length
      +    ? optionColumns
      +    : [{ options: emptyList }];
      + 
      +  const columnNodes: React.ReactElement[] = mergedOptionColumns.map((col, index) => (
      +    <Column
      +      key={index}
      +      index={index}
      +      {...columnProps}
      +      options={col.options}
      +      openKey={openPath[index]}
      +    />
      +  ));
      + 
      +  // >>>>> Render
      +  return (
      +    <>
      +      <div
      +        className={classNames(`${prefixCls}-holder`, {
      +          [`${prefixCls}-rtl`]: rtl,
      +        })}
      +      >
      +        {columnNodes}
      +      </div>
      +    </>
      +  );
      +});
      + 
      +export default RefOptionList;
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/context.ts.html b/coverage/lcov-report/src/context.ts.html new file mode 100644 index 00000000..8f7ce5a1 --- /dev/null +++ b/coverage/lcov-report/src/context.ts.html @@ -0,0 +1,172 @@ + + + + + + Code coverage report for src/context.ts + + + + + + + + +
      +
      +

      All files / src context.ts

      +
      + +
      + 100% + Statements + 1/1 +
      + + +
      + 100% + Branches + 0/0 +
      + + +
      + 100% + Functions + 0/0 +
      + + +
      + 100% + Lines + 1/1 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
      import * as React from 'react';
      +import type { CascaderProps } from './Cascader';
      +import type { ShowSearchType } from './interface';
      + 
      +const CascaderContext = React.createContext<
      +  Required<
      +    Pick<
      +      CascaderProps,
      +      | 'changeOnSelect'
      +      | 'expandTrigger'
      +      | 'fieldNames'
      +      | 'expandIcon'
      +      | 'loadingIcon'
      +      | 'loadData'
      +      | 'dropdownMenuColumnStyle'
      +    > & {
      +      search: ShowSearchType;
      +    }
      +  >
      +>({
      +  changeOnSelect: false,
      +  expandTrigger: 'click',
      +  fieldNames: null,
      +  expandIcon: null,
      +  loadingIcon: null,
      +  loadData: null,
      +  dropdownMenuColumnStyle: null,
      +  search: null,
      +});
      + 
      +export default CascaderContext;
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/index.html b/coverage/lcov-report/src/hooks/index.html new file mode 100644 index 00000000..99b640bf --- /dev/null +++ b/coverage/lcov-report/src/hooks/index.html @@ -0,0 +1,140 @@ + + + + + + Code coverage report for src/hooks + + + + + + + + +
      +
      +

      All files src/hooks

      +
      + +
      + 100% + Statements + 50/50 +
      + + +
      + 100% + Branches + 24/24 +
      + + +
      + 100% + Functions + 16/16 +
      + + +
      + 100% + Lines + 45/45 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      FileStatementsBranchesFunctionsLines
      useSearchConfig.ts +
      +
      100%7/7100%6/6100%2/2100%7/7
      useSearchResult.ts +
      +
      100%37/37100%16/16100%12/12100%32/32
      useUpdateEffect.ts +
      +
      100%6/6100%2/2100%2/2100%6/6
      +
      +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/useSearchConfig.ts.html b/coverage/lcov-report/src/hooks/useSearchConfig.ts.html new file mode 100644 index 00000000..0abbb734 --- /dev/null +++ b/coverage/lcov-report/src/hooks/useSearchConfig.ts.html @@ -0,0 +1,157 @@ + + + + + + Code coverage report for src/hooks/useSearchConfig.ts + + + + + + + + +
      +
      +

      All files / src/hooks useSearchConfig.ts

      +
      + +
      + 100% + Statements + 7/7 +
      + + +
      + 100% + Branches + 6/6 +
      + + +
      + 100% + Functions + 2/2 +
      + + +
      + 100% + Lines + 7/7 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27  +  +  +  +  +  +51x +35x +30x +  +  +5x +  +  +  +  +5x +3x +  +  +  +  +  +5x +  +  + 
      import * as React from 'react';
      +import type { ShowSearchType } from '../interface';
      +import type { CascaderProps } from '../Cascader';
      + 
      +// Convert `showSearch` to unique config
      +export default function useSearchConfig(showSearch?: CascaderProps['showSearch']) {
      +  return React.useMemo<[boolean, ShowSearchType]>(() => {
      +    if (!showSearch) {
      +      return [false, {}];
      +    }
      + 
      +    let searchConfig: ShowSearchType = {
      +      matchInputWidth: true,
      +      limit: 50,
      +    };
      + 
      +    if (showSearch && typeof showSearch === 'object') {
      +      searchConfig = {
      +        ...searchConfig,
      +        ...showSearch,
      +      };
      +    }
      + 
      +    return [true, searchConfig];
      +  }, [showSearch]);
      +}
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/useSearchResult.ts.html b/coverage/lcov-report/src/hooks/useSearchResult.ts.html new file mode 100644 index 00000000..ce1ec968 --- /dev/null +++ b/coverage/lcov-report/src/hooks/useSearchResult.ts.html @@ -0,0 +1,346 @@ + + + + + + Code coverage report for src/hooks/useSearchResult.ts + + + + + + + + +
      +
      +

      All files / src/hooks useSearchResult.ts

      +
      + +
      + 100% + Statements + 37/37 +
      + + +
      + 100% + Branches + 16/16 +
      + + +
      + 100% + Functions + 12/12 +
      + + +
      + 100% + Lines + 32/32 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90  +  +  +  +  +4x +38x +  +4x +29x +  +  +  +  +  +  +  +  +  +  +  +  +136x +  +  +136x +136x +  +  +  +136x +39x +33x +  +  +  +6x +30x +60x +30x +  +  +  +  +  +  +6x +25x +  +  +  +6x +6x +  +20x +1x +  +  +  +19x +19x +12x +  +  +  +  +6x +1x +1x +  +  +  +6x +  +  +  +  +136x +39x +12x +12x +  +  +  +  +  +  +136x +  + 
      import * as React from 'react';
      +import type { FlattenOptions } from '../OptionList';
      +import type { FieldNames, ShowSearchType } from '../interface';
      +import { isLeaf, restoreCompatibleValue } from '../util';
      + 
      +const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) =>
      +  options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase()));
      + 
      +const defaultRender: ShowSearchType['render'] = (inputValue, path, prefixCls, fieldNames) =>
      +  path.map(opt => opt[fieldNames.label]).join(' / ');
      + 
      +export interface SearchResultConfig {
      +  prefixCls: string;
      +  searchConfig: ShowSearchType;
      +  flattenOptions: FlattenOptions;
      +  fieldNames: FieldNames;
      +  changeOnSelect: boolean;
      +  searchValue: string;
      +}
      + 
      +export default function useSearchResult(resultConfig: SearchResultConfig) {
      +  const { searchConfig, flattenOptions, fieldNames, changeOnSelect, searchValue, prefixCls } =
      +    resultConfig;
      + 
      +  // ============================== MISC ==============================
      +  const filterOption = searchConfig.filter || defaultFilter;
      +  const renderOption = searchConfig.render || defaultRender;
      + 
      +  // ============================= Filter =============================
      +  // Do filter
      +  const filteredEntityList = React.useMemo(() => {
      +    if (!searchValue) {
      +      return [];
      +    }
      + 
      +    // Normalize list
      +    let normalizeList = flattenOptions.map(option => {
      +      const { options } = restoreCompatibleValue(option as any, fieldNames);
      +      const originOptionList = options.map(opt => opt.node);
      +      return {
      +        option,
      +        originOptionList,
      +      };
      +    });
      + 
      +    // Not keep parent node when !changeOnSelect
      +    if (!changeOnSelect) {
      +      normalizeList = normalizeList.filter(({ option }) => isLeaf(option.data));
      +    }
      + 
      +    // Filter
      +    const filteredList: typeof normalizeList = [];
      +    for (let i = 0; i < normalizeList.length; i += 1) {
      +      // Perf saving if enabled
      +      if (!searchConfig.sort && filteredList.length >= searchConfig.limit) {
      +        break;
      +      }
      + 
      +      // Do filter
      +      const optGrp = normalizeList[i];
      +      if (filterOption(searchValue, optGrp.originOptionList, fieldNames)) {
      +        filteredList.push(optGrp);
      +      }
      +    }
      + 
      +    // Sort: When searchConfig.sort is enabled. We have to filter all the list
      +    if (searchConfig.sort) {
      +      filteredList.sort((a, b) => {
      +        return searchConfig.sort(a.originOptionList, b.originOptionList, searchValue, fieldNames);
      +      });
      +    }
      + 
      +    return filteredList;
      +  }, [flattenOptions, fieldNames, searchValue, filterOption, changeOnSelect, searchConfig]);
      + 
      +  // ======================== Generate Options ========================
      +  // Wrap with connected label
      +  const options = React.useMemo(() => {
      +    return filteredEntityList.map(({ option, originOptionList }) => {
      +      const title = renderOption(searchValue, originOptionList, prefixCls, fieldNames);
      +      return {
      +        ...option.data,
      +        title,
      +      };
      +    });
      +  }, [searchValue, renderOption, filteredEntityList, fieldNames, prefixCls]);
      + 
      +  return options;
      +}
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/useUpdateEffect.ts.html b/coverage/lcov-report/src/hooks/useUpdateEffect.ts.html new file mode 100644 index 00000000..9eec6cd8 --- /dev/null +++ b/coverage/lcov-report/src/hooks/useUpdateEffect.ts.html @@ -0,0 +1,133 @@ + + + + + + Code coverage report for src/hooks/useUpdateEffect.ts + + + + + + + + +
      +
      +

      All files / src/hooks useUpdateEffect.ts

      +
      + +
      + 100% + Statements + 6/6 +
      + + +
      + 100% + Branches + 2/2 +
      + + +
      + 100% + Functions + 2/2 +
      + + +
      + 100% + Lines + 6/6 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19  +  +  +  +  +  +51x +  +51x +36x +35x +35x +  +  +  +1x +  +  + 
      import * as React from 'react';
      + 
      +/**
      + * Work like `componentDidUpdate`
      + */
      +export default function useUpdateEffect(updater: () => void, condition?: any[]) {
      +  const prepareRef = React.useRef(true);
      + 
      +  React.useEffect(() => {
      +    if (prepareRef.current) {
      +      prepareRef.current = false;
      +      return;
      +    }
      + 
      +    // eslint-disable-next-line consistent-return
      +    return updater();
      +  }, condition);
      +}
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/index.html b/coverage/lcov-report/src/index.html new file mode 100644 index 00000000..e5f28acf --- /dev/null +++ b/coverage/lcov-report/src/index.html @@ -0,0 +1,170 @@ + + + + + + Code coverage report for src + + + + + + + + +
      +
      +

      All files src

      +
      + +
      + 94.25% + Statements + 82/87 +
      + + +
      + 79.07% + Branches + 34/43 +
      + + +
      + 88.89% + Functions + 24/27 +
      + + +
      + 95.12% + Lines + 78/82 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      FileStatementsBranchesFunctionsLines
      Cascader.tsx +
      +
      92.42%61/6675%24/3285.71%18/2193.44%57/61
      context.ts +
      +
      100%1/1100%0/0100%0/0100%1/1
      index.tsx +
      +
      0%0/00%0/00%0/00%0/0
      interface.ts +
      +
      0%0/00%0/00%0/00%0/0
      util.ts +
      +
      100%20/2090.91%10/11100%6/6100%20/20
      +
      +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/index.tsx.html b/coverage/lcov-report/src/index.tsx.html new file mode 100644 index 00000000..c4b6a802 --- /dev/null +++ b/coverage/lcov-report/src/index.tsx.html @@ -0,0 +1,85 @@ + + + + + + Code coverage report for src/index.tsx + + + + + + + + +
      +
      +

      All files / src index.tsx

      +
      + +
      + 0% + Statements + 0/0 +
      + + +
      + 0% + Branches + 0/0 +
      + + +
      + 0% + Functions + 0/0 +
      + + +
      + 0% + Lines + 0/0 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3  +  + 
      import Cascader from './Cascader'
      + 
      +export default Cascader;
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/interface.ts.html b/coverage/lcov-report/src/interface.ts.html new file mode 100644 index 00000000..a3ecf1f4 --- /dev/null +++ b/coverage/lcov-report/src/interface.ts.html @@ -0,0 +1,235 @@ + + + + + + Code coverage report for src/interface.ts + + + + + + + + +
      +
      +

      All files / src interface.ts

      +
      + +
      + 0% + Statements + 0/0 +
      + + +
      + 0% + Branches + 0/0 +
      + + +
      + 0% + Functions + 0/0 +
      + + +
      + 0% + Lines + 0/0 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
      import type * as React from 'react';
      + 
      +export interface FieldNames {
      +  value?: string;
      +  label?: string;
      +  children?: string;
      +}
      + 
      +export type CascaderValueType = React.Key[];
      + 
      +export interface DataNode {
      +  label: React.ReactNode;
      +  /** Customize hover title */
      +  title?: string;
      +  value: string;
      +  disabled?: boolean;
      +  children?: DataNode[];
      +  isLeaf?: boolean;
      +}
      + 
      +export interface InternalDataNode extends DataNode {
      +  node: DataNode;
      +}
      + 
      +export interface OptionDataNode extends Omit<InternalDataNode, 'label' | 'children' | 'title'> {
      +  title: React.ReactNode;
      +  children?: OptionDataNode[];
      +}
      +export interface FlattenDataNode {
      +  key: React.Key;
      +  data: DataNode;
      +  path: React.Key[];
      +  parent?: FlattenDataNode;
      +}
      + 
      +export interface ShowSearchType {
      +  filter?: (inputValue: string, options: DataNode[], fieldNames: FieldNames) => boolean;
      +  render?: (
      +    inputValue: string,
      +    path: DataNode[],
      +    prefixCls: string,
      +    fieldNames: FieldNames,
      +  ) => React.ReactNode;
      +  sort?: (
      +    a: DataNode[],
      +    b: DataNode[],
      +    inputValue: string,
      +    fieldNames: FieldNames,
      +  ) => number;
      +  matchInputWidth?: boolean;
      +  limit?: number | false;
      +}
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov-report/src/util.ts.html b/coverage/lcov-report/src/util.ts.html new file mode 100644 index 00000000..348481e4 --- /dev/null +++ b/coverage/lcov-report/src/util.ts.html @@ -0,0 +1,334 @@ + + + + + + Code coverage report for src/util.ts + + + + + + + + +
      +
      +

      All files / src util.ts

      +
      + +
      + 100% + Statements + 20/20 +
      + + +
      + 90.91% + Branches + 10/11 +
      + + +
      + 100% + Functions + 6/6 +
      + + +
      + 100% + Lines + 20/20 +
      + + +
      +

      + Press n or j to go to the next uncovered block, b, p or k for the previous block. +

      +
      +
      +
      
      +
      1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86  +  +  +  +  +  +  +  +  +4x +  +  +  +  +  +  +  +  +  +  +  +  +61x +61x +  +61x +  +61x +133x +133x +  +133x +  +  +61x +  +  +  +519x +  +519x +  +519x +  +  +  +  +  +  +  +  +  +187x +  +  +  +  +  +  +  +  +  +  +  +  +162x +267x +  +  +267x +  +  +  +  +  +267x +127x +  +  +267x +  +  +  +35x +  + 
      import type { FlattenDataNode } from 'rc-tree-select/lib/interface';
      +import type {
      +  CascaderValueType,
      +  DataNode,
      +  FieldNames,
      +  InternalDataNode,
      +  OptionDataNode,
      +} from './interface';
      + 
      +const VALUE_SPLIT = '__RC_CASCADER_SPLIT__';
      + 
      +/**
      + * Convert entity back to path & options
      + * @returns
      + */
      +export function restoreCompatibleValue(
      +  entity: FlattenDataNode,
      +  fieldNames: FieldNames,
      +): {
      +  path: CascaderValueType;
      +  options: InternalDataNode[];
      +} {
      +  const path: CascaderValueType = [];
      +  const options: InternalDataNode[] = [];
      + 
      +  let current = entity;
      + 
      +  while (current) {
      +    path.unshift(current.data.node[fieldNames.value]);
      +    options.unshift(current.data.node as any as InternalDataNode);
      + 
      +    current = current.parent;
      +  }
      + 
      +  return { path, options };
      +}
      + 
      +export function isLeaf(option: OptionDataNode) {
      +  const { children, node } = option;
      + 
      +  const leaf = node?.isLeaf;
      + 
      +  return leaf !== undefined ? leaf : !children?.length;
      +}
      + 
      +/**
      + * We will connect path value to a string. e.g.
      + * ['little', 'bamboo'] => 'little__bamboo'
      + *
      + * zombieJ: It's better to deprecate the same key in the nest tree. Maybe next major version.
      + */
      +export function connectValue(value: React.Key[]) {
      +  return (value || []).join(VALUE_SPLIT);
      +}
      + 
      +/**
      + * Fill options with fully value by path to avoid nest entity with same value.
      + * Which means we need another round to get origin node back!
      + * This is slow perf on large list. We should abandon same value in nest in future.
      + */
      +export function convertOptions(
      +  options: DataNode[],
      +  { value: fieldValue, children: fieldChildren }: FieldNames,
      +): InternalDataNode[] {
      +  function injectValue(list: DataNode[], parentValue = ''): InternalDataNode[] {
      +    return (list || []).map(option => {
      +      const newValue = parentValue
      +        ? connectValue([parentValue, option[fieldValue]])
      +        : option[fieldValue];
      +      const cloneOption = {
      +        ...option,
      +        [fieldValue]: newValue,
      +        node: option,
      +      };
      + 
      +      if (cloneOption[fieldChildren]) {
      +        cloneOption[fieldChildren] = injectValue(cloneOption[fieldChildren], newValue);
      +      }
      + 
      +      return cloneOption;
      +    });
      +  }
      + 
      +  return injectValue(options);
      +}
      + 
      + +
      +
      + + + + + + + + + \ No newline at end of file diff --git a/coverage/lcov.info b/coverage/lcov.info new file mode 100644 index 00000000..371acdf3 --- /dev/null +++ b/coverage/lcov.info @@ -0,0 +1,688 @@ +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/Cascader.tsx +FN:120,(anonymous_0) +FN:150,(anonymous_1) +FN:155,(anonymous_2) +FN:156,(anonymous_3) +FN:159,(anonymous_4) +FN:164,(anonymous_5) +FN:176,(anonymous_6) +FN:186,(anonymous_7) +FN:197,(anonymous_8) +FN:201,(anonymous_9) +FN:206,(anonymous_10) +FN:214,(anonymous_11) +FN:219,(anonymous_12) +FN:228,(anonymous_13) +FN:230,(anonymous_14) +FN:232,(anonymous_15) +FN:234,(anonymous_16) +FN:265,(anonymous_17) +FN:272,(anonymous_18) +FN:322,(anonymous_19) +FN:327,(anonymous_20) +FNF:21 +FNH:18 +FNDA:51,(anonymous_0) +FNDA:35,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +FNDA:7,(anonymous_5) +FNDA:35,(anonymous_6) +FNDA:36,(anonymous_7) +FNDA:35,(anonymous_8) +FNDA:1,(anonymous_9) +FNDA:13,(anonymous_10) +FNDA:32,(anonymous_11) +FNDA:7,(anonymous_12) +FNDA:7,(anonymous_13) +FNDA:7,(anonymous_14) +FNDA:17,(anonymous_15) +FNDA:17,(anonymous_16) +FNDA:50,(anonymous_17) +FNDA:35,(anonymous_18) +FNDA:12,(anonymous_19) +FNDA:151,(anonymous_20) +DA:36,4 +DA:120,4 +DA:146,51 +DA:148,51 +DA:150,51 +DA:153,51 +DA:155,51 +DA:157,0 +DA:160,0 +DA:164,51 +DA:165,7 +DA:168,51 +DA:173,51 +DA:176,51 +DA:177,35 +DA:186,51 +DA:189,36 +DA:190,36 +DA:191,9 +DA:194,36 +DA:197,51 +DA:198,35 +DA:201,51 +DA:202,1 +DA:206,51 +DA:207,13 +DA:209,13 +DA:210,0 +DA:213,13 +DA:214,32 +DA:219,51 +DA:221,7 +DA:223,7 +DA:225,7 +DA:226,7 +DA:228,7 +DA:230,7 +DA:231,7 +DA:232,17 +DA:234,17 +DA:235,7 +DA:239,7 +DA:240,6 +DA:243,7 +DA:244,6 +DA:245,0 +DA:249,6 +DA:255,51 +DA:256,51 +DA:260,51 +DA:263,51 +DA:265,51 +DA:266,50 +DA:267,50 +DA:271,51 +DA:272,35 +DA:297,51 +DA:305,51 +DA:322,12 +DA:327,151 +DA:334,4 +LF:61 +LH:57 +BRDA:139,0,0,40 +BRDA:190,1,0,9 +BRDA:190,1,1,27 +BRDA:191,2,0,0 +BRDA:191,2,1,9 +BRDA:198,3,0,35 +BRDA:198,3,1,33 +BRDA:209,4,0,0 +BRDA:209,4,1,13 +BRDA:223,5,0,0 +BRDA:223,5,1,7 +BRDA:239,6,0,6 +BRDA:239,6,1,1 +BRDA:243,7,0,6 +BRDA:243,7,1,1 +BRDA:244,8,0,0 +BRDA:244,8,1,6 +BRDA:249,9,0,6 +BRDA:249,9,1,0 +BRDA:249,10,0,6 +BRDA:249,10,1,0 +BRDA:255,11,0,51 +BRDA:255,11,1,0 +BRDA:263,12,0,12 +BRDA:263,12,1,39 +BRDA:297,13,0,9 +BRDA:297,13,1,42 +BRDA:297,14,0,51 +BRDA:297,14,1,6 +BRDA:297,14,2,45 +BRDA:310,15,0,0 +BRDA:310,15,1,51 +BRF:32 +BRH:24 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/context.ts +FNF:0 +FNH:0 +DA:5,4 +LF:1 +LH:1 +BRF:0 +BRH:0 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/index.tsx +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/interface.ts +FNF:0 +FNH:0 +LF:0 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/util.ts +FN:16,restoreCompatibleValue +FN:38,isLeaf +FN:52,connectValue +FN:61,convertOptions +FN:65,injectValue +FN:66,(anonymous_5) +FNF:6 +FNH:6 +FNDA:61,restoreCompatibleValue +FNDA:519,isLeaf +FNDA:187,connectValue +FNDA:35,convertOptions +FNDA:162,injectValue +FNDA:267,(anonymous_5) +DA:10,4 +DA:23,61 +DA:24,61 +DA:26,61 +DA:28,61 +DA:29,133 +DA:30,133 +DA:32,133 +DA:35,61 +DA:39,519 +DA:41,519 +DA:43,519 +DA:53,187 +DA:66,162 +DA:67,267 +DA:70,267 +DA:76,267 +DA:77,127 +DA:80,267 +DA:84,35 +LF:20 +LH:20 +BRDA:43,0,0,6 +BRDA:43,0,1,513 +BRDA:53,1,0,187 +BRDA:53,1,1,0 +BRDA:65,2,0,35 +BRDA:66,3,0,162 +BRDA:66,3,1,1 +BRDA:67,4,0,178 +BRDA:67,4,1,89 +BRDA:76,5,0,127 +BRDA:76,5,1,140 +BRF:11 +BRH:10 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/OptionList/Column.tsx +FN:22,Column +FN:45,(anonymous_1) +FN:55,(anonymous_2) +FN:62,(anonymous_3) +FN:89,(anonymous_4) +FN:105,(anonymous_5) +FN:112,(anonymous_6) +FN:118,(anonymous_7) +FNF:8 +FNH:7 +FNDA:217,Column +FNDA:493,(anonymous_1) +FNDA:19,(anonymous_2) +FNDA:15,(anonymous_3) +FNDA:4,(anonymous_4) +FNDA:0,(anonymous_5) +FNDA:15,(anonymous_6) +FNDA:1,(anonymous_7) +DA:35,217 +DA:36,217 +DA:37,217 +DA:40,217 +DA:43,217 +DA:46,493 +DA:47,493 +DA:49,493 +DA:52,493 +DA:55,493 +DA:56,19 +DA:57,15 +DA:62,493 +DA:63,15 +DA:64,6 +DA:70,493 +DA:71,4 +DA:72,489 +DA:73,489 +DA:77,493 +DA:90,4 +DA:91,4 +DA:106,0 +DA:113,15 +DA:114,15 +DA:115,15 +DA:119,1 +DA:120,1 +LF:28 +LH:27 +BRDA:56,0,0,15 +BRDA:56,0,1,4 +BRDA:56,1,0,19 +BRDA:56,1,1,19 +BRDA:63,2,0,6 +BRDA:63,2,1,9 +BRDA:63,3,0,15 +BRDA:63,3,1,15 +BRDA:63,3,2,11 +BRDA:63,3,3,9 +BRDA:70,4,0,4 +BRDA:70,4,1,489 +BRDA:72,5,0,489 +BRDA:72,5,1,0 +BRDA:90,6,0,4 +BRDA:90,6,1,0 +BRDA:95,7,0,493 +BRDA:95,7,1,0 +BRDA:101,8,0,0 +BRDA:101,8,1,0 +BRDA:114,9,0,15 +BRDA:114,9,1,0 +BRDA:114,10,0,15 +BRDA:114,10,1,0 +BRDA:119,11,0,1 +BRDA:119,11,1,0 +BRDA:126,12,0,493 +BRDA:126,12,1,492 +BRDA:126,12,2,346 +BRDA:126,12,3,251 +BRDA:129,13,0,493 +BRDA:129,13,1,1 +BRDA:129,13,2,0 +BRF:33 +BRH:24 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/OptionList/index.tsx +FN:19,(anonymous_0) +FN:42,(anonymous_1) +FN:47,(anonymous_2) +FN:50,(anonymous_3) +FN:52,(anonymous_4) +FN:59,(anonymous_5) +FN:61,(anonymous_6) +FN:62,(anonymous_7) +FN:64,(anonymous_8) +FN:64,(anonymous_9) +FN:71,(anonymous_10) +FN:72,(anonymous_11) +FN:77,(anonymous_12) +FN:83,(anonymous_13) +FN:95,(anonymous_14) +FN:103,(anonymous_15) +FN:111,(anonymous_16) +FN:115,(anonymous_17) +FN:130,(anonymous_18) +FN:159,(anonymous_19) +FN:162,(anonymous_20) +FN:178,(anonymous_21) +FN:185,(anonymous_22) +FN:193,(anonymous_23) +FN:195,(anonymous_24) +FN:247,(anonymous_25) +FN:273,(anonymous_26) +FN:302,(anonymous_27) +FNF:28 +FNH:25 +FNDA:136,(anonymous_0) +FNDA:22,(anonymous_1) +FNDA:1,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:1,(anonymous_4) +FNDA:40,(anonymous_5) +FNDA:1,(anonymous_6) +FNDA:1,(anonymous_7) +FNDA:0,(anonymous_8) +FNDA:0,(anonymous_9) +FNDA:39,(anonymous_10) +FNDA:39,(anonymous_11) +FNDA:57,(anonymous_12) +FNDA:35,(anonymous_13) +FNDA:22,(anonymous_14) +FNDA:7,(anonymous_15) +FNDA:157,(anonymous_16) +FNDA:103,(anonymous_17) +FNDA:105,(anonymous_18) +FNDA:7,(anonymous_19) +FNDA:12,(anonymous_20) +FNDA:4,(anonymous_21) +FNDA:5,(anonymous_22) +FNDA:136,(anonymous_23) +FNDA:17,(anonymous_24) +FNDA:1,(anonymous_25) +FNDA:0,(anonymous_26) +FNDA:217,(anonymous_27) +DA:19,4 +DA:31,136 +DA:33,136 +DA:35,136 +DA:37,136 +DA:40,136 +DA:42,136 +DA:43,22 +DA:44,21 +DA:47,1 +DA:48,1 +DA:49,1 +DA:50,1 +DA:52,1 +DA:54,1 +DA:59,136 +DA:60,40 +DA:61,1 +DA:62,1 +DA:63,1 +DA:64,0 +DA:71,136 +DA:72,136 +DA:75,136 +DA:77,136 +DA:78,57 +DA:79,40 +DA:81,40 +DA:82,11 +DA:83,35 +DA:86,11 +DA:87,10 +DA:90,40 +DA:95,136 +DA:96,22 +DA:97,22 +DA:100,22 +DA:103,136 +DA:104,7 +DA:106,7 +DA:107,6 +DA:111,136 +DA:112,157 +DA:114,157 +DA:115,103 +DA:118,157 +DA:122,136 +DA:130,136 +DA:131,105 +DA:132,6 +DA:141,99 +DA:143,99 +DA:144,157 +DA:146,157 +DA:147,147 +DA:151,10 +DA:155,99 +DA:159,136 +DA:160,7 +DA:161,7 +DA:162,12 +DA:164,7 +DA:166,7 +DA:167,7 +DA:168,7 +DA:170,7 +DA:171,7 +DA:175,0 +DA:178,136 +DA:179,4 +DA:180,2 +DA:182,4 +DA:185,136 +DA:186,5 +DA:187,5 +DA:188,5 +DA:189,5 +DA:193,136 +DA:196,17 +DA:198,17 +DA:202,2 +DA:203,2 +DA:204,0 +DA:205,2 +DA:206,2 +DA:209,2 +DA:210,2 +DA:211,2 +DA:212,2 +DA:213,2 +DA:217,2 +DA:221,3 +DA:222,0 +DA:224,3 +DA:226,3 +DA:230,5 +DA:231,0 +DA:233,5 +DA:235,5 +DA:239,1 +DA:240,1 +DA:245,1 +DA:246,1 +DA:247,1 +DA:250,1 +DA:252,1 +DA:253,1 +DA:257,1 +DA:258,0 +DA:260,1 +DA:265,4 +DA:267,4 +DA:268,4 +DA:277,136 +DA:288,136 +DA:298,136 +DA:302,136 +DA:303,217 +DA:313,136 +LF:119 +LH:113 +BRDA:43,0,0,21 +BRDA:43,0,1,1 +BRDA:48,1,0,1 +BRDA:48,1,1,0 +BRDA:60,2,0,1 +BRDA:60,2,1,39 +BRDA:63,3,0,0 +BRDA:63,3,1,1 +BRDA:78,4,0,40 +BRDA:78,4,1,17 +BRDA:81,5,0,11 +BRDA:81,5,1,29 +BRDA:81,6,0,40 +BRDA:81,6,1,40 +BRDA:86,7,0,10 +BRDA:86,7,1,1 +BRDA:106,8,0,6 +BRDA:106,8,1,1 +BRDA:106,9,0,7 +BRDA:106,9,1,7 +BRDA:106,9,2,2 +BRDA:106,9,3,2 +BRDA:115,10,0,90 +BRDA:115,10,1,0 +BRDA:131,11,0,6 +BRDA:131,11,1,99 +BRDA:146,12,0,147 +BRDA:146,12,1,10 +BRDA:161,13,0,7 +BRDA:161,13,1,0 +BRDA:170,14,0,7 +BRDA:170,14,1,0 +BRDA:179,15,0,2 +BRDA:179,15,1,2 +BRDA:188,16,0,5 +BRDA:188,16,1,0 +BRDA:198,17,0,0 +BRDA:198,17,1,2 +BRDA:198,17,2,3 +BRDA:198,17,3,5 +BRDA:198,17,4,1 +BRDA:198,17,5,1 +BRDA:198,17,6,4 +BRDA:203,18,0,0 +BRDA:203,18,1,2 +BRDA:205,19,0,2 +BRDA:205,19,1,0 +BRDA:209,20,0,2 +BRDA:209,20,1,0 +BRDA:212,21,0,2 +BRDA:212,21,1,0 +BRDA:221,22,0,0 +BRDA:221,22,1,3 +BRDA:230,23,0,0 +BRDA:230,23,1,5 +BRDA:252,24,0,1 +BRDA:252,24,1,0 +BRDA:252,25,0,1 +BRDA:252,25,1,1 +BRDA:252,25,2,1 +BRDA:257,26,0,0 +BRDA:257,26,1,1 +BRDA:267,27,0,4 +BRDA:267,27,1,0 +BRDA:298,28,0,131 +BRDA:298,28,1,5 +BRF:66 +BRH:50 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchConfig.ts +FN:6,useSearchConfig +FN:7,(anonymous_1) +FNF:2 +FNH:2 +FNDA:51,useSearchConfig +FNDA:35,(anonymous_1) +DA:7,51 +DA:8,35 +DA:9,30 +DA:12,5 +DA:17,5 +DA:18,3 +DA:24,5 +LF:7 +LH:7 +BRDA:8,0,0,30 +BRDA:8,0,1,5 +BRDA:17,1,0,3 +BRDA:17,1,1,2 +BRDA:17,2,0,5 +BRDA:17,2,1,5 +BRF:6 +BRH:6 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchResult.ts +FN:6,(anonymous_0) +FN:7,(anonymous_1) +FN:9,(anonymous_2) +FN:10,(anonymous_3) +FN:21,useSearchResult +FN:31,(anonymous_5) +FN:37,(anonymous_6) +FN:39,(anonymous_7) +FN:48,(anonymous_8) +FN:68,(anonymous_9) +FN:78,(anonymous_10) +FN:79,(anonymous_11) +FNF:12 +FNH:12 +FNDA:19,(anonymous_0) +FNDA:38,(anonymous_1) +FNDA:10,(anonymous_2) +FNDA:29,(anonymous_3) +FNDA:136,useSearchResult +FNDA:39,(anonymous_5) +FNDA:30,(anonymous_6) +FNDA:60,(anonymous_7) +FNDA:25,(anonymous_8) +FNDA:1,(anonymous_9) +FNDA:39,(anonymous_10) +FNDA:12,(anonymous_11) +DA:6,4 +DA:7,38 +DA:9,4 +DA:10,29 +DA:23,136 +DA:26,136 +DA:27,136 +DA:31,136 +DA:32,39 +DA:33,33 +DA:37,6 +DA:38,30 +DA:39,60 +DA:40,30 +DA:47,6 +DA:48,25 +DA:52,6 +DA:53,6 +DA:55,20 +DA:56,1 +DA:60,19 +DA:61,19 +DA:62,12 +DA:67,6 +DA:68,1 +DA:69,1 +DA:73,6 +DA:78,136 +DA:79,39 +DA:80,12 +DA:81,12 +DA:88,136 +LF:32 +LH:32 +BRDA:26,0,0,136 +BRDA:26,0,1,136 +BRDA:27,1,0,136 +BRDA:27,1,1,133 +BRDA:32,2,0,33 +BRDA:32,2,1,6 +BRDA:47,3,0,5 +BRDA:47,3,1,1 +BRDA:55,4,0,1 +BRDA:55,4,1,19 +BRDA:55,5,0,20 +BRDA:55,5,1,17 +BRDA:61,6,0,12 +BRDA:61,6,1,7 +BRDA:67,7,0,1 +BRDA:67,7,1,5 +BRF:16 +BRH:16 +end_of_record +TN: +SF:/Users/jilin/projects/antd/rc-cascader/src/hooks/useUpdateEffect.ts +FN:6,useUpdateEffect +FN:9,(anonymous_1) +FNF:2 +FNH:2 +FNDA:51,useUpdateEffect +FNDA:36,(anonymous_1) +DA:7,51 +DA:9,51 +DA:10,36 +DA:11,35 +DA:12,35 +DA:16,1 +LF:6 +LH:6 +BRDA:10,0,0,35 +BRDA:10,0,1,1 +BRF:2 +BRH:2 +end_of_record diff --git a/examples/debug.tsx b/examples/debug.tsx index 98d49517..2eb1416c 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -119,7 +119,9 @@ const Demo = () => { defaultValue={multiple ? [defaultValue] : defaultValue} showSearch // direction="rtl" - /> + > + {/* */} +
      ); }; diff --git a/tests/keyboard.spec.js b/tests/keyboard.spec.js index f7e70f58..57d59277 100644 --- a/tests/keyboard.spec.js +++ b/tests/keyboard.spec.js @@ -13,7 +13,6 @@ describe('Cascader.Keyboard', () => { }; beforeEach(() => { - // TODO: should also handle keyboard wrapper = mount(); }); @@ -76,6 +75,24 @@ describe('Cascader.Keyboard', () => { expect(selectedValue).toEqual(['zj', 'hangzhou', 'yuhang']); }); + it('rtl', () => { + wrapper = mount(); + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); + expect(wrapper.isOpen()).toBeTruthy(); + + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); + expect(wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text()).toEqual('福建'); + + wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); + expect(wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text()).toEqual('福州'); + + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); + expect(wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text()).toEqual('福建'); + + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); + expect(wrapper.isOpen()).toBeFalsy(); + }); + it('should have close menu when press some keys', () => { wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect(wrapper.isOpen()).toBeTruthy(); From 83e5f17f45a1b42ea05dc2b1a981869e751127ed Mon Sep 17 00:00:00 2001 From: zombiej Date: Wed, 25 Aug 2021 21:38:33 +0800 Subject: [PATCH 46/54] chore: clean up --- .gitignore | 1 + coverage/clover.xml | 319 ----- coverage/coverage-final.json | 11 - coverage/lcov-report/block-navigation.js | 79 -- coverage/lcov-report/index.html | 140 --- coverage/lcov-report/prettify.js | 2 - coverage/lcov-report/sort-arrow-sprite.png | Bin 209 -> 0 bytes coverage/lcov-report/sorter.js | 170 --- coverage/lcov-report/src/Cascader.tsx.html | 1087 ----------------- .../src/OptionList/Column.tsx.html | 490 -------- .../lcov-report/src/OptionList/index.html | 125 -- .../lcov-report/src/OptionList/index.tsx.html | 1057 ---------------- coverage/lcov-report/src/context.ts.html | 172 --- coverage/lcov-report/src/hooks/index.html | 140 --- .../src/hooks/useSearchConfig.ts.html | 157 --- .../src/hooks/useSearchResult.ts.html | 346 ------ .../src/hooks/useUpdateEffect.ts.html | 133 -- coverage/lcov-report/src/index.html | 170 --- coverage/lcov-report/src/index.tsx.html | 85 -- coverage/lcov-report/src/interface.ts.html | 235 ---- coverage/lcov-report/src/util.ts.html | 334 ----- coverage/lcov.info | 688 ----------- 22 files changed, 1 insertion(+), 5940 deletions(-) delete mode 100644 coverage/clover.xml delete mode 100644 coverage/coverage-final.json delete mode 100644 coverage/lcov-report/block-navigation.js delete mode 100644 coverage/lcov-report/index.html delete mode 100644 coverage/lcov-report/prettify.js delete mode 100644 coverage/lcov-report/sort-arrow-sprite.png delete mode 100644 coverage/lcov-report/sorter.js delete mode 100644 coverage/lcov-report/src/Cascader.tsx.html delete mode 100644 coverage/lcov-report/src/OptionList/Column.tsx.html delete mode 100644 coverage/lcov-report/src/OptionList/index.html delete mode 100644 coverage/lcov-report/src/OptionList/index.tsx.html delete mode 100644 coverage/lcov-report/src/context.ts.html delete mode 100644 coverage/lcov-report/src/hooks/index.html delete mode 100644 coverage/lcov-report/src/hooks/useSearchConfig.ts.html delete mode 100644 coverage/lcov-report/src/hooks/useSearchResult.ts.html delete mode 100644 coverage/lcov-report/src/hooks/useUpdateEffect.ts.html delete mode 100644 coverage/lcov-report/src/index.html delete mode 100644 coverage/lcov-report/src/index.tsx.html delete mode 100644 coverage/lcov-report/src/interface.ts.html delete mode 100644 coverage/lcov-report/src/util.ts.html delete mode 100644 coverage/lcov.info diff --git a/.gitignore b/.gitignore index 1fad7848..fb945893 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ lib es yarn.lock package-lock.json +coverage # umi .umi .umi-production diff --git a/coverage/clover.xml b/coverage/clover.xml deleted file mode 100644 index 728edfc9..00000000 --- a/coverage/clover.xml +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/coverage/coverage-final.json b/coverage/coverage-final.json deleted file mode 100644 index 3a055f2d..00000000 --- a/coverage/coverage-final.json +++ /dev/null @@ -1,11 +0,0 @@ -{"/Users/jilin/projects/antd/rc-cascader/src/Cascader.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/Cascader.tsx","statementMap":{"0":{"start":{"line":36,"column":20},"end":{"line":39,"column":2}},"1":{"start":{"line":120,"column":17},"end":{"line":332,"column":2}},"2":{"start":{"line":146,"column":6},"end":{"line":146,"column":11}},"3":{"start":{"line":148,"column":35},"end":{"line":148,"column":44}},"4":{"start":{"line":150,"column":27},"end":{"line":150,"column":88}},"5":{"start":{"line":150,"column":47},"end":{"line":150,"column":73}},"6":{"start":{"line":153,"column":22},"end":{"line":153,"column":52}},"7":{"start":{"line":155,"column":2},"end":{"line":162,"column":6}},"8":{"start":{"line":155,"column":40},"end":{"line":162,"column":3}},"9":{"start":{"line":157,"column":6},"end":{"line":157,"column":34}},"10":{"start":{"line":160,"column":6},"end":{"line":160,"column":33}},"11":{"start":{"line":164,"column":27},"end":{"line":165,"column":54}},"12":{"start":{"line":165,"column":4},"end":{"line":165,"column":54}},"13":{"start":{"line":168,"column":42},"end":{"line":171,"column":4}},"14":{"start":{"line":173,"column":43},"end":{"line":173,"column":70}},"15":{"start":{"line":176,"column":24},"end":{"line":178,"column":33}},"16":{"start":{"line":177,"column":4},"end":{"line":177,"column":53}},"17":{"start":{"line":186,"column":31},"end":{"line":195,"column":3}},"18":{"start":{"line":189,"column":45},"end":{"line":189,"column":47}},"19":{"start":{"line":190,"column":4},"end":{"line":192,"column":5}},"20":{"start":{"line":191,"column":6},"end":{"line":191,"column":82}},"21":{"start":{"line":194,"column":4},"end":{"line":194,"column":43}},"22":{"start":{"line":197,"column":44},"end":{"line":199,"column":3}},"23":{"start":{"line":198,"column":4},"end":{"line":198,"column":47}},"24":{"start":{"line":201,"column":2},"end":{"line":203,"column":14}},"25":{"start":{"line":202,"column":4},"end":{"line":202,"column":50}},"26":{"start":{"line":206,"column":22},"end":{"line":216,"column":3}},"27":{"start":{"line":207,"column":34},"end":{"line":207,"column":50}},"28":{"start":{"line":209,"column":4},"end":{"line":211,"column":5}},"29":{"start":{"line":210,"column":6},"end":{"line":210,"column":42}},"30":{"start":{"line":213,"column":4},"end":{"line":215,"column":17}},"31":{"start":{"line":214,"column":26},"end":{"line":214,"column":41}},"32":{"start":{"line":219,"column":27},"end":{"line":252,"column":3}},"33":{"start":{"line":221,"column":4},"end":{"line":221,"column":24}},"34":{"start":{"line":223,"column":22},"end":{"line":223,"column":71}},"35":{"start":{"line":225,"column":42},"end":{"line":225,"column":44}},"36":{"start":{"line":226,"column":38},"end":{"line":226,"column":40}},"37":{"start":{"line":228,"column":26},"end":{"line":228,"column":82}},"38":{"start":{"line":228,"column":75},"end":{"line":228,"column":81}},"39":{"start":{"line":230,"column":4},"end":{"line":236,"column":7}},"40":{"start":{"line":231,"column":40},"end":{"line":231,"column":88}},"41":{"start":{"line":232,"column":28},"end":{"line":232,"column":67}},"42":{"start":{"line":232,"column":55},"end":{"line":232,"column":66}},"43":{"start":{"line":234,"column":6},"end":{"line":234,"column":75}},"44":{"start":{"line":234,"column":45},"end":{"line":234,"column":72}},"45":{"start":{"line":235,"column":6},"end":{"line":235,"column":38}},"46":{"start":{"line":239,"column":4},"end":{"line":241,"column":5}},"47":{"start":{"line":240,"column":6},"end":{"line":240,"column":34}},"48":{"start":{"line":243,"column":4},"end":{"line":251,"column":5}},"49":{"start":{"line":244,"column":6},"end":{"line":250,"column":7}},"50":{"start":{"line":245,"column":8},"end":{"line":245,"column":62}},"51":{"start":{"line":249,"column":8},"end":{"line":249,"column":78}},"52":{"start":{"line":255,"column":2},"end":{"line":261,"column":3}},"53":{"start":{"line":256,"column":4},"end":{"line":259,"column":6}},"54":{"start":{"line":260,"column":4},"end":{"line":260,"column":100}},"55":{"start":{"line":263,"column":21},"end":{"line":263,"column":61}},"56":{"start":{"line":265,"column":42},"end":{"line":268,"column":3}},"57":{"start":{"line":266,"column":4},"end":{"line":266,"column":43}},"58":{"start":{"line":267,"column":4},"end":{"line":267,"column":40}},"59":{"start":{"line":271,"column":18},"end":{"line":292,"column":3}},"60":{"start":{"line":272,"column":11},"end":{"line":281,"column":5}},"61":{"start":{"line":297,"column":4},"end":{"line":303,"column":9}},"62":{"start":{"line":305,"column":2},"end":{"line":331,"column":4}},"63":{"start":{"line":322,"column":30},"end":{"line":322,"column":34}},"64":{"start":{"line":327,"column":36},"end":{"line":327,"column":44}},"65":{"start":{"line":334,"column":0},"end":{"line":334,"column":34}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":120,"column":34},"end":{"line":120,"column":35}},"loc":{"start":{"line":120,"column":89},"end":{"line":332,"column":1}},"line":120},"1":{"name":"(anonymous_1)","decl":{"start":{"line":150,"column":41},"end":{"line":150,"column":42}},"loc":{"start":{"line":150,"column":47},"end":{"line":150,"column":73}},"line":150},"2":{"name":"(anonymous_2)","decl":{"start":{"line":155,"column":33},"end":{"line":155,"column":34}},"loc":{"start":{"line":155,"column":40},"end":{"line":162,"column":3}},"line":155},"3":{"name":"(anonymous_3)","decl":{"start":{"line":156,"column":11},"end":{"line":156,"column":12}},"loc":{"start":{"line":156,"column":17},"end":{"line":158,"column":5}},"line":156},"4":{"name":"(anonymous_4)","decl":{"start":{"line":159,"column":10},"end":{"line":159,"column":11}},"loc":{"start":{"line":159,"column":16},"end":{"line":161,"column":5}},"line":159},"5":{"name":"(anonymous_5)","decl":{"start":{"line":164,"column":27},"end":{"line":164,"column":28}},"loc":{"start":{"line":165,"column":4},"end":{"line":165,"column":54}},"line":165},"6":{"name":"(anonymous_6)","decl":{"start":{"line":176,"column":38},"end":{"line":176,"column":39}},"loc":{"start":{"line":176,"column":44},"end":{"line":178,"column":3}},"line":176},"7":{"name":"(anonymous_7)","decl":{"start":{"line":186,"column":31},"end":{"line":186,"column":32}},"loc":{"start":{"line":188,"column":20},"end":{"line":195,"column":3}},"line":188},"8":{"name":"(anonymous_8)","decl":{"start":{"line":197,"column":59},"end":{"line":197,"column":60}},"loc":{"start":{"line":198,"column":4},"end":{"line":198,"column":47}},"line":198},"9":{"name":"(anonymous_9)","decl":{"start":{"line":201,"column":18},"end":{"line":201,"column":19}},"loc":{"start":{"line":201,"column":24},"end":{"line":203,"column":3}},"line":201},"10":{"name":"(anonymous_10)","decl":{"start":{"line":206,"column":22},"end":{"line":206,"column":23}},"loc":{"start":{"line":206,"column":51},"end":{"line":216,"column":3}},"line":206},"11":{"name":"(anonymous_11)","decl":{"start":{"line":214,"column":19},"end":{"line":214,"column":20}},"loc":{"start":{"line":214,"column":26},"end":{"line":214,"column":41}},"line":214},"12":{"name":"(anonymous_12)","decl":{"start":{"line":219,"column":27},"end":{"line":219,"column":28}},"loc":{"start":{"line":219,"column":75},"end":{"line":252,"column":3}},"line":219},"13":{"name":"(anonymous_13)","decl":{"start":{"line":228,"column":65},"end":{"line":228,"column":66}},"loc":{"start":{"line":228,"column":75},"end":{"line":228,"column":81}},"line":228},"14":{"name":"(anonymous_14)","decl":{"start":{"line":230,"column":26},"end":{"line":230,"column":27}},"loc":{"start":{"line":230,"column":36},"end":{"line":236,"column":5}},"line":230},"15":{"name":"(anonymous_15)","decl":{"start":{"line":232,"column":45},"end":{"line":232,"column":46}},"loc":{"start":{"line":232,"column":55},"end":{"line":232,"column":66}},"line":232},"16":{"name":"(anonymous_16)","decl":{"start":{"line":234,"column":38},"end":{"line":234,"column":39}},"loc":{"start":{"line":234,"column":45},"end":{"line":234,"column":72}},"line":234},"17":{"name":"(anonymous_17)","decl":{"start":{"line":265,"column":42},"end":{"line":265,"column":43}},"loc":{"start":{"line":265,"column":68},"end":{"line":268,"column":3}},"line":265},"18":{"name":"(anonymous_18)","decl":{"start":{"line":272,"column":4},"end":{"line":272,"column":5}},"loc":{"start":{"line":272,"column":11},"end":{"line":281,"column":5}},"line":272},"19":{"name":"(anonymous_19)","decl":{"start":{"line":322,"column":24},"end":{"line":322,"column":25}},"loc":{"start":{"line":322,"column":30},"end":{"line":322,"column":34}},"line":322},"20":{"name":"(anonymous_20)","decl":{"start":{"line":327,"column":30},"end":{"line":327,"column":31}},"loc":{"start":{"line":327,"column":36},"end":{"line":327,"column":44}},"line":327}},"branchMap":{"0":{"loc":{"start":{"line":139,"column":4},"end":{"line":139,"column":20}},"type":"default-arg","locations":[{"start":{"line":139,"column":17},"end":{"line":139,"column":20}}],"line":139},"1":{"loc":{"start":{"line":190,"column":4},"end":{"line":192,"column":5}},"type":"if","locations":[{"start":{"line":190,"column":4},"end":{"line":192,"column":5}},{"start":{"line":190,"column":4},"end":{"line":192,"column":5}}],"line":190},"2":{"loc":{"start":{"line":191,"column":23},"end":{"line":191,"column":57}},"type":"cond-expr","locations":[{"start":{"line":191,"column":34},"end":{"line":191,"column":43}},{"start":{"line":191,"column":46},"end":{"line":191,"column":57}}],"line":191},"3":{"loc":{"start":{"line":198,"column":25},"end":{"line":198,"column":46}},"type":"binary-expr","locations":[{"start":{"line":198,"column":25},"end":{"line":198,"column":30}},{"start":{"line":198,"column":34},"end":{"line":198,"column":46}}],"line":198},"4":{"loc":{"start":{"line":209,"column":4},"end":{"line":211,"column":5}},"type":"if","locations":[{"start":{"line":209,"column":4},"end":{"line":211,"column":5}},{"start":{"line":209,"column":4},"end":{"line":211,"column":5}}],"line":209},"5":{"loc":{"start":{"line":223,"column":23},"end":{"line":223,"column":55}},"type":"cond-expr","locations":[{"start":{"line":223,"column":34},"end":{"line":223,"column":42}},{"start":{"line":223,"column":45},"end":{"line":223,"column":55}}],"line":223},"6":{"loc":{"start":{"line":239,"column":4},"end":{"line":241,"column":5}},"type":"if","locations":[{"start":{"line":239,"column":4},"end":{"line":241,"column":5}},{"start":{"line":239,"column":4},"end":{"line":241,"column":5}}],"line":239},"7":{"loc":{"start":{"line":243,"column":4},"end":{"line":251,"column":5}},"type":"if","locations":[{"start":{"line":243,"column":4},"end":{"line":251,"column":5}},{"start":{"line":243,"column":4},"end":{"line":251,"column":5}}],"line":243},"8":{"loc":{"start":{"line":244,"column":6},"end":{"line":250,"column":7}},"type":"if","locations":[{"start":{"line":244,"column":6},"end":{"line":250,"column":7}},{"start":{"line":244,"column":6},"end":{"line":250,"column":7}}],"line":244},"9":{"loc":{"start":{"line":249,"column":37},"end":{"line":249,"column":54}},"type":"binary-expr","locations":[{"start":{"line":249,"column":37},"end":{"line":249,"column":48}},{"start":{"line":249,"column":52},"end":{"line":249,"column":54}}],"line":249},"10":{"loc":{"start":{"line":249,"column":56},"end":{"line":249,"column":76}},"type":"binary-expr","locations":[{"start":{"line":249,"column":56},"end":{"line":249,"column":70}},{"start":{"line":249,"column":74},"end":{"line":249,"column":76}}],"line":249},"11":{"loc":{"start":{"line":255,"column":2},"end":{"line":261,"column":3}},"type":"if","locations":[{"start":{"line":255,"column":2},"end":{"line":261,"column":3}},{"start":{"line":255,"column":2},"end":{"line":261,"column":3}}],"line":255},"12":{"loc":{"start":{"line":263,"column":21},"end":{"line":263,"column":61}},"type":"cond-expr","locations":[{"start":{"line":263,"column":42},"end":{"line":263,"column":46}},{"start":{"line":263,"column":49},"end":{"line":263,"column":61}}],"line":263},"13":{"loc":{"start":{"line":297,"column":4},"end":{"line":303,"column":9}},"type":"cond-expr","locations":[{"start":{"line":300,"column":8},"end":{"line":300,"column":10}},{"start":{"line":301,"column":8},"end":{"line":303,"column":9}}],"line":297},"14":{"loc":{"start":{"line":297,"column":4},"end":{"line":299,"column":25}},"type":"binary-expr","locations":[{"start":{"line":297,"column":5},"end":{"line":297,"column":17}},{"start":{"line":297,"column":21},"end":{"line":297,"column":49}},{"start":{"line":299,"column":4},"end":{"line":299,"column":25}}],"line":297},"15":{"loc":{"start":{"line":310,"column":15},"end":{"line":310,"column":58}},"type":"cond-expr","locations":[{"start":{"line":310,"column":26},"end":{"line":310,"column":39}},{"start":{"line":310,"column":42},"end":{"line":310,"column":58}}],"line":310}},"s":{"0":4,"1":4,"2":51,"3":51,"4":51,"5":35,"6":51,"7":51,"8":0,"9":0,"10":0,"11":51,"12":7,"13":51,"14":51,"15":51,"16":35,"17":51,"18":36,"19":36,"20":9,"21":36,"22":51,"23":35,"24":51,"25":1,"26":51,"27":13,"28":13,"29":0,"30":13,"31":32,"32":51,"33":7,"34":7,"35":7,"36":7,"37":7,"38":7,"39":7,"40":7,"41":7,"42":17,"43":7,"44":17,"45":7,"46":7,"47":6,"48":7,"49":6,"50":0,"51":6,"52":51,"53":51,"54":51,"55":51,"56":51,"57":50,"58":50,"59":51,"60":35,"61":51,"62":51,"63":12,"64":151,"65":4},"f":{"0":51,"1":35,"2":0,"3":0,"4":0,"5":7,"6":35,"7":36,"8":35,"9":1,"10":13,"11":32,"12":7,"13":7,"14":7,"15":17,"16":17,"17":50,"18":35,"19":12,"20":151},"b":{"0":[40],"1":[9,27],"2":[0,9],"3":[35,33],"4":[0,13],"5":[0,7],"6":[6,1],"7":[6,1],"8":[0,6],"9":[6,0],"10":[6,0],"11":[51,0],"12":[12,39],"13":[9,42],"14":[51,6,45],"15":[0,51]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"0943459e78a382e0357268a89b0627ee6ceff367"} -,"/Users/jilin/projects/antd/rc-cascader/src/context.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/context.ts","statementMap":{"0":{"start":{"line":5,"column":24},"end":{"line":29,"column":2}}},"fnMap":{},"branchMap":{},"s":{"0":4},"f":{},"b":{},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"52c49f34245bd46782adce8a6aaabb426b8c46bc"} -,"/Users/jilin/projects/antd/rc-cascader/src/index.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/index.tsx","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"f1bf564db6e30a965dc2d76a1c380f191d85b0bf"} -,"/Users/jilin/projects/antd/rc-cascader/src/interface.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/interface.ts","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"hash":"d79da64d0d4463ae415da4e74750b2f83ea87810"} -,"/Users/jilin/projects/antd/rc-cascader/src/util.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/util.ts","statementMap":{"0":{"start":{"line":10,"column":20},"end":{"line":10,"column":43}},"1":{"start":{"line":23,"column":34},"end":{"line":23,"column":36}},"2":{"start":{"line":24,"column":38},"end":{"line":24,"column":40}},"3":{"start":{"line":26,"column":16},"end":{"line":26,"column":22}},"4":{"start":{"line":28,"column":2},"end":{"line":33,"column":3}},"5":{"start":{"line":29,"column":4},"end":{"line":29,"column":54}},"6":{"start":{"line":30,"column":4},"end":{"line":30,"column":66}},"7":{"start":{"line":32,"column":4},"end":{"line":32,"column":29}},"8":{"start":{"line":35,"column":2},"end":{"line":35,"column":27}},"9":{"start":{"line":39,"column":29},"end":{"line":39,"column":35}},"10":{"start":{"line":41,"column":15},"end":{"line":41,"column":27}},"11":{"start":{"line":43,"column":2},"end":{"line":43,"column":55}},"12":{"start":{"line":53,"column":2},"end":{"line":53,"column":41}},"13":{"start":{"line":66,"column":4},"end":{"line":81,"column":7}},"14":{"start":{"line":67,"column":23},"end":{"line":69,"column":28}},"15":{"start":{"line":70,"column":26},"end":{"line":74,"column":7}},"16":{"start":{"line":76,"column":6},"end":{"line":78,"column":7}},"17":{"start":{"line":77,"column":8},"end":{"line":77,"column":87}},"18":{"start":{"line":80,"column":6},"end":{"line":80,"column":25}},"19":{"start":{"line":84,"column":2},"end":{"line":84,"column":30}}},"fnMap":{"0":{"name":"restoreCompatibleValue","decl":{"start":{"line":16,"column":16},"end":{"line":16,"column":38}},"loc":{"start":{"line":22,"column":2},"end":{"line":36,"column":1}},"line":22},"1":{"name":"isLeaf","decl":{"start":{"line":38,"column":16},"end":{"line":38,"column":22}},"loc":{"start":{"line":38,"column":47},"end":{"line":44,"column":1}},"line":38},"2":{"name":"connectValue","decl":{"start":{"line":52,"column":16},"end":{"line":52,"column":28}},"loc":{"start":{"line":52,"column":49},"end":{"line":54,"column":1}},"line":52},"3":{"name":"convertOptions","decl":{"start":{"line":61,"column":16},"end":{"line":61,"column":30}},"loc":{"start":{"line":64,"column":22},"end":{"line":85,"column":1}},"line":64},"4":{"name":"injectValue","decl":{"start":{"line":65,"column":11},"end":{"line":65,"column":22}},"loc":{"start":{"line":65,"column":79},"end":{"line":82,"column":3}},"line":65},"5":{"name":"(anonymous_5)","decl":{"start":{"line":66,"column":28},"end":{"line":66,"column":29}},"loc":{"start":{"line":66,"column":38},"end":{"line":81,"column":5}},"line":66}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":9},"end":{"line":43,"column":54}},"type":"cond-expr","locations":[{"start":{"line":43,"column":30},"end":{"line":43,"column":34}},{"start":{"line":43,"column":37},"end":{"line":43,"column":54}}],"line":43},"1":{"loc":{"start":{"line":53,"column":10},"end":{"line":53,"column":21}},"type":"binary-expr","locations":[{"start":{"line":53,"column":10},"end":{"line":53,"column":15}},{"start":{"line":53,"column":19},"end":{"line":53,"column":21}}],"line":53},"2":{"loc":{"start":{"line":65,"column":41},"end":{"line":65,"column":57}},"type":"default-arg","locations":[{"start":{"line":65,"column":55},"end":{"line":65,"column":57}}],"line":65},"3":{"loc":{"start":{"line":66,"column":12},"end":{"line":66,"column":22}},"type":"binary-expr","locations":[{"start":{"line":66,"column":12},"end":{"line":66,"column":16}},{"start":{"line":66,"column":20},"end":{"line":66,"column":22}}],"line":66},"4":{"loc":{"start":{"line":67,"column":23},"end":{"line":69,"column":28}},"type":"cond-expr","locations":[{"start":{"line":68,"column":10},"end":{"line":68,"column":57}},{"start":{"line":69,"column":10},"end":{"line":69,"column":28}}],"line":67},"5":{"loc":{"start":{"line":76,"column":6},"end":{"line":78,"column":7}},"type":"if","locations":[{"start":{"line":76,"column":6},"end":{"line":78,"column":7}},{"start":{"line":76,"column":6},"end":{"line":78,"column":7}}],"line":76}},"s":{"0":4,"1":61,"2":61,"3":61,"4":61,"5":133,"6":133,"7":133,"8":61,"9":519,"10":519,"11":519,"12":187,"13":162,"14":267,"15":267,"16":267,"17":127,"18":267,"19":35},"f":{"0":61,"1":519,"2":187,"3":35,"4":162,"5":267},"b":{"0":[6,513],"1":[187,0],"2":[35],"3":[162,1],"4":[178,89],"5":[127,140]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"3553d2b2bf6f9395203010e4e139e2ae0df0c88b"} -,"/Users/jilin/projects/antd/rc-cascader/src/OptionList/Column.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/OptionList/Column.tsx","statementMap":{"0":{"start":{"line":35,"column":24},"end":{"line":35,"column":43}},"1":{"start":{"line":36,"column":28},"end":{"line":36,"column":52}},"2":{"start":{"line":37,"column":28},"end":{"line":37,"column":59}},"3":{"start":{"line":40,"column":4},"end":{"line":40,"column":37}},"4":{"start":{"line":43,"column":2},"end":{"line":136,"column":4}},"5":{"start":{"line":46,"column":42},"end":{"line":46,"column":48}},"6":{"start":{"line":47,"column":29},"end":{"line":47,"column":43}},"7":{"start":{"line":49,"column":26},"end":{"line":49,"column":53}},"8":{"start":{"line":52,"column":24},"end":{"line":52,"column":45}},"9":{"start":{"line":55,"column":32},"end":{"line":59,"column":9}},"10":{"start":{"line":56,"column":10},"end":{"line":58,"column":11}},"11":{"start":{"line":57,"column":12},"end":{"line":57,"column":33}},"12":{"start":{"line":62,"column":30},"end":{"line":66,"column":9}},"13":{"start":{"line":63,"column":10},"end":{"line":65,"column":11}},"14":{"start":{"line":64,"column":12},"end":{"line":64,"column":42}},"15":{"start":{"line":70,"column":8},"end":{"line":74,"column":9}},"16":{"start":{"line":71,"column":10},"end":{"line":71,"column":29}},"17":{"start":{"line":72,"column":15},"end":{"line":74,"column":9}},"18":{"start":{"line":73,"column":10},"end":{"line":73,"column":31}},"19":{"start":{"line":77,"column":8},"end":{"line":133,"column":10}},"20":{"start":{"line":90,"column":14},"end":{"line":92,"column":15}},"21":{"start":{"line":91,"column":16},"end":{"line":91,"column":34}},"22":{"start":{"line":106,"column":18},"end":{"line":106,"column":34}},"23":{"start":{"line":113,"column":16},"end":{"line":113,"column":34}},"24":{"start":{"line":114,"column":16},"end":{"line":116,"column":17}},"25":{"start":{"line":115,"column":18},"end":{"line":115,"column":34}},"26":{"start":{"line":119,"column":16},"end":{"line":121,"column":17}},"27":{"start":{"line":120,"column":18},"end":{"line":120,"column":38}}},"fnMap":{"0":{"name":"Column","decl":{"start":{"line":22,"column":24},"end":{"line":22,"column":30}},"loc":{"start":{"line":34,"column":16},"end":{"line":137,"column":1}},"line":34},"1":{"name":"(anonymous_1)","decl":{"start":{"line":45,"column":19},"end":{"line":45,"column":20}},"loc":{"start":{"line":45,"column":29},"end":{"line":134,"column":7}},"line":45},"2":{"name":"(anonymous_2)","decl":{"start":{"line":55,"column":32},"end":{"line":55,"column":33}},"loc":{"start":{"line":55,"column":38},"end":{"line":59,"column":9}},"line":55},"3":{"name":"(anonymous_3)","decl":{"start":{"line":62,"column":30},"end":{"line":62,"column":31}},"loc":{"start":{"line":62,"column":36},"end":{"line":66,"column":9}},"line":62},"4":{"name":"(anonymous_4)","decl":{"start":{"line":89,"column":26},"end":{"line":89,"column":27}},"loc":{"start":{"line":89,"column":32},"end":{"line":93,"column":13}},"line":89},"5":{"name":"(anonymous_5)","decl":{"start":{"line":105,"column":25},"end":{"line":105,"column":26}},"loc":{"start":{"line":105,"column":31},"end":{"line":107,"column":17}},"line":105},"6":{"name":"(anonymous_6)","decl":{"start":{"line":112,"column":23},"end":{"line":112,"column":24}},"loc":{"start":{"line":112,"column":29},"end":{"line":117,"column":15}},"line":112},"7":{"name":"(anonymous_7)","decl":{"start":{"line":118,"column":29},"end":{"line":118,"column":30}},"loc":{"start":{"line":118,"column":35},"end":{"line":122,"column":15}},"line":118}},"branchMap":{"0":{"loc":{"start":{"line":56,"column":10},"end":{"line":58,"column":11}},"type":"if","locations":[{"start":{"line":56,"column":10},"end":{"line":58,"column":11}},{"start":{"line":56,"column":10},"end":{"line":58,"column":11}}],"line":56},"1":{"loc":{"start":{"line":56,"column":14},"end":{"line":56,"column":40}},"type":"binary-expr","locations":[{"start":{"line":56,"column":14},"end":{"line":56,"column":23}},{"start":{"line":56,"column":27},"end":{"line":56,"column":40}}],"line":56},"2":{"loc":{"start":{"line":63,"column":10},"end":{"line":65,"column":11}},"type":"if","locations":[{"start":{"line":63,"column":10},"end":{"line":65,"column":11}},{"start":{"line":63,"column":10},"end":{"line":65,"column":11}}],"line":63},"3":{"loc":{"start":{"line":63,"column":14},"end":{"line":63,"column":71}},"type":"binary-expr","locations":[{"start":{"line":63,"column":14},"end":{"line":63,"column":23}},{"start":{"line":63,"column":28},"end":{"line":63,"column":40}},{"start":{"line":63,"column":44},"end":{"line":63,"column":58}},{"start":{"line":63,"column":62},"end":{"line":63,"column":70}}],"line":63},"4":{"loc":{"start":{"line":70,"column":8},"end":{"line":74,"column":9}},"type":"if","locations":[{"start":{"line":70,"column":8},"end":{"line":74,"column":9}},{"start":{"line":70,"column":8},"end":{"line":74,"column":9}}],"line":70},"5":{"loc":{"start":{"line":72,"column":15},"end":{"line":74,"column":9}},"type":"if","locations":[{"start":{"line":72,"column":15},"end":{"line":74,"column":9}},{"start":{"line":72,"column":15},"end":{"line":74,"column":9}}],"line":72},"6":{"loc":{"start":{"line":90,"column":14},"end":{"line":92,"column":15}},"type":"if","locations":[{"start":{"line":90,"column":14},"end":{"line":92,"column":15}},{"start":{"line":90,"column":14},"end":{"line":92,"column":15}}],"line":90},"7":{"loc":{"start":{"line":95,"column":13},"end":{"line":109,"column":13}},"type":"binary-expr","locations":[{"start":{"line":95,"column":13},"end":{"line":95,"column":21}},{"start":{"line":96,"column":14},"end":{"line":108,"column":16}}],"line":95},"8":{"loc":{"start":{"line":101,"column":60},"end":{"line":101,"column":97}},"type":"binary-expr","locations":[{"start":{"line":101,"column":60},"end":{"line":101,"column":68}},{"start":{"line":101,"column":72},"end":{"line":101,"column":97}}],"line":101},"9":{"loc":{"start":{"line":114,"column":16},"end":{"line":116,"column":17}},"type":"if","locations":[{"start":{"line":114,"column":16},"end":{"line":116,"column":17}},{"start":{"line":114,"column":16},"end":{"line":116,"column":17}}],"line":114},"10":{"loc":{"start":{"line":114,"column":20},"end":{"line":114,"column":45}},"type":"binary-expr","locations":[{"start":{"line":114,"column":20},"end":{"line":114,"column":29}},{"start":{"line":114,"column":33},"end":{"line":114,"column":45}}],"line":114},"11":{"loc":{"start":{"line":119,"column":16},"end":{"line":121,"column":17}},"type":"if","locations":[{"start":{"line":119,"column":16},"end":{"line":121,"column":17}},{"start":{"line":119,"column":16},"end":{"line":121,"column":17}}],"line":119},"12":{"loc":{"start":{"line":126,"column":13},"end":{"line":128,"column":13}},"type":"binary-expr","locations":[{"start":{"line":126,"column":13},"end":{"line":126,"column":23}},{"start":{"line":126,"column":27},"end":{"line":126,"column":37}},{"start":{"line":126,"column":41},"end":{"line":126,"column":54}},{"start":{"line":127,"column":14},"end":{"line":127,"column":84}}],"line":126},"13":{"loc":{"start":{"line":129,"column":13},"end":{"line":131,"column":13}},"type":"binary-expr","locations":[{"start":{"line":129,"column":13},"end":{"line":129,"column":22}},{"start":{"line":129,"column":26},"end":{"line":129,"column":37}},{"start":{"line":130,"column":14},"end":{"line":130,"column":81}}],"line":129}},"s":{"0":217,"1":217,"2":217,"3":217,"4":217,"5":493,"6":493,"7":493,"8":493,"9":493,"10":19,"11":15,"12":493,"13":15,"14":6,"15":493,"16":4,"17":489,"18":489,"19":493,"20":4,"21":4,"22":0,"23":15,"24":15,"25":15,"26":1,"27":1},"f":{"0":217,"1":493,"2":19,"3":15,"4":4,"5":0,"6":15,"7":1},"b":{"0":[15,4],"1":[19,19],"2":[6,9],"3":[15,15,11,9],"4":[4,489],"5":[489,0],"6":[4,0],"7":[493,0],"8":[0,0],"9":[15,0],"10":[15,0],"11":[1,0],"12":[493,492,346,251],"13":[493,1,0]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"a3166b972e9a5ddccbba40dc4d4fef34dddf6ece"} -,"/Users/jilin/projects/antd/rc-cascader/src/OptionList/index.tsx": {"path":"/Users/jilin/projects/antd/rc-cascader/src/OptionList/index.tsx","statementMap":{"0":{"start":{"line":19,"column":22},"end":{"line":324,"column":2}},"1":{"start":{"line":31,"column":6},"end":{"line":31,"column":11}},"2":{"start":{"line":33,"column":14},"end":{"line":33,"column":33}},"3":{"start":{"line":35,"column":43},"end":{"line":35,"column":74}},"4":{"start":{"line":37,"column":4},"end":{"line":37,"column":37}},"5":{"start":{"line":40,"column":40},"end":{"line":40,"column":58}},"6":{"start":{"line":42,"column":27},"end":{"line":56,"column":3}},"7":{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},"8":{"start":{"line":44,"column":6},"end":{"line":44,"column":13}},"9":{"start":{"line":47,"column":19},"end":{"line":47,"column":95}},"10":{"start":{"line":47,"column":56},"end":{"line":47,"column":94}},"11":{"start":{"line":48,"column":4},"end":{"line":55,"column":5}},"12":{"start":{"line":49,"column":38},"end":{"line":49,"column":87}},"13":{"start":{"line":50,"column":28},"end":{"line":50,"column":59}},"14":{"start":{"line":50,"column":50},"end":{"line":50,"column":58}},"15":{"start":{"line":52,"column":6},"end":{"line":52,"column":81}},"16":{"start":{"line":52,"column":29},"end":{"line":52,"column":79}},"17":{"start":{"line":54,"column":6},"end":{"line":54,"column":30}},"18":{"start":{"line":59,"column":2},"end":{"line":68,"column":36}},"19":{"start":{"line":60,"column":4},"end":{"line":67,"column":5}},"20":{"start":{"line":61,"column":6},"end":{"line":66,"column":9}},"21":{"start":{"line":62,"column":23},"end":{"line":62,"column":75}},"22":{"start":{"line":62,"column":50},"end":{"line":62,"column":74}},"23":{"start":{"line":63,"column":8},"end":{"line":65,"column":9}},"24":{"start":{"line":64,"column":10},"end":{"line":64,"column":73}},"25":{"start":{"line":64,"column":33},"end":{"line":64,"column":71}},"26":{"start":{"line":64,"column":52},"end":{"line":64,"column":70}},"27":{"start":{"line":71,"column":21},"end":{"line":71,"column":77}},"28":{"start":{"line":71,"column":41},"end":{"line":71,"column":61}},"29":{"start":{"line":72,"column":25},"end":{"line":72,"column":89}},"30":{"start":{"line":72,"column":45},"end":{"line":72,"column":69}},"31":{"start":{"line":75,"column":34},"end":{"line":75,"column":65}},"32":{"start":{"line":77,"column":2},"end":{"line":92,"column":13}},"33":{"start":{"line":78,"column":4},"end":{"line":91,"column":5}},"34":{"start":{"line":79,"column":38},"end":{"line":79,"column":40}},"35":{"start":{"line":81,"column":6},"end":{"line":89,"column":7}},"36":{"start":{"line":82,"column":23},"end":{"line":84,"column":9}},"37":{"start":{"line":83,"column":27},"end":{"line":83,"column":70}},"38":{"start":{"line":86,"column":8},"end":{"line":88,"column":9}},"39":{"start":{"line":87,"column":10},"end":{"line":87,"column":80}},"40":{"start":{"line":90,"column":6},"end":{"line":90,"column":32}},"41":{"start":{"line":95,"column":21},"end":{"line":101,"column":3}},"42":{"start":{"line":96,"column":25},"end":{"line":96,"column":65}},"43":{"start":{"line":97,"column":4},"end":{"line":97,"column":30}},"44":{"start":{"line":100,"column":4},"end":{"line":100,"column":32}},"45":{"start":{"line":103,"column":23},"end":{"line":109,"column":3}},"46":{"start":{"line":104,"column":4},"end":{"line":104,"column":66}},"47":{"start":{"line":106,"column":4},"end":{"line":108,"column":5}},"48":{"start":{"line":107,"column":6},"end":{"line":107,"column":26}},"49":{"start":{"line":111,"column":22},"end":{"line":119,"column":3}},"50":{"start":{"line":112,"column":25},"end":{"line":112,"column":32}},"51":{"start":{"line":114,"column":4},"end":{"line":116,"column":5}},"52":{"start":{"line":114,"column":17},"end":{"line":114,"column":18}},"53":{"start":{"line":115,"column":6},"end":{"line":115,"column":100}},"54":{"start":{"line":115,"column":61},"end":{"line":115,"column":89}},"55":{"start":{"line":118,"column":4},"end":{"line":118,"column":26}},"56":{"start":{"line":122,"column":24},"end":{"line":127,"column":4}},"57":{"start":{"line":130,"column":24},"end":{"line":156,"column":44}},"58":{"start":{"line":131,"column":4},"end":{"line":137,"column":5}},"59":{"start":{"line":132,"column":6},"end":{"line":136,"column":8}},"60":{"start":{"line":141,"column":10},"end":{"line":141,"column":12}},"61":{"start":{"line":143,"column":4},"end":{"line":153,"column":5}},"62":{"start":{"line":143,"column":17},"end":{"line":143,"column":18}},"63":{"start":{"line":144,"column":25},"end":{"line":144,"column":58}},"64":{"start":{"line":146,"column":6},"end":{"line":152,"column":7}},"65":{"start":{"line":147,"column":8},"end":{"line":149,"column":11}},"66":{"start":{"line":151,"column":8},"end":{"line":151,"column":14}},"67":{"start":{"line":155,"column":4},"end":{"line":155,"column":28}},"68":{"start":{"line":159,"column":26},"end":{"line":176,"column":3}},"69":{"start":{"line":160,"column":28},"end":{"line":160,"column":55}},"70":{"start":{"line":161,"column":27},"end":{"line":161,"column":74}},"71":{"start":{"line":162,"column":30},"end":{"line":162,"column":92}},"72":{"start":{"line":162,"column":62},"end":{"line":162,"column":91}},"73":{"start":{"line":164,"column":16},"end":{"line":164,"column":37}},"74":{"start":{"line":166,"column":4},"end":{"line":173,"column":5}},"75":{"start":{"line":166,"column":17},"end":{"line":166,"column":18}},"76":{"start":{"line":167,"column":22},"end":{"line":167,"column":66}},"77":{"start":{"line":168,"column":21},"end":{"line":168,"column":44}},"78":{"start":{"line":170,"column":6},"end":{"line":172,"column":7}},"79":{"start":{"line":171,"column":8},"end":{"line":171,"column":22}},"80":{"start":{"line":175,"column":4},"end":{"line":175,"column":16}},"81":{"start":{"line":178,"column":21},"end":{"line":183,"column":3}},"82":{"start":{"line":179,"column":4},"end":{"line":181,"column":5}},"83":{"start":{"line":180,"column":6},"end":{"line":180,"column":26}},"84":{"start":{"line":182,"column":4},"end":{"line":182,"column":39}},"85":{"start":{"line":185,"column":21},"end":{"line":191,"column":3}},"86":{"start":{"line":186,"column":28},"end":{"line":186,"column":43}},"87":{"start":{"line":187,"column":29},"end":{"line":187,"column":64}},"88":{"start":{"line":188,"column":4},"end":{"line":190,"column":5}},"89":{"start":{"line":189,"column":6},"end":{"line":189,"column":58}},"90":{"start":{"line":193,"column":2},"end":{"line":274,"column":6}},"91":{"start":{"line":193,"column":40},"end":{"line":274,"column":3}},"92":{"start":{"line":196,"column":24},"end":{"line":196,"column":29}},"93":{"start":{"line":198,"column":6},"end":{"line":271,"column":7}},"94":{"start":{"line":202,"column":23},"end":{"line":202,"column":24}},"95":{"start":{"line":203,"column":10},"end":{"line":207,"column":11}},"96":{"start":{"line":204,"column":12},"end":{"line":204,"column":24}},"97":{"start":{"line":205,"column":17},"end":{"line":207,"column":11}},"98":{"start":{"line":206,"column":12},"end":{"line":206,"column":23}},"99":{"start":{"line":209,"column":10},"end":{"line":215,"column":11}},"100":{"start":{"line":210,"column":38},"end":{"line":210,"column":70}},"101":{"start":{"line":211,"column":37},"end":{"line":211,"column":79}},"102":{"start":{"line":212,"column":12},"end":{"line":214,"column":13}},"103":{"start":{"line":213,"column":14},"end":{"line":213,"column":68}},"104":{"start":{"line":217,"column":10},"end":{"line":217,"column":16}},"105":{"start":{"line":221,"column":10},"end":{"line":225,"column":11}},"106":{"start":{"line":222,"column":12},"end":{"line":222,"column":25}},"107":{"start":{"line":224,"column":12},"end":{"line":224,"column":25}},"108":{"start":{"line":226,"column":10},"end":{"line":226,"column":16}},"109":{"start":{"line":230,"column":10},"end":{"line":234,"column":11}},"110":{"start":{"line":231,"column":12},"end":{"line":231,"column":25}},"111":{"start":{"line":233,"column":12},"end":{"line":233,"column":25}},"112":{"start":{"line":235,"column":10},"end":{"line":235,"column":16}},"113":{"start":{"line":239,"column":10},"end":{"line":239,"column":23}},"114":{"start":{"line":240,"column":10},"end":{"line":240,"column":16}},"115":{"start":{"line":245,"column":28},"end":{"line":245,"column":57}},"116":{"start":{"line":246,"column":25},"end":{"line":248,"column":11}},"117":{"start":{"line":247,"column":19},"end":{"line":247,"column":42}},"118":{"start":{"line":250,"column":23},"end":{"line":250,"column":37}},"119":{"start":{"line":252,"column":10},"end":{"line":254,"column":11}},"120":{"start":{"line":253,"column":12},"end":{"line":253,"column":42}},"121":{"start":{"line":257,"column":10},"end":{"line":259,"column":11}},"122":{"start":{"line":258,"column":12},"end":{"line":258,"column":32}},"123":{"start":{"line":260,"column":10},"end":{"line":260,"column":16}},"124":{"start":{"line":265,"column":10},"end":{"line":265,"column":30}},"125":{"start":{"line":267,"column":10},"end":{"line":269,"column":11}},"126":{"start":{"line":268,"column":12},"end":{"line":268,"column":36}},"127":{"start":{"line":277,"column":22},"end":{"line":285,"column":3}},"128":{"start":{"line":288,"column":38},"end":{"line":295,"column":3}},"129":{"start":{"line":298,"column":30},"end":{"line":300,"column":30}},"130":{"start":{"line":302,"column":44},"end":{"line":310,"column":4}},"131":{"start":{"line":303,"column":4},"end":{"line":309,"column":6}},"132":{"start":{"line":313,"column":2},"end":{"line":323,"column":4}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":19,"column":76},"end":{"line":19,"column":77}},"loc":{"start":{"line":19,"column":92},"end":{"line":324,"column":1}},"line":19},"1":{"name":"(anonymous_1)","decl":{"start":{"line":42,"column":27},"end":{"line":42,"column":28}},"loc":{"start":{"line":42,"column":53},"end":{"line":56,"column":3}},"line":42},"2":{"name":"(anonymous_2)","decl":{"start":{"line":47,"column":39},"end":{"line":47,"column":40}},"loc":{"start":{"line":47,"column":56},"end":{"line":47,"column":94}},"line":47},"3":{"name":"(anonymous_3)","decl":{"start":{"line":50,"column":43},"end":{"line":50,"column":44}},"loc":{"start":{"line":50,"column":50},"end":{"line":50,"column":58}},"line":50},"4":{"name":"(anonymous_4)","decl":{"start":{"line":52,"column":21},"end":{"line":52,"column":22}},"loc":{"start":{"line":52,"column":29},"end":{"line":52,"column":79}},"line":52},"5":{"name":"(anonymous_5)","decl":{"start":{"line":59,"column":18},"end":{"line":59,"column":19}},"loc":{"start":{"line":59,"column":24},"end":{"line":68,"column":3}},"line":59},"6":{"name":"(anonymous_6)","decl":{"start":{"line":61,"column":26},"end":{"line":61,"column":27}},"loc":{"start":{"line":61,"column":40},"end":{"line":66,"column":7}},"line":61},"7":{"name":"(anonymous_7)","decl":{"start":{"line":62,"column":43},"end":{"line":62,"column":44}},"loc":{"start":{"line":62,"column":50},"end":{"line":62,"column":74}},"line":62},"8":{"name":"(anonymous_8)","decl":{"start":{"line":64,"column":25},"end":{"line":64,"column":26}},"loc":{"start":{"line":64,"column":33},"end":{"line":64,"column":71}},"line":64},"9":{"name":"(anonymous_9)","decl":{"start":{"line":64,"column":45},"end":{"line":64,"column":46}},"loc":{"start":{"line":64,"column":52},"end":{"line":64,"column":70}},"line":64},"10":{"name":"(anonymous_10)","decl":{"start":{"line":71,"column":35},"end":{"line":71,"column":36}},"loc":{"start":{"line":71,"column":41},"end":{"line":71,"column":61}},"line":71},"11":{"name":"(anonymous_11)","decl":{"start":{"line":72,"column":39},"end":{"line":72,"column":40}},"loc":{"start":{"line":72,"column":45},"end":{"line":72,"column":69}},"line":72},"12":{"name":"(anonymous_12)","decl":{"start":{"line":77,"column":18},"end":{"line":77,"column":19}},"loc":{"start":{"line":77,"column":24},"end":{"line":92,"column":3}},"line":77},"13":{"name":"(anonymous_13)","decl":{"start":{"line":83,"column":10},"end":{"line":83,"column":11}},"loc":{"start":{"line":83,"column":27},"end":{"line":83,"column":70}},"line":83},"14":{"name":"(anonymous_14)","decl":{"start":{"line":95,"column":21},"end":{"line":95,"column":22}},"loc":{"start":{"line":95,"column":62},"end":{"line":101,"column":3}},"line":95},"15":{"name":"(anonymous_15)","decl":{"start":{"line":103,"column":23},"end":{"line":103,"column":24}},"loc":{"start":{"line":103,"column":64},"end":{"line":109,"column":3}},"line":103},"16":{"name":"(anonymous_16)","decl":{"start":{"line":111,"column":22},"end":{"line":111,"column":23}},"loc":{"start":{"line":111,"column":49},"end":{"line":119,"column":3}},"line":111},"17":{"name":"(anonymous_17)","decl":{"start":{"line":115,"column":51},"end":{"line":115,"column":52}},"loc":{"start":{"line":115,"column":61},"end":{"line":115,"column":89}},"line":115},"18":{"name":"(anonymous_18)","decl":{"start":{"line":130,"column":38},"end":{"line":130,"column":39}},"loc":{"start":{"line":130,"column":44},"end":{"line":156,"column":3}},"line":130},"19":{"name":"(anonymous_19)","decl":{"start":{"line":159,"column":26},"end":{"line":159,"column":27}},"loc":{"start":{"line":159,"column":73},"end":{"line":176,"column":3}},"line":159},"20":{"name":"(anonymous_20)","decl":{"start":{"line":162,"column":55},"end":{"line":162,"column":56}},"loc":{"start":{"line":162,"column":62},"end":{"line":162,"column":91}},"line":162},"21":{"name":"(anonymous_21)","decl":{"start":{"line":178,"column":21},"end":{"line":178,"column":22}},"loc":{"start":{"line":178,"column":27},"end":{"line":183,"column":3}},"line":178},"22":{"name":"(anonymous_22)","decl":{"start":{"line":185,"column":21},"end":{"line":185,"column":22}},"loc":{"start":{"line":185,"column":27},"end":{"line":191,"column":3}},"line":185},"23":{"name":"(anonymous_23)","decl":{"start":{"line":193,"column":33},"end":{"line":193,"column":34}},"loc":{"start":{"line":193,"column":40},"end":{"line":274,"column":3}},"line":193},"24":{"name":"(anonymous_24)","decl":{"start":{"line":195,"column":15},"end":{"line":195,"column":16}},"loc":{"start":{"line":195,"column":24},"end":{"line":272,"column":5}},"line":195},"25":{"name":"(anonymous_25)","decl":{"start":{"line":247,"column":12},"end":{"line":247,"column":13}},"loc":{"start":{"line":247,"column":19},"end":{"line":247,"column":42}},"line":247},"26":{"name":"(anonymous_26)","decl":{"start":{"line":273,"column":13},"end":{"line":273,"column":14}},"loc":{"start":{"line":273,"column":19},"end":{"line":273,"column":21}},"line":273},"27":{"name":"(anonymous_27)","decl":{"start":{"line":302,"column":68},"end":{"line":302,"column":69}},"loc":{"start":{"line":303,"column":4},"end":{"line":309,"column":6}},"line":303}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},"type":"if","locations":[{"start":{"line":43,"column":4},"end":{"line":45,"column":5}},{"start":{"line":43,"column":4},"end":{"line":45,"column":5}}],"line":43},"1":{"loc":{"start":{"line":48,"column":4},"end":{"line":55,"column":5}},"type":"if","locations":[{"start":{"line":48,"column":4},"end":{"line":55,"column":5}},{"start":{"line":48,"column":4},"end":{"line":55,"column":5}}],"line":48},"2":{"loc":{"start":{"line":60,"column":4},"end":{"line":67,"column":5}},"type":"if","locations":[{"start":{"line":60,"column":4},"end":{"line":67,"column":5}},{"start":{"line":60,"column":4},"end":{"line":67,"column":5}}],"line":60},"3":{"loc":{"start":{"line":63,"column":8},"end":{"line":65,"column":9}},"type":"if","locations":[{"start":{"line":63,"column":8},"end":{"line":65,"column":9}},{"start":{"line":63,"column":8},"end":{"line":65,"column":9}}],"line":63},"4":{"loc":{"start":{"line":78,"column":4},"end":{"line":91,"column":5}},"type":"if","locations":[{"start":{"line":78,"column":4},"end":{"line":91,"column":5}},{"start":{"line":78,"column":4},"end":{"line":91,"column":5}}],"line":78},"5":{"loc":{"start":{"line":81,"column":6},"end":{"line":89,"column":7}},"type":"if","locations":[{"start":{"line":81,"column":6},"end":{"line":89,"column":7}},{"start":{"line":81,"column":6},"end":{"line":89,"column":7}}],"line":81},"6":{"loc":{"start":{"line":81,"column":10},"end":{"line":81,"column":41}},"type":"binary-expr","locations":[{"start":{"line":81,"column":10},"end":{"line":81,"column":19}},{"start":{"line":81,"column":23},"end":{"line":81,"column":41}}],"line":81},"7":{"loc":{"start":{"line":86,"column":8},"end":{"line":88,"column":9}},"type":"if","locations":[{"start":{"line":86,"column":8},"end":{"line":88,"column":9}},{"start":{"line":86,"column":8},"end":{"line":88,"column":9}}],"line":86},"8":{"loc":{"start":{"line":106,"column":4},"end":{"line":108,"column":5}},"type":"if","locations":[{"start":{"line":106,"column":4},"end":{"line":108,"column":5}},{"start":{"line":106,"column":4},"end":{"line":108,"column":5}}],"line":106},"9":{"loc":{"start":{"line":106,"column":8},"end":{"line":106,"column":76}},"type":"binary-expr","locations":[{"start":{"line":106,"column":8},"end":{"line":106,"column":17}},{"start":{"line":106,"column":22},"end":{"line":106,"column":26}},{"start":{"line":106,"column":31},"end":{"line":106,"column":45}},{"start":{"line":106,"column":49},"end":{"line":106,"column":74}}],"line":106},"10":{"loc":{"start":{"line":115,"column":24},"end":{"line":115,"column":44}},"type":"binary-expr","locations":[{"start":{"line":115,"column":24},"end":{"line":115,"column":38}},{"start":{"line":115,"column":42},"end":{"line":115,"column":44}}],"line":115},"11":{"loc":{"start":{"line":131,"column":4},"end":{"line":137,"column":5}},"type":"if","locations":[{"start":{"line":131,"column":4},"end":{"line":137,"column":5}},{"start":{"line":131,"column":4},"end":{"line":137,"column":5}}],"line":131},"12":{"loc":{"start":{"line":146,"column":6},"end":{"line":152,"column":7}},"type":"if","locations":[{"start":{"line":146,"column":6},"end":{"line":152,"column":7}},{"start":{"line":146,"column":6},"end":{"line":152,"column":7}}],"line":146},"13":{"loc":{"start":{"line":161,"column":27},"end":{"line":161,"column":74}},"type":"binary-expr","locations":[{"start":{"line":161,"column":27},"end":{"line":161,"column":68}},{"start":{"line":161,"column":72},"end":{"line":161,"column":74}}],"line":161},"14":{"loc":{"start":{"line":170,"column":6},"end":{"line":172,"column":7}},"type":"if","locations":[{"start":{"line":170,"column":6},"end":{"line":172,"column":7}},{"start":{"line":170,"column":6},"end":{"line":172,"column":7}}],"line":170},"15":{"loc":{"start":{"line":179,"column":4},"end":{"line":181,"column":5}},"type":"if","locations":[{"start":{"line":179,"column":4},"end":{"line":181,"column":5}},{"start":{"line":179,"column":4},"end":{"line":181,"column":5}}],"line":179},"16":{"loc":{"start":{"line":188,"column":4},"end":{"line":190,"column":5}},"type":"if","locations":[{"start":{"line":188,"column":4},"end":{"line":190,"column":5}},{"start":{"line":188,"column":4},"end":{"line":190,"column":5}}],"line":188},"17":{"loc":{"start":{"line":198,"column":6},"end":{"line":271,"column":7}},"type":"switch","locations":[{"start":{"line":200,"column":8},"end":{"line":200,"column":24}},{"start":{"line":201,"column":8},"end":{"line":218,"column":9}},{"start":{"line":220,"column":8},"end":{"line":227,"column":9}},{"start":{"line":229,"column":8},"end":{"line":236,"column":9}},{"start":{"line":238,"column":8},"end":{"line":241,"column":9}},{"start":{"line":244,"column":8},"end":{"line":261,"column":9}},{"start":{"line":264,"column":8},"end":{"line":270,"column":9}}],"line":198},"18":{"loc":{"start":{"line":203,"column":10},"end":{"line":207,"column":11}},"type":"if","locations":[{"start":{"line":203,"column":10},"end":{"line":207,"column":11}},{"start":{"line":203,"column":10},"end":{"line":207,"column":11}}],"line":203},"19":{"loc":{"start":{"line":205,"column":17},"end":{"line":207,"column":11}},"type":"if","locations":[{"start":{"line":205,"column":17},"end":{"line":207,"column":11}},{"start":{"line":205,"column":17},"end":{"line":207,"column":11}}],"line":205},"20":{"loc":{"start":{"line":209,"column":10},"end":{"line":215,"column":11}},"type":"if","locations":[{"start":{"line":209,"column":10},"end":{"line":215,"column":11}},{"start":{"line":209,"column":10},"end":{"line":215,"column":11}}],"line":209},"21":{"loc":{"start":{"line":212,"column":12},"end":{"line":214,"column":13}},"type":"if","locations":[{"start":{"line":212,"column":12},"end":{"line":214,"column":13}},{"start":{"line":212,"column":12},"end":{"line":214,"column":13}}],"line":212},"22":{"loc":{"start":{"line":221,"column":10},"end":{"line":225,"column":11}},"type":"if","locations":[{"start":{"line":221,"column":10},"end":{"line":225,"column":11}},{"start":{"line":221,"column":10},"end":{"line":225,"column":11}}],"line":221},"23":{"loc":{"start":{"line":230,"column":10},"end":{"line":234,"column":11}},"type":"if","locations":[{"start":{"line":230,"column":10},"end":{"line":234,"column":11}},{"start":{"line":230,"column":10},"end":{"line":234,"column":11}}],"line":230},"24":{"loc":{"start":{"line":252,"column":10},"end":{"line":254,"column":11}},"type":"if","locations":[{"start":{"line":252,"column":10},"end":{"line":254,"column":11}},{"start":{"line":252,"column":10},"end":{"line":254,"column":11}}],"line":252},"25":{"loc":{"start":{"line":252,"column":14},"end":{"line":252,"column":48}},"type":"binary-expr","locations":[{"start":{"line":252,"column":14},"end":{"line":252,"column":22}},{"start":{"line":252,"column":26},"end":{"line":252,"column":40}},{"start":{"line":252,"column":44},"end":{"line":252,"column":48}}],"line":252},"26":{"loc":{"start":{"line":257,"column":10},"end":{"line":259,"column":11}},"type":"if","locations":[{"start":{"line":257,"column":10},"end":{"line":259,"column":11}},{"start":{"line":257,"column":10},"end":{"line":259,"column":11}}],"line":257},"27":{"loc":{"start":{"line":267,"column":10},"end":{"line":269,"column":11}},"type":"if","locations":[{"start":{"line":267,"column":10},"end":{"line":269,"column":11}},{"start":{"line":267,"column":10},"end":{"line":269,"column":11}}],"line":267},"28":{"loc":{"start":{"line":298,"column":30},"end":{"line":300,"column":30}},"type":"cond-expr","locations":[{"start":{"line":299,"column":6},"end":{"line":299,"column":19}},{"start":{"line":300,"column":6},"end":{"line":300,"column":30}}],"line":298}},"s":{"0":4,"1":136,"2":136,"3":136,"4":136,"5":136,"6":136,"7":22,"8":21,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":136,"19":40,"20":1,"21":1,"22":1,"23":1,"24":0,"25":0,"26":0,"27":136,"28":39,"29":136,"30":39,"31":136,"32":136,"33":57,"34":40,"35":40,"36":11,"37":35,"38":11,"39":10,"40":40,"41":136,"42":22,"43":22,"44":22,"45":136,"46":7,"47":7,"48":6,"49":136,"50":157,"51":157,"52":157,"53":90,"54":103,"55":157,"56":136,"57":136,"58":105,"59":6,"60":99,"61":99,"62":99,"63":157,"64":157,"65":147,"66":10,"67":99,"68":136,"69":7,"70":7,"71":7,"72":12,"73":7,"74":7,"75":7,"76":7,"77":7,"78":7,"79":7,"80":0,"81":136,"82":4,"83":2,"84":4,"85":136,"86":5,"87":5,"88":5,"89":5,"90":136,"91":136,"92":17,"93":17,"94":2,"95":2,"96":0,"97":2,"98":2,"99":2,"100":2,"101":2,"102":2,"103":2,"104":2,"105":3,"106":0,"107":3,"108":3,"109":5,"110":0,"111":5,"112":5,"113":1,"114":1,"115":1,"116":1,"117":1,"118":1,"119":1,"120":1,"121":1,"122":0,"123":1,"124":4,"125":4,"126":4,"127":136,"128":136,"129":136,"130":136,"131":217,"132":136},"f":{"0":136,"1":22,"2":1,"3":1,"4":1,"5":40,"6":1,"7":1,"8":0,"9":0,"10":39,"11":39,"12":57,"13":35,"14":22,"15":7,"16":157,"17":103,"18":105,"19":7,"20":12,"21":4,"22":5,"23":136,"24":17,"25":1,"26":0,"27":217},"b":{"0":[21,1],"1":[1,0],"2":[1,39],"3":[0,1],"4":[40,17],"5":[11,29],"6":[40,40],"7":[10,1],"8":[6,1],"9":[7,7,2,2],"10":[90,0],"11":[6,99],"12":[147,10],"13":[7,0],"14":[7,0],"15":[2,2],"16":[5,0],"17":[0,2,3,5,1,1,4],"18":[0,2],"19":[2,0],"20":[2,0],"21":[2,0],"22":[0,3],"23":[0,5],"24":[1,0],"25":[1,1,1],"26":[0,1],"27":[4,0],"28":[131,5]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"a24d58c779c7494f0d0efa20f10a71a5b3ff944c"} -,"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchConfig.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchConfig.ts","statementMap":{"0":{"start":{"line":7,"column":2},"end":{"line":25,"column":19}},"1":{"start":{"line":8,"column":4},"end":{"line":10,"column":5}},"2":{"start":{"line":9,"column":6},"end":{"line":9,"column":25}},"3":{"start":{"line":12,"column":39},"end":{"line":15,"column":5}},"4":{"start":{"line":17,"column":4},"end":{"line":22,"column":5}},"5":{"start":{"line":18,"column":6},"end":{"line":21,"column":8}},"6":{"start":{"line":24,"column":4},"end":{"line":24,"column":32}}},"fnMap":{"0":{"name":"useSearchConfig","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":39}},"loc":{"start":{"line":6,"column":82},"end":{"line":26,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":50},"end":{"line":7,"column":51}},"loc":{"start":{"line":7,"column":56},"end":{"line":25,"column":3}},"line":7}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":4},"end":{"line":10,"column":5}},"type":"if","locations":[{"start":{"line":8,"column":4},"end":{"line":10,"column":5}},{"start":{"line":8,"column":4},"end":{"line":10,"column":5}}],"line":8},"1":{"loc":{"start":{"line":17,"column":4},"end":{"line":22,"column":5}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":22,"column":5}},{"start":{"line":17,"column":4},"end":{"line":22,"column":5}}],"line":17},"2":{"loc":{"start":{"line":17,"column":8},"end":{"line":17,"column":52}},"type":"binary-expr","locations":[{"start":{"line":17,"column":8},"end":{"line":17,"column":18}},{"start":{"line":17,"column":22},"end":{"line":17,"column":52}}],"line":17}},"s":{"0":51,"1":35,"2":30,"3":5,"4":5,"5":3,"6":5},"f":{"0":51,"1":35},"b":{"0":[30,5],"1":[3,2],"2":[5,5]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"b7f498d195a1a4fd1389a8a3c1eeb82cc55a66ef"} -,"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchResult.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchResult.ts","statementMap":{"0":{"start":{"line":6,"column":48},"end":{"line":7,"column":86}},"1":{"start":{"line":7,"column":2},"end":{"line":7,"column":86}},"2":{"start":{"line":7,"column":22},"end":{"line":7,"column":85}},"3":{"start":{"line":9,"column":48},"end":{"line":10,"column":52}},"4":{"start":{"line":10,"column":2},"end":{"line":10,"column":52}},"5":{"start":{"line":10,"column":18},"end":{"line":10,"column":39}},"6":{"start":{"line":23,"column":4},"end":{"line":23,"column":16}},"7":{"start":{"line":26,"column":23},"end":{"line":26,"column":59}},"8":{"start":{"line":27,"column":23},"end":{"line":27,"column":59}},"9":{"start":{"line":31,"column":29},"end":{"line":74,"column":91}},"10":{"start":{"line":32,"column":4},"end":{"line":34,"column":5}},"11":{"start":{"line":33,"column":6},"end":{"line":33,"column":16}},"12":{"start":{"line":37,"column":24},"end":{"line":44,"column":6}},"13":{"start":{"line":38,"column":26},"end":{"line":38,"column":75}},"14":{"start":{"line":39,"column":31},"end":{"line":39,"column":59}},"15":{"start":{"line":39,"column":50},"end":{"line":39,"column":58}},"16":{"start":{"line":40,"column":6},"end":{"line":43,"column":8}},"17":{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},"18":{"start":{"line":48,"column":6},"end":{"line":48,"column":80}},"19":{"start":{"line":48,"column":59},"end":{"line":48,"column":78}},"20":{"start":{"line":52,"column":47},"end":{"line":52,"column":49}},"21":{"start":{"line":53,"column":4},"end":{"line":64,"column":5}},"22":{"start":{"line":53,"column":17},"end":{"line":53,"column":18}},"23":{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},"24":{"start":{"line":56,"column":8},"end":{"line":56,"column":14}},"25":{"start":{"line":60,"column":21},"end":{"line":60,"column":37}},"26":{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},"27":{"start":{"line":62,"column":8},"end":{"line":62,"column":34}},"28":{"start":{"line":67,"column":4},"end":{"line":71,"column":5}},"29":{"start":{"line":68,"column":6},"end":{"line":70,"column":9}},"30":{"start":{"line":69,"column":8},"end":{"line":69,"column":98}},"31":{"start":{"line":73,"column":4},"end":{"line":73,"column":24}},"32":{"start":{"line":78,"column":18},"end":{"line":86,"column":76}},"33":{"start":{"line":79,"column":4},"end":{"line":85,"column":7}},"34":{"start":{"line":80,"column":20},"end":{"line":80,"column":86}},"35":{"start":{"line":81,"column":6},"end":{"line":84,"column":8}},"36":{"start":{"line":88,"column":2},"end":{"line":88,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":48},"end":{"line":6,"column":49}},"loc":{"start":{"line":7,"column":2},"end":{"line":7,"column":86}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":7,"column":15},"end":{"line":7,"column":16}},"loc":{"start":{"line":7,"column":22},"end":{"line":7,"column":85}},"line":7},"2":{"name":"(anonymous_2)","decl":{"start":{"line":9,"column":48},"end":{"line":9,"column":49}},"loc":{"start":{"line":10,"column":2},"end":{"line":10,"column":52}},"line":10},"3":{"name":"(anonymous_3)","decl":{"start":{"line":10,"column":11},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":18},"end":{"line":10,"column":39}},"line":10},"4":{"name":"useSearchResult","decl":{"start":{"line":21,"column":24},"end":{"line":21,"column":39}},"loc":{"start":{"line":21,"column":74},"end":{"line":89,"column":1}},"line":21},"5":{"name":"(anonymous_5)","decl":{"start":{"line":31,"column":43},"end":{"line":31,"column":44}},"loc":{"start":{"line":31,"column":49},"end":{"line":74,"column":3}},"line":31},"6":{"name":"(anonymous_6)","decl":{"start":{"line":37,"column":43},"end":{"line":37,"column":44}},"loc":{"start":{"line":37,"column":53},"end":{"line":44,"column":5}},"line":37},"7":{"name":"(anonymous_7)","decl":{"start":{"line":39,"column":43},"end":{"line":39,"column":44}},"loc":{"start":{"line":39,"column":50},"end":{"line":39,"column":58}},"line":39},"8":{"name":"(anonymous_8)","decl":{"start":{"line":48,"column":43},"end":{"line":48,"column":44}},"loc":{"start":{"line":48,"column":59},"end":{"line":48,"column":78}},"line":48},"9":{"name":"(anonymous_9)","decl":{"start":{"line":68,"column":24},"end":{"line":68,"column":25}},"loc":{"start":{"line":68,"column":34},"end":{"line":70,"column":7}},"line":68},"10":{"name":"(anonymous_10)","decl":{"start":{"line":78,"column":32},"end":{"line":78,"column":33}},"loc":{"start":{"line":78,"column":38},"end":{"line":86,"column":3}},"line":78},"11":{"name":"(anonymous_11)","decl":{"start":{"line":79,"column":34},"end":{"line":79,"column":35}},"loc":{"start":{"line":79,"column":68},"end":{"line":85,"column":5}},"line":79}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":23},"end":{"line":26,"column":59}},"type":"binary-expr","locations":[{"start":{"line":26,"column":23},"end":{"line":26,"column":42}},{"start":{"line":26,"column":46},"end":{"line":26,"column":59}}],"line":26},"1":{"loc":{"start":{"line":27,"column":23},"end":{"line":27,"column":59}},"type":"binary-expr","locations":[{"start":{"line":27,"column":23},"end":{"line":27,"column":42}},{"start":{"line":27,"column":46},"end":{"line":27,"column":59}}],"line":27},"2":{"loc":{"start":{"line":32,"column":4},"end":{"line":34,"column":5}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":34,"column":5}},{"start":{"line":32,"column":4},"end":{"line":34,"column":5}}],"line":32},"3":{"loc":{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":49,"column":5}},{"start":{"line":47,"column":4},"end":{"line":49,"column":5}}],"line":47},"4":{"loc":{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},"type":"if","locations":[{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},{"start":{"line":55,"column":6},"end":{"line":57,"column":7}}],"line":55},"5":{"loc":{"start":{"line":55,"column":10},"end":{"line":55,"column":73}},"type":"binary-expr","locations":[{"start":{"line":55,"column":10},"end":{"line":55,"column":28}},{"start":{"line":55,"column":32},"end":{"line":55,"column":73}}],"line":55},"6":{"loc":{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},"type":"if","locations":[{"start":{"line":61,"column":6},"end":{"line":63,"column":7}},{"start":{"line":61,"column":6},"end":{"line":63,"column":7}}],"line":61},"7":{"loc":{"start":{"line":67,"column":4},"end":{"line":71,"column":5}},"type":"if","locations":[{"start":{"line":67,"column":4},"end":{"line":71,"column":5}},{"start":{"line":67,"column":4},"end":{"line":71,"column":5}}],"line":67}},"s":{"0":4,"1":19,"2":38,"3":4,"4":10,"5":29,"6":136,"7":136,"8":136,"9":136,"10":39,"11":33,"12":6,"13":30,"14":30,"15":60,"16":30,"17":6,"18":5,"19":25,"20":6,"21":6,"22":6,"23":20,"24":1,"25":19,"26":19,"27":12,"28":6,"29":1,"30":1,"31":6,"32":136,"33":39,"34":12,"35":12,"36":136},"f":{"0":19,"1":38,"2":10,"3":29,"4":136,"5":39,"6":30,"7":60,"8":25,"9":1,"10":39,"11":12},"b":{"0":[136,136],"1":[136,133],"2":[33,6],"3":[5,1],"4":[1,19],"5":[20,17],"6":[12,7],"7":[1,5]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"c8946832dc9d134c9c7486264bfdd797ebbfd0a4"} -,"/Users/jilin/projects/antd/rc-cascader/src/hooks/useUpdateEffect.ts": {"path":"/Users/jilin/projects/antd/rc-cascader/src/hooks/useUpdateEffect.ts","statementMap":{"0":{"start":{"line":7,"column":21},"end":{"line":7,"column":39}},"1":{"start":{"line":9,"column":2},"end":{"line":17,"column":16}},"2":{"start":{"line":10,"column":4},"end":{"line":13,"column":5}},"3":{"start":{"line":11,"column":6},"end":{"line":11,"column":33}},"4":{"start":{"line":12,"column":6},"end":{"line":12,"column":13}},"5":{"start":{"line":16,"column":4},"end":{"line":16,"column":21}}},"fnMap":{"0":{"name":"useUpdateEffect","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":39}},"loc":{"start":{"line":6,"column":80},"end":{"line":18,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":9,"column":18},"end":{"line":9,"column":19}},"loc":{"start":{"line":9,"column":24},"end":{"line":17,"column":3}},"line":9}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":4},"end":{"line":13,"column":5}},"type":"if","locations":[{"start":{"line":10,"column":4},"end":{"line":13,"column":5}},{"start":{"line":10,"column":4},"end":{"line":13,"column":5}}],"line":10}},"s":{"0":51,"1":51,"2":36,"3":35,"4":35,"5":1},"f":{"0":51,"1":36},"b":{"0":[35,1]},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"d54101987d46e9479ee1cea1d2c8df92961e3b0e"} -} diff --git a/coverage/lcov-report/block-navigation.js b/coverage/lcov-report/block-navigation.js deleted file mode 100644 index c7ff5a5c..00000000 --- a/coverage/lcov-report/block-navigation.js +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable */ -var jumpToCode = (function init() { - // Classes of code we would like to highlight in the file view - var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; - - // Elements to highlight in the file listing view - var fileListingElements = ['td.pct.low']; - - // We don't want to select elements that are direct descendants of another match - var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` - - // Selecter that finds elements on the page to which we can jump - var selector = - fileListingElements.join(', ') + - ', ' + - notSelector + - missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` - - // The NodeList of matching elements - var missingCoverageElements = document.querySelectorAll(selector); - - var currentIndex; - - function toggleClass(index) { - missingCoverageElements - .item(currentIndex) - .classList.remove('highlighted'); - missingCoverageElements.item(index).classList.add('highlighted'); - } - - function makeCurrent(index) { - toggleClass(index); - currentIndex = index; - missingCoverageElements.item(index).scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center' - }); - } - - function goToPrevious() { - var nextIndex = 0; - if (typeof currentIndex !== 'number' || currentIndex === 0) { - nextIndex = missingCoverageElements.length - 1; - } else if (missingCoverageElements.length > 1) { - nextIndex = currentIndex - 1; - } - - makeCurrent(nextIndex); - } - - function goToNext() { - var nextIndex = 0; - - if ( - typeof currentIndex === 'number' && - currentIndex < missingCoverageElements.length - 1 - ) { - nextIndex = currentIndex + 1; - } - - makeCurrent(nextIndex); - } - - return function jump(event) { - switch (event.which) { - case 78: // n - case 74: // j - goToNext(); - break; - case 66: // b - case 75: // k - case 80: // p - goToPrevious(); - break; - } - }; -})(); -window.addEventListener('keydown', jumpToCode); diff --git a/coverage/lcov-report/index.html b/coverage/lcov-report/index.html deleted file mode 100644 index aa627ca0..00000000 --- a/coverage/lcov-report/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - Code coverage report for All files - - - - - - - - -
      -
      -

      All files

      -
      - -
      - 95.3% - Statements - 284/298 -
      - - -
      - 79.52% - Branches - 132/166 -
      - - -
      - 91.14% - Functions - 72/79 -
      - - -
      - 95.99% - Lines - 263/274 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FileStatementsBranchesFunctionsLines
      src -
      -
      94.25%82/8779.07%34/4388.89%24/2795.12%78/82
      src/OptionList -
      -
      94.41%152/16174.75%74/9988.89%32/3695.24%140/147
      src/hooks -
      -
      100%50/50100%24/24100%16/16100%45/45
      -
      -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/prettify.js b/coverage/lcov-report/prettify.js deleted file mode 100644 index b3225238..00000000 --- a/coverage/lcov-report/prettify.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable */ -window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/coverage/lcov-report/sort-arrow-sprite.png b/coverage/lcov-report/sort-arrow-sprite.png deleted file mode 100644 index 03f704a609c6fd0dbfdac63466a7d7c958b5cbf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jii$m5978H@?Fn+^JD|Y9yzj{W`447Gxa{7*dM7nnnD-Lb z6^}Hx2)'; - } - } - return cols; - } - // attaches a data attribute to every tr element with an object - // of data values keyed by column name - function loadRowData(tableRow) { - var tableCols = tableRow.querySelectorAll('td'), - colNode, - col, - data = {}, - i, - val; - for (i = 0; i < tableCols.length; i += 1) { - colNode = tableCols[i]; - col = cols[i]; - val = colNode.getAttribute('data-value'); - if (col.type === 'number') { - val = Number(val); - } - data[col.key] = val; - } - return data; - } - // loads all row data - function loadData() { - var rows = getTableBody().querySelectorAll('tr'), - i; - - for (i = 0; i < rows.length; i += 1) { - rows[i].data = loadRowData(rows[i]); - } - } - // sorts the table using the data for the ith column - function sortByIndex(index, desc) { - var key = cols[index].key, - sorter = function(a, b) { - a = a.data[key]; - b = b.data[key]; - return a < b ? -1 : a > b ? 1 : 0; - }, - finalSorter = sorter, - tableBody = document.querySelector('.coverage-summary tbody'), - rowNodes = tableBody.querySelectorAll('tr'), - rows = [], - i; - - if (desc) { - finalSorter = function(a, b) { - return -1 * sorter(a, b); - }; - } - - for (i = 0; i < rowNodes.length; i += 1) { - rows.push(rowNodes[i]); - tableBody.removeChild(rowNodes[i]); - } - - rows.sort(finalSorter); - - for (i = 0; i < rows.length; i += 1) { - tableBody.appendChild(rows[i]); - } - } - // removes sort indicators for current column being sorted - function removeSortIndicators() { - var col = getNthColumn(currentSort.index), - cls = col.className; - - cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); - col.className = cls; - } - // adds sort indicators for current column being sorted - function addSortIndicators() { - getNthColumn(currentSort.index).className += currentSort.desc - ? ' sorted-desc' - : ' sorted'; - } - // adds event listeners for all sorter widgets - function enableUI() { - var i, - el, - ithSorter = function ithSorter(i) { - var col = cols[i]; - - return function() { - var desc = col.defaultDescSort; - - if (currentSort.index === i) { - desc = !currentSort.desc; - } - sortByIndex(i, desc); - removeSortIndicators(); - currentSort.index = i; - currentSort.desc = desc; - addSortIndicators(); - }; - }; - for (i = 0; i < cols.length; i += 1) { - if (cols[i].sortable) { - // add the click event handler on the th so users - // dont have to click on those tiny arrows - el = getNthColumn(i).querySelector('.sorter').parentElement; - if (el.addEventListener) { - el.addEventListener('click', ithSorter(i)); - } else { - el.attachEvent('onclick', ithSorter(i)); - } - } - } - } - // adds sorting functionality to the UI - return function() { - if (!getTable()) { - return; - } - cols = loadColumns(); - loadData(); - addSortIndicators(); - enableUI(); - }; -})(); - -window.addEventListener('load', addSorting); diff --git a/coverage/lcov-report/src/Cascader.tsx.html b/coverage/lcov-report/src/Cascader.tsx.html deleted file mode 100644 index 8a152bf2..00000000 --- a/coverage/lcov-report/src/Cascader.tsx.html +++ /dev/null @@ -1,1087 +0,0 @@ - - - - - - Code coverage report for src/Cascader.tsx - - - - - - - - -
      -
      -

      All files / src Cascader.tsx

      -
      - -
      - 92.42% - Statements - 61/66 -
      - - -
      - 75% - Branches - 24/32 -
      - - -
      - 85.71% - Functions - 18/21 -
      - - -
      - 93.44% - Lines - 57/61 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -51x -  -51x -  -51x -  -  -51x -  -51x -  -  -  -  -  -  -  -  -51x -7x -  -  -51x -  -  -  -  -51x -  -  -51x -35x -  -  -  -  -  -  -  -  -51x -  -  -36x -36x -9x -  -  -36x -  -  -51x -35x -  -  -51x -1x -  -  -  -51x -13x -  -13x -  -  -  -13x -32x -  -  -  -  -51x -  -7x -  -7x -  -7x -7x -  -7x -  -7x -7x -17x -  -17x -7x -  -  -  -7x -6x -  -  -7x -6x -  -  -  -  -6x -  -  -  -  -  -51x -51x -  -  -  -51x -  -  -51x -  -51x -50x -50x -  -  -  -51x -35x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -51x -  -  -  -  -  -  -  -51x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -12x -  -  -  -  -151x -  -  -  -  -  -  -4x -  -  - 
      import * as React from 'react';
      -import warning from 'rc-util/lib/warning';
      -import useMergedState from 'rc-util/lib/hooks/useMergedState';
      -import type { TreeSelectProps } from 'rc-tree-select';
      -import generate from 'rc-tree-select/lib/generate';
      -import type { FlattenDataNode } from 'rc-tree-select/lib/interface';
      -import { fillFieldNames } from 'rc-tree-select/lib/utils/valueUtil';
      -import type { RefSelectProps } from 'rc-select/lib/generate';
      -import OptionList from './OptionList';
      -import type { CascaderValueType, DataNode, FieldNames, ShowSearchType } from './interface';
      -import CascaderContext from './context';
      -import { connectValue, convertOptions, restoreCompatibleValue } from './util';
      -import useUpdateEffect from './hooks/useUpdateEffect';
      -import useSearchConfig from './hooks/useSearchConfig';
      - 
      -/**
      - * `rc-cascader` is much like `rc-tree-select` but API is very different.
      - * It's caused that component developer is not same person
      - * and we do not rice the API naming standard at that time.
      - *
      - * To avoid breaking change, wrap the `rc-tree-select` to compatible with `rc-cascader` API.
      - * This should be better to merge to same API like `rc-tree-select` or `rc-select` in next major version.
      - *
      - * Update:
      - * - dropdown class change to `rc-cascader-dropdown`
      - * - direction rtl keyboard
      - *
      - * Deprecated:
      - * - popupVisible
      - * - onPopupVisibleChange
      - *
      - * Removed:
      - * - builtinPlacements: Handle by select
      - */
      - 
      -const RefCascader = generate({
      -  prefixCls: 'rc-cascader',
      -  optionList: OptionList,
      -});
      - 
      -// ====================================== Wrap ======================================
      -interface BaseCascaderProps
      -  extends Omit<
      -    TreeSelectProps,
      -    | 'value'
      -    | 'defaultValue'
      -    | 'filterTreeNode'
      -    | 'labelInValue'
      -    | 'loadData'
      -    | 'showCheckedStrategy'
      -    | 'showSearch'
      -    | 'treeCheckable'
      -    | 'treeCheckStrictly'
      -    | 'treeDataSimpleMode'
      -    | 'treeNodeFilterProp'
      -    | 'treeNodeLabelProp'
      -    | 'treeDefaultExpandAll'
      -    | 'treeDefaultExpandedKeys'
      -    | 'treeExpandedKeys'
      -    | 'treeIcon'
      -    | 'onChange'
      -  > {
      -  options?: DataNode[];
      -  children?: React.ReactElement;
      - 
      -  // Value
      -  value?: CascaderValueType | CascaderValueType[];
      -  defaultValue?: CascaderValueType | CascaderValueType[];
      -  changeOnSelect?: boolean;
      -  allowClear?: boolean;
      -  disabled?: boolean;
      - 
      -  fieldNames?: FieldNames;
      - 
      -  // Search
      -  showSearch?: boolean | ShowSearchType;
      -  searchValue?: string;
      -  onSearch?: (search: string) => void;
      - 
      -  // Open
      -  /** @deprecated Use `open` instead */
      -  popupVisible?: boolean;
      - 
      -  /** @deprecated Use `onDropdownVisibleChange` instead */
      -  onPopupVisibleChange?: (open: boolean) => void;
      -  onDropdownVisibleChange?: (open: boolean) => void;
      - 
      -  // Trigger
      -  expandTrigger?: 'hover' | 'click';
      - 
      -  dropdownMenuColumnStyle?: React.CSSProperties;
      -  loadData?: (selectOptions: DataNode[]) => void;
      - 
      -  expandIcon?: React.ReactNode;
      -  loadingIcon?: React.ReactNode;
      -}
      - 
      -type OnSingleChange = (value: CascaderValueType, selectOptions: DataNode[]) => void;
      -type OnMultipleChange = (value: CascaderValueType[], selectOptions: DataNode[][]) => void;
      - 
      -interface SingleCascaderProps extends BaseCascaderProps {
      -  multiple?: false;
      - 
      -  onChange?: OnSingleChange;
      -}
      - 
      -interface MultipleCascaderProps extends BaseCascaderProps {
      -  multiple: true;
      - 
      -  onChange?: OnMultipleChange;
      -}
      - 
      -export type CascaderProps = SingleCascaderProps | MultipleCascaderProps;
      - 
      -interface CascaderRef {
      -  focus: () => void;
      -  blur: () => void;
      -}
      - 
      -const Cascader = React.forwardRef((props: CascaderProps, ref: React.Ref<CascaderRef>) => {
      -  const {
      -    changeOnSelect,
      -    children,
      -    options,
      -    onChange,
      -    value,
      -    defaultValue,
      - 
      -    popupVisible,
      -    open,
      -    onDropdownVisibleChange,
      -    onPopupVisibleChange,
      - 
      -    searchValue,
      -    onSearch,
      -    showSearch,
      - 
      -    expandTrigger,
      -    expandIcon = '>',
      -    loadingIcon,
      - 
      -    loadData,
      -    dropdownMenuColumnStyle,
      - 
      -    ...restProps
      -  } = props;
      - 
      -  const { multiple, fieldNames } = restProps;
      - 
      -  const mergedFieldNames = React.useMemo(() => fillFieldNames(fieldNames), [fieldNames]);
      - 
      -  // ============================ Ref =============================
      -  const cascaderRef = React.useRef<RefSelectProps>();
      - 
      -  React.useImperativeHandle(ref, () => ({
      -    focus: () => {
      -      cascaderRef.current.focus();
      -    },
      -    blur: () => {
      -      cascaderRef.current.blur();
      -    },
      -  }));
      - 
      -  const getEntityByValue = (val: React.Key): FlattenDataNode =>
      -    (cascaderRef.current as any).getEntityByValue(val);
      - 
      -  // =========================== Search ===========================
      -  const [mergedSearch, setMergedSearch] = useMergedState(undefined, {
      -    value: searchValue,
      -    onChange: onSearch,
      -  });
      - 
      -  const [mergedShowSearch, searchConfig] = useSearchConfig(showSearch);
      - 
      -  // ========================== Options ===========================
      -  const mergedOptions = React.useMemo(() => {
      -    return convertOptions(options, mergedFieldNames);
      -  }, [options, mergedFieldNames]);
      - 
      -  // =========================== Value ============================
      -  /**
      -   * Always pass props value to last value unit:
      -   * - single: ['light', 'little'] => ['light__little']
      -   * - multiple: [['light', 'little'], ['bamboo']] => ['light__little', 'bamboo']
      -   */
      -  const parseToInternalValue = (
      -    propValue?: CascaderValueType | CascaderValueType[],
      -  ): React.Key[] => {
      -    let propValueList: CascaderValueType[] = [];
      -    if (propValue) {
      -      propValueList = (multiple ? propValue : [propValue]) as CascaderValueType[];
      -    }
      - 
      -    return propValueList.map(connectValue);
      -  };
      - 
      -  const [internalValue, setInternalValue] = React.useState(() =>
      -    parseToInternalValue(value || defaultValue),
      -  );
      - 
      -  useUpdateEffect(() => {
      -    setInternalValue(parseToInternalValue(value));
      -  }, [value]);
      - 
      -  // =========================== Label ============================
      -  const labelRender = (entity: FlattenDataNode) => {
      -    const { label: fieldLabel } = mergedFieldNames;
      - 
      -    Iif (multiple) {
      -      return entity.data.node[fieldLabel];
      -    }
      - 
      -    return restoreCompatibleValue(entity, mergedFieldNames)
      -      .options.map(opt => opt[fieldLabel])
      -      .join('>');
      -  };
      - 
      -  // =========================== Change ===========================
      -  const onInternalChange = (newValue: any /** Not care current type */) => {
      -    // TODO: Need improve motion experience
      -    setMergedSearch('');
      - 
      -    const valueList = (multiple ? newValue : [newValue]) as React.Key[];
      - 
      -    const pathList: CascaderValueType[] = [];
      -    const optionsList: DataNode[][] = [];
      - 
      -    const valueEntities = valueList.map(getEntityByValue).filter(entity => entity);
      - 
      -    valueEntities.forEach(entity => {
      -      const { options: valueOptions } = restoreCompatibleValue(entity, mergedFieldNames);
      -      const originOptions = valueOptions.map(option => option.node);
      - 
      -      pathList.push(originOptions.map(opt => opt[mergedFieldNames.value]));
      -      optionsList.push(originOptions);
      -    });
      - 
      -    // Fill state
      -    if (value === undefined) {
      -      setInternalValue(valueList);
      -    }
      - 
      -    if (onChange) {
      -      Iif (multiple) {
      -        (onChange as OnMultipleChange)(pathList, optionsList);
      -      } else {
      -        // TODO: This should return null as other component.
      -        // But its a breaking change and we should keep the logic.
      -        (onChange as OnSingleChange)(pathList[0] || [], optionsList[0] || []);
      -      }
      -    }
      -  };
      - 
      -  // ============================ Open ============================
      -  Eif (process.env.NODE_ENV !== 'production') {
      -    warning(
      -      !onPopupVisibleChange,
      -      '`onPopupVisibleChange` is deprecated. Please use `onDropdownVisibleChange` instead.',
      -    );
      -    warning(popupVisible === undefined, '`popupVisible` is deprecated. Please use `open` instead.');
      -  }
      - 
      -  const mergedOpen = open !== undefined ? open : popupVisible;
      - 
      -  const onInternalDropdownVisibleChange = (nextVisible: boolean) => {
      -    onDropdownVisibleChange?.(nextVisible);
      -    onPopupVisibleChange?.(nextVisible);
      -  };
      - 
      -  // ========================== Context ===========================
      -  const context = React.useMemo(
      -    () => ({
      -      changeOnSelect,
      -      expandTrigger,
      -      fieldNames: mergedFieldNames,
      -      expandIcon,
      -      loadingIcon,
      -      loadData,
      -      dropdownMenuColumnStyle,
      -      search: searchConfig,
      -    }),
      -    [
      -      changeOnSelect,
      -      expandTrigger,
      -      mergedFieldNames,
      -      expandIcon,
      -      loadingIcon,
      -      loadData,
      -      dropdownMenuColumnStyle,
      -      searchConfig,
      -    ],
      -  );
      - 
      -  // =========================== Render ===========================
      -  const dropdownStyle: React.CSSProperties =
      -    // Search to match width
      -    (mergedSearch && searchConfig.matchInputWidth) ||
      -    // Empty keep the width
      -    !mergedOptions.length
      -      ? {}
      -      : {
      -          minWidth: 'auto',
      -        };
      - 
      -  return (
      -    <CascaderContext.Provider value={context}>
      -      <RefCascader
      -        ref={cascaderRef}
      -        {...restProps}
      -        value={multiple ? internalValue : internalValue[0]}
      -        dropdownMatchSelectWidth={false}
      -        dropdownStyle={dropdownStyle}
      -        treeData={mergedOptions}
      -        treeCheckable={multiple}
      -        treeNodeFilterProp="label"
      -        onChange={onInternalChange}
      -        showCheckedStrategy={RefCascader.SHOW_PARENT}
      -        open={mergedOpen}
      -        onDropdownVisibleChange={onInternalDropdownVisibleChange}
      -        searchValue={mergedSearch}
      -        // Customize filter logic in OptionList
      -        filterTreeNode={() => true}
      -        showSearch={mergedShowSearch}
      -        onSearch={setMergedSearch}
      -        labelRender={labelRender}
      -        {...{
      -          getRawInputElement: () => children,
      -        }}
      -      />
      -    </CascaderContext.Provider>
      -  );
      -});
      - 
      -Cascader.displayName = 'Cascader';
      - 
      -export default Cascader;
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/OptionList/Column.tsx.html b/coverage/lcov-report/src/OptionList/Column.tsx.html deleted file mode 100644 index 82fadb43..00000000 --- a/coverage/lcov-report/src/OptionList/Column.tsx.html +++ /dev/null @@ -1,490 +0,0 @@ - - - - - - Code coverage report for src/OptionList/Column.tsx - - - - - - - - -
      -
      -

      All files / src/OptionList Column.tsx

      -
      - -
      - 96.43% - Statements - 27/28 -
      - - -
      - 72.73% - Branches - 24/33 -
      - - -
      - 87.5% - Functions - 7/8 -
      - - -
      - 96.43% - Lines - 27/28 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -217x -217x -217x -  -  -217x -  -  -217x -  -  -493x -493x -  -493x -  -  -493x -  -  -493x -19x -15x -  -  -  -  -493x -15x -6x -  -  -  -  -  -493x -4x -489x -489x -  -  -  -493x -  -  -  -  -  -  -  -  -  -  -  -  -4x -4x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -15x -15x -15x -  -  -  -1x -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
      import * as React from 'react';
      -import classNames from 'classnames';
      -import type { OptionDataNode } from '../interface';
      -import { isLeaf } from '../util';
      -import CascaderContext from '../context';
      - 
      -export interface ColumnProps {
      -  prefixCls: string;
      -  index: number;
      -  multiple?: boolean;
      -  options: OptionDataNode[];
      -  /** Current Column opened item key */
      -  openKey?: React.Key;
      -  onSelect: (value: React.Key, isLeaf: boolean) => void;
      -  onOpen: (index: number, value: React.Key) => void;
      -  onToggleOpen: (open: boolean) => void;
      -  checkedSet: Set<React.Key>;
      -  halfCheckedSet: Set<React.Key>;
      -  loadingKeys: React.Key[];
      -}
      - 
      -export default function Column({
      -  prefixCls,
      -  index,
      -  multiple,
      -  options,
      -  openKey,
      -  onSelect,
      -  onOpen,
      -  onToggleOpen,
      -  checkedSet,
      -  halfCheckedSet,
      -  loadingKeys,
      -}: ColumnProps) {
      -  const menuPrefixCls = `${prefixCls}-menu`;
      -  const menuItemPrefixCls = `${prefixCls}-menu-item`;
      -  const checkboxPrefixCls = `${menuItemPrefixCls}-checkbox`;
      - 
      -  const { changeOnSelect, expandTrigger, expandIcon, loadingIcon, dropdownMenuColumnStyle } =
      -    React.useContext(CascaderContext);
      - 
      -  // ============================ Render ============================
      -  return (
      -    <ul className={menuPrefixCls} role="menu">
      -      {options.map(option => {
      -        const { disabled, value, node } = option;
      -        const isMergedLeaf = isLeaf(option);
      - 
      -        const isLoading = loadingKeys.includes(value);
      - 
      -        // >>>>> checked
      -        const checked = checkedSet.has(value);
      - 
      -        // >>>>> Open
      -        const triggerOpenPath = () => {
      -          if (!disabled && !isMergedLeaf) {
      -            onOpen(index, value);
      -          }
      -        };
      - 
      -        // >>>>> Selection
      -        const triggerSelect = () => {
      -          if (!disabled && (isMergedLeaf || changeOnSelect || multiple)) {
      -            onSelect(value, isMergedLeaf);
      -          }
      -        };
      - 
      -        // >>>>> Title
      -        let title: string;
      -        if (typeof node?.title === 'string') {
      -          title = node.title;
      -        } else Eif (typeof option.title === 'string') {
      -          title = option.title;
      -        }
      - 
      -        // >>>>> Render
      -        return (
      -          <li
      -            key={value}
      -            className={classNames(menuItemPrefixCls, {
      -              [`${menuItemPrefixCls}-active`]: openKey === value,
      -              // [`${menuItemPrefixCls}-selected`]: !multiple && checked,
      -              [`${menuItemPrefixCls}-disabled`]: disabled,
      -            })}
      -            style={dropdownMenuColumnStyle}
      -            role="menuitemcheckbox"
      -            title={title}
      -            aria-checked={checked}
      -            onMouseEnter={() => {
      -              Eif (expandTrigger) {
      -                triggerOpenPath();
      -              }
      -            }}
      -          >
      -            {multiple && (
      -              <span
      -                className={classNames(
      -                  checkboxPrefixCls,
      -                  {
      -                    [`${checkboxPrefixCls}-checked`]: checked,
      -                    [`${checkboxPrefixCls}-indeterminate`]: !checked && halfCheckedSet.has(value),
      -                  },
      -                  // (disabled || disableCheckbox) && `${prefixCls}-checkbox-disabled`,
      -                )}
      -                onClick={() => {
      -                  triggerSelect();
      -                }}
      -              />
      -            )}
      -            <div
      -              className={`${menuItemPrefixCls}-content`}
      -              onClick={() => {
      -                triggerOpenPath();
      -                Eif (!multiple || isMergedLeaf) {
      -                  triggerSelect();
      -                }
      -              }}
      -              onDoubleClick={() => {
      -                Eif (changeOnSelect) {
      -                  onToggleOpen(false);
      -                }
      -              }}
      -            >
      -              {option.title}
      -            </div>
      -            {!isLoading && expandIcon && !isMergedLeaf && (
      -              <div className={`${menuItemPrefixCls}-expand-icon`}>{expandIcon}</div>
      -            )}
      -            {isLoading && loadingIcon && (
      -              <div className={`${menuItemPrefixCls}-loading`}>{loadingIcon}</div>
      -            )}
      -          </li>
      -        );
      -      })}
      -    </ul>
      -  );
      -}
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/OptionList/index.html b/coverage/lcov-report/src/OptionList/index.html deleted file mode 100644 index 338d319b..00000000 --- a/coverage/lcov-report/src/OptionList/index.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - Code coverage report for src/OptionList - - - - - - - - -
      -
      -

      All files src/OptionList

      -
      - -
      - 94.41% - Statements - 152/161 -
      - - -
      - 74.75% - Branches - 74/99 -
      - - -
      - 88.89% - Functions - 32/36 -
      - - -
      - 95.24% - Lines - 140/147 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FileStatementsBranchesFunctionsLines
      Column.tsx -
      -
      96.43%27/2872.73%24/3387.5%7/896.43%27/28
      index.tsx -
      -
      93.98%125/13375.76%50/6689.29%25/2894.96%113/119
      -
      -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/OptionList/index.tsx.html b/coverage/lcov-report/src/OptionList/index.tsx.html deleted file mode 100644 index eef75283..00000000 --- a/coverage/lcov-report/src/OptionList/index.tsx.html +++ /dev/null @@ -1,1057 +0,0 @@ - - - - - - Code coverage report for src/OptionList/index.tsx - - - - - - - - -
      -
      -

      All files / src/OptionList index.tsx

      -
      - -
      - 93.98% - Statements - 125/133 -
      - - -
      - 75.76% - Branches - 50/66 -
      - - -
      - 89.29% - Functions - 25/28 -
      - - -
      - 94.96% - Lines - 113/119 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -136x -  -136x -  -136x -  -136x -  -  -136x -  -136x -22x -21x -  -  -1x -1x -1x -1x -  -1x -  -1x -  -  -  -  -136x -40x -1x -1x -1x -  -  -  -  -  -  -  -136x -136x -  -  -136x -  -136x -57x -40x -  -40x -11x -35x -  -  -11x -10x -  -  -40x -  -  -  -  -136x -22x -22x -  -  -22x -  -  -136x -7x -  -7x -6x -  -  -  -136x -157x -  -157x -103x -  -  -157x -  -  -  -136x -  -  -  -  -  -  -  -136x -105x -6x -  -  -  -  -  -  -  -  -99x -  -99x -157x -  -157x -147x -  -  -  -10x -  -  -  -99x -  -  -  -136x -7x -7x -12x -  -7x -  -7x -7x -7x -  -7x -7x -  -  -  -  -  -  -136x -4x -2x -  -4x -  -  -136x -5x -5x -5x -5x -  -  -  -136x -  -  -17x -  -17x -  -  -  -2x -2x -  -2x -2x -  -  -2x -2x -2x -2x -2x -  -  -  -2x -  -  -  -3x -  -  -3x -  -3x -  -  -  -5x -  -  -5x -  -5x -  -  -  -1x -1x -  -  -  -  -1x -1x -1x -  -  -1x -  -1x -1x -  -  -  -1x -  -  -1x -  -  -  -  -4x -  -4x -4x -  -  -  -  -  -  -  -  -136x -  -  -  -  -  -  -  -  -  -  -136x -  -  -  -  -  -  -  -  -  -136x -  -  -  -136x -217x -  -  -  -  -  -  -  -  -  -136x -  -  -  -  -  -  -  -  -  -  -  -  -  - 
      /* eslint-disable default-case */
      -import * as React from 'react';
      -import classNames from 'classnames';
      -import KeyCode from 'rc-util/lib/KeyCode';
      -import type {
      -  OptionListProps as SelectOptionListProps,
      -  RefOptionListProps,
      -} from 'rc-select/lib/OptionList';
      -import { SelectContext } from 'rc-tree-select/lib/Context';
      -import type { OptionDataNode } from '../interface';
      -import Column from './Column';
      -import { isLeaf, restoreCompatibleValue } from '../util';
      -import CascaderContext from '../context';
      -import useSearchResult from '../hooks/useSearchResult';
      - 
      -type OptionListProps = SelectOptionListProps<OptionDataNode[]>;
      -export type FlattenOptions = OptionListProps['flattenOptions'];
      - 
      -const RefOptionList = React.forwardRef<RefOptionListProps, OptionListProps>((props, ref) => {
      -  const {
      -    prefixCls,
      -    options,
      -    onSelect,
      -    multiple,
      -    open,
      -    flattenOptions,
      -    searchValue,
      -    onToggleOpen,
      -    notFoundContent,
      -    direction,
      -  } = props;
      - 
      -  const rtl = direction === 'rtl';
      - 
      -  const { checkedKeys, halfCheckedKeys } = React.useContext(SelectContext);
      -  const { changeOnSelect, expandTrigger, fieldNames, loadData, search } =
      -    React.useContext(CascaderContext);
      - 
      -  // ========================= loadData =========================
      -  const [loadingKeys, setLoadingKeys] = React.useState([]);
      - 
      -  const internalLoadData = (pathValue: React.Key) => {
      -    if (!loadData) {
      -      return;
      -    }
      - 
      -    const entity = flattenOptions.find(flattenOption => flattenOption.data.value === pathValue);
      -    Eif (entity) {
      -      const { options: optionList } = restoreCompatibleValue(entity as any, fieldNames);
      -      const rawOptionList = optionList.map(opt => opt.node);
      - 
      -      setLoadingKeys(keys => [...keys, optionList[optionList.length - 1].value]);
      - 
      -      loadData(rawOptionList);
      -    }
      -  };
      - 
      -  // zombieJ: This is bad. We should make this same as `rc-tree` to use Promise instead.
      -  React.useEffect(() => {
      -    if (loadingKeys.length) {
      -      loadingKeys.forEach(loadingKey => {
      -        const option = flattenOptions.find(opt => opt.value === loadingKey);
      -        Iif (option.data.children) {
      -          setLoadingKeys(keys => keys.filter(key => key !== loadingKey));
      -        }
      -      });
      -    }
      -  }, [flattenOptions, loadingKeys]);
      - 
      -  // ========================== Values ==========================
      -  const checkedSet = React.useMemo(() => new Set(checkedKeys), [checkedKeys]);
      -  const halfCheckedSet = React.useMemo(() => new Set(halfCheckedKeys), [halfCheckedKeys]);
      - 
      -  // =========================== Open ===========================
      -  const [openPath, setOpenPath] = React.useState<React.Key[]>([]);
      - 
      -  React.useEffect(() => {
      -    if (open) {
      -      let nextOpenPath: React.Key[] = [];
      - 
      -      if (!multiple && checkedKeys.length) {
      -        const entity = flattenOptions.find(
      -          flattenOption => flattenOption.data.value === checkedKeys[0],
      -        );
      - 
      -        if (entity) {
      -          nextOpenPath = restoreCompatibleValue(entity as any, fieldNames).path;
      -        }
      -      }
      -      setOpenPath(nextOpenPath);
      -    }
      -  }, [open]);
      - 
      -  // =========================== Path ===========================
      -  const onPathOpen = (index: number, pathValue: React.Key) => {
      -    const nextOpenPath = [...openPath.slice(0, index), pathValue];
      -    setOpenPath(nextOpenPath);
      - 
      -    // Trigger loadData
      -    internalLoadData(pathValue);
      -  };
      - 
      -  const onPathSelect = (pathValue: React.Key, leaf: boolean) => {
      -    onSelect(pathValue, { selected: !checkedSet.has(pathValue) });
      - 
      -    if (!multiple && (leaf || (changeOnSelect && expandTrigger === 'hover'))) {
      -      onToggleOpen(false);
      -    }
      -  };
      - 
      -  const getPathList = (pathList: React.Key[]) => {
      -    let currentOptions = options;
      - 
      -    for (let i = 0; i < pathList.length; i += 1) {
      -      currentOptions = (currentOptions || []).find(option => option.value === pathList[i]).children;
      -    }
      - 
      -    return currentOptions;
      -  };
      - 
      -  // ========================== Search ==========================
      -  const searchOptions = useSearchResult({
      -    ...props,
      -    fieldNames,
      -    changeOnSelect,
      -    searchConfig: search,
      -  });
      - 
      -  // ========================== Column ==========================
      -  const optionColumns = React.useMemo(() => {
      -    if (searchValue) {
      -      return [
      -        {
      -          options: searchOptions,
      -        },
      -      ];
      -    }
      - 
      -    const rawOptionColumns: {
      -      options: OptionDataNode[];
      -    }[] = [];
      - 
      -    for (let i = 0; i <= openPath.length; i += 1) {
      -      const subOptions = getPathList(openPath.slice(0, i));
      - 
      -      if (subOptions) {
      -        rawOptionColumns.push({
      -          options: subOptions,
      -        });
      -      } else {
      -        break;
      -      }
      -    }
      - 
      -    return rawOptionColumns;
      -  }, [searchValue, searchOptions, openPath]);
      - 
      -  // ========================= Keyboard =========================
      -  const getActiveOption = (activeColumnIndex: number, offset: number) => {
      -    const pathActiveValue = openPath[activeColumnIndex];
      -    const currentOptions = optionColumns[activeColumnIndex]?.options || [];
      -    const activeOptionIndex = currentOptions.findIndex(opt => opt.value === pathActiveValue);
      - 
      -    const len = currentOptions.length;
      - 
      -    for (let i = 1; i <= len; i += 1) {
      -      const current = (activeOptionIndex + i * offset + len) % len;
      -      const option = currentOptions[current];
      - 
      -      Eif (!option.disabled) {
      -        return option;
      -      }
      -    }
      - 
      -    return null;
      -  };
      - 
      -  const prevColumn = () => {
      -    if (openPath.length <= 1) {
      -      onToggleOpen(false);
      -    }
      -    setOpenPath(openPath.slice(0, -1));
      -  };
      - 
      -  const nextColumn = () => {
      -    const nextColumnIndex = openPath.length;
      -    const nextActiveOption = getActiveOption(nextColumnIndex, 1);
      -    Eif (nextActiveOption) {
      -      onPathOpen(nextColumnIndex, nextActiveOption.value);
      -    }
      -  };
      - 
      -  React.useImperativeHandle(ref, () => ({
      -    // scrollTo: treeRef.current?.scrollTo,
      -    onKeyDown: event => {
      -      const { which } = event;
      - 
      -      switch (which) {
      -        // >>> Arrow keys
      -        case KeyCode.UP:
      -        case KeyCode.DOWN: {
      -          let offset = 0;
      -          Iif (which === KeyCode.UP) {
      -            offset = -1;
      -          } else Eif (which === KeyCode.DOWN) {
      -            offset = 1;
      -          }
      - 
      -          Eif (offset !== 0) {
      -            const activeColumnIndex = Math.max(openPath.length - 1, 0);
      -            const nextActiveOption = getActiveOption(activeColumnIndex, offset);
      -            Eif (nextActiveOption) {
      -              onPathOpen(activeColumnIndex, nextActiveOption.value);
      -            }
      -          }
      - 
      -          break;
      -        }
      - 
      -        case KeyCode.LEFT: {
      -          Iif (rtl) {
      -            nextColumn();
      -          } else {
      -            prevColumn();
      -          }
      -          break;
      -        }
      - 
      -        case KeyCode.RIGHT: {
      -          Iif (rtl) {
      -            prevColumn();
      -          } else {
      -            nextColumn();
      -          }
      -          break;
      -        }
      - 
      -        case KeyCode.BACKSPACE: {
      -          prevColumn();
      -          break;
      -        }
      - 
      -        // >>> Select
      -        case KeyCode.ENTER: {
      -          const lastValue = openPath[openPath.length - 1];
      -          const option = optionColumns[openPath.length - 1].options?.find(
      -            opt => opt.value === lastValue,
      -          );
      - 
      -          const leaf = isLeaf(option);
      - 
      -          Eif (multiple || changeOnSelect || leaf) {
      -            onPathSelect(lastValue, leaf);
      -          }
      - 
      -          // Close for changeOnSelect
      -          Iif (changeOnSelect) {
      -            onToggleOpen(false);
      -          }
      -          break;
      -        }
      - 
      -        // >>> Close
      -        case KeyCode.ESC: {
      -          onToggleOpen(false);
      - 
      -          Eif (open) {
      -            event.stopPropagation();
      -          }
      -        }
      -      }
      -    },
      -    onKeyUp: () => {},
      -  }));
      - 
      -  // ========================== Render ==========================
      -  const columnProps = {
      -    ...props,
      -    onOpen: onPathOpen,
      -    onSelect: onPathSelect,
      -    onToggleOpen,
      -    checkedSet,
      -    halfCheckedSet,
      -    loadingKeys,
      -  };
      - 
      -  // >>>>> Empty
      -  const emptyList: OptionDataNode[] = [
      -    {
      -      title: notFoundContent,
      -      value: '__EMPTY__',
      -      disabled: true,
      -      node: null,
      -    },
      -  ];
      - 
      -  // >>>>> Columns
      -  const mergedOptionColumns = optionColumns[0]?.options?.length
      -    ? optionColumns
      -    : [{ options: emptyList }];
      - 
      -  const columnNodes: React.ReactElement[] = mergedOptionColumns.map((col, index) => (
      -    <Column
      -      key={index}
      -      index={index}
      -      {...columnProps}
      -      options={col.options}
      -      openKey={openPath[index]}
      -    />
      -  ));
      - 
      -  // >>>>> Render
      -  return (
      -    <>
      -      <div
      -        className={classNames(`${prefixCls}-holder`, {
      -          [`${prefixCls}-rtl`]: rtl,
      -        })}
      -      >
      -        {columnNodes}
      -      </div>
      -    </>
      -  );
      -});
      - 
      -export default RefOptionList;
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/context.ts.html b/coverage/lcov-report/src/context.ts.html deleted file mode 100644 index 8f7ce5a1..00000000 --- a/coverage/lcov-report/src/context.ts.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - Code coverage report for src/context.ts - - - - - - - - -
      -
      -

      All files / src context.ts

      -
      - -
      - 100% - Statements - 1/1 -
      - - -
      - 100% - Branches - 0/0 -
      - - -
      - 100% - Functions - 0/0 -
      - - -
      - 100% - Lines - 1/1 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32  -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
      import * as React from 'react';
      -import type { CascaderProps } from './Cascader';
      -import type { ShowSearchType } from './interface';
      - 
      -const CascaderContext = React.createContext<
      -  Required<
      -    Pick<
      -      CascaderProps,
      -      | 'changeOnSelect'
      -      | 'expandTrigger'
      -      | 'fieldNames'
      -      | 'expandIcon'
      -      | 'loadingIcon'
      -      | 'loadData'
      -      | 'dropdownMenuColumnStyle'
      -    > & {
      -      search: ShowSearchType;
      -    }
      -  >
      ->({
      -  changeOnSelect: false,
      -  expandTrigger: 'click',
      -  fieldNames: null,
      -  expandIcon: null,
      -  loadingIcon: null,
      -  loadData: null,
      -  dropdownMenuColumnStyle: null,
      -  search: null,
      -});
      - 
      -export default CascaderContext;
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/index.html b/coverage/lcov-report/src/hooks/index.html deleted file mode 100644 index 99b640bf..00000000 --- a/coverage/lcov-report/src/hooks/index.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - Code coverage report for src/hooks - - - - - - - - -
      -
      -

      All files src/hooks

      -
      - -
      - 100% - Statements - 50/50 -
      - - -
      - 100% - Branches - 24/24 -
      - - -
      - 100% - Functions - 16/16 -
      - - -
      - 100% - Lines - 45/45 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FileStatementsBranchesFunctionsLines
      useSearchConfig.ts -
      -
      100%7/7100%6/6100%2/2100%7/7
      useSearchResult.ts -
      -
      100%37/37100%16/16100%12/12100%32/32
      useUpdateEffect.ts -
      -
      100%6/6100%2/2100%2/2100%6/6
      -
      -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/useSearchConfig.ts.html b/coverage/lcov-report/src/hooks/useSearchConfig.ts.html deleted file mode 100644 index 0abbb734..00000000 --- a/coverage/lcov-report/src/hooks/useSearchConfig.ts.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - Code coverage report for src/hooks/useSearchConfig.ts - - - - - - - - -
      -
      -

      All files / src/hooks useSearchConfig.ts

      -
      - -
      - 100% - Statements - 7/7 -
      - - -
      - 100% - Branches - 6/6 -
      - - -
      - 100% - Functions - 2/2 -
      - - -
      - 100% - Lines - 7/7 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27  -  -  -  -  -  -51x -35x -30x -  -  -5x -  -  -  -  -5x -3x -  -  -  -  -  -5x -  -  - 
      import * as React from 'react';
      -import type { ShowSearchType } from '../interface';
      -import type { CascaderProps } from '../Cascader';
      - 
      -// Convert `showSearch` to unique config
      -export default function useSearchConfig(showSearch?: CascaderProps['showSearch']) {
      -  return React.useMemo<[boolean, ShowSearchType]>(() => {
      -    if (!showSearch) {
      -      return [false, {}];
      -    }
      - 
      -    let searchConfig: ShowSearchType = {
      -      matchInputWidth: true,
      -      limit: 50,
      -    };
      - 
      -    if (showSearch && typeof showSearch === 'object') {
      -      searchConfig = {
      -        ...searchConfig,
      -        ...showSearch,
      -      };
      -    }
      - 
      -    return [true, searchConfig];
      -  }, [showSearch]);
      -}
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/useSearchResult.ts.html b/coverage/lcov-report/src/hooks/useSearchResult.ts.html deleted file mode 100644 index ce1ec968..00000000 --- a/coverage/lcov-report/src/hooks/useSearchResult.ts.html +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - Code coverage report for src/hooks/useSearchResult.ts - - - - - - - - -
      -
      -

      All files / src/hooks useSearchResult.ts

      -
      - -
      - 100% - Statements - 37/37 -
      - - -
      - 100% - Branches - 16/16 -
      - - -
      - 100% - Functions - 12/12 -
      - - -
      - 100% - Lines - 32/32 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90  -  -  -  -  -4x -38x -  -4x -29x -  -  -  -  -  -  -  -  -  -  -  -  -136x -  -  -136x -136x -  -  -  -136x -39x -33x -  -  -  -6x -30x -60x -30x -  -  -  -  -  -  -6x -25x -  -  -  -6x -6x -  -20x -1x -  -  -  -19x -19x -12x -  -  -  -  -6x -1x -1x -  -  -  -6x -  -  -  -  -136x -39x -12x -12x -  -  -  -  -  -  -136x -  - 
      import * as React from 'react';
      -import type { FlattenOptions } from '../OptionList';
      -import type { FieldNames, ShowSearchType } from '../interface';
      -import { isLeaf, restoreCompatibleValue } from '../util';
      - 
      -const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) =>
      -  options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase()));
      - 
      -const defaultRender: ShowSearchType['render'] = (inputValue, path, prefixCls, fieldNames) =>
      -  path.map(opt => opt[fieldNames.label]).join(' / ');
      - 
      -export interface SearchResultConfig {
      -  prefixCls: string;
      -  searchConfig: ShowSearchType;
      -  flattenOptions: FlattenOptions;
      -  fieldNames: FieldNames;
      -  changeOnSelect: boolean;
      -  searchValue: string;
      -}
      - 
      -export default function useSearchResult(resultConfig: SearchResultConfig) {
      -  const { searchConfig, flattenOptions, fieldNames, changeOnSelect, searchValue, prefixCls } =
      -    resultConfig;
      - 
      -  // ============================== MISC ==============================
      -  const filterOption = searchConfig.filter || defaultFilter;
      -  const renderOption = searchConfig.render || defaultRender;
      - 
      -  // ============================= Filter =============================
      -  // Do filter
      -  const filteredEntityList = React.useMemo(() => {
      -    if (!searchValue) {
      -      return [];
      -    }
      - 
      -    // Normalize list
      -    let normalizeList = flattenOptions.map(option => {
      -      const { options } = restoreCompatibleValue(option as any, fieldNames);
      -      const originOptionList = options.map(opt => opt.node);
      -      return {
      -        option,
      -        originOptionList,
      -      };
      -    });
      - 
      -    // Not keep parent node when !changeOnSelect
      -    if (!changeOnSelect) {
      -      normalizeList = normalizeList.filter(({ option }) => isLeaf(option.data));
      -    }
      - 
      -    // Filter
      -    const filteredList: typeof normalizeList = [];
      -    for (let i = 0; i < normalizeList.length; i += 1) {
      -      // Perf saving if enabled
      -      if (!searchConfig.sort && filteredList.length >= searchConfig.limit) {
      -        break;
      -      }
      - 
      -      // Do filter
      -      const optGrp = normalizeList[i];
      -      if (filterOption(searchValue, optGrp.originOptionList, fieldNames)) {
      -        filteredList.push(optGrp);
      -      }
      -    }
      - 
      -    // Sort: When searchConfig.sort is enabled. We have to filter all the list
      -    if (searchConfig.sort) {
      -      filteredList.sort((a, b) => {
      -        return searchConfig.sort(a.originOptionList, b.originOptionList, searchValue, fieldNames);
      -      });
      -    }
      - 
      -    return filteredList;
      -  }, [flattenOptions, fieldNames, searchValue, filterOption, changeOnSelect, searchConfig]);
      - 
      -  // ======================== Generate Options ========================
      -  // Wrap with connected label
      -  const options = React.useMemo(() => {
      -    return filteredEntityList.map(({ option, originOptionList }) => {
      -      const title = renderOption(searchValue, originOptionList, prefixCls, fieldNames);
      -      return {
      -        ...option.data,
      -        title,
      -      };
      -    });
      -  }, [searchValue, renderOption, filteredEntityList, fieldNames, prefixCls]);
      - 
      -  return options;
      -}
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/hooks/useUpdateEffect.ts.html b/coverage/lcov-report/src/hooks/useUpdateEffect.ts.html deleted file mode 100644 index 9eec6cd8..00000000 --- a/coverage/lcov-report/src/hooks/useUpdateEffect.ts.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - Code coverage report for src/hooks/useUpdateEffect.ts - - - - - - - - -
      -
      -

      All files / src/hooks useUpdateEffect.ts

      -
      - -
      - 100% - Statements - 6/6 -
      - - -
      - 100% - Branches - 2/2 -
      - - -
      - 100% - Functions - 2/2 -
      - - -
      - 100% - Lines - 6/6 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19  -  -  -  -  -  -51x -  -51x -36x -35x -35x -  -  -  -1x -  -  - 
      import * as React from 'react';
      - 
      -/**
      - * Work like `componentDidUpdate`
      - */
      -export default function useUpdateEffect(updater: () => void, condition?: any[]) {
      -  const prepareRef = React.useRef(true);
      - 
      -  React.useEffect(() => {
      -    if (prepareRef.current) {
      -      prepareRef.current = false;
      -      return;
      -    }
      - 
      -    // eslint-disable-next-line consistent-return
      -    return updater();
      -  }, condition);
      -}
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/index.html b/coverage/lcov-report/src/index.html deleted file mode 100644 index e5f28acf..00000000 --- a/coverage/lcov-report/src/index.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - Code coverage report for src - - - - - - - - -
      -
      -

      All files src

      -
      - -
      - 94.25% - Statements - 82/87 -
      - - -
      - 79.07% - Branches - 34/43 -
      - - -
      - 88.89% - Functions - 24/27 -
      - - -
      - 95.12% - Lines - 78/82 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      FileStatementsBranchesFunctionsLines
      Cascader.tsx -
      -
      92.42%61/6675%24/3285.71%18/2193.44%57/61
      context.ts -
      -
      100%1/1100%0/0100%0/0100%1/1
      index.tsx -
      -
      0%0/00%0/00%0/00%0/0
      interface.ts -
      -
      0%0/00%0/00%0/00%0/0
      util.ts -
      -
      100%20/2090.91%10/11100%6/6100%20/20
      -
      -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/index.tsx.html b/coverage/lcov-report/src/index.tsx.html deleted file mode 100644 index c4b6a802..00000000 --- a/coverage/lcov-report/src/index.tsx.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - Code coverage report for src/index.tsx - - - - - - - - -
      -
      -

      All files / src index.tsx

      -
      - -
      - 0% - Statements - 0/0 -
      - - -
      - 0% - Branches - 0/0 -
      - - -
      - 0% - Functions - 0/0 -
      - - -
      - 0% - Lines - 0/0 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3  -  - 
      import Cascader from './Cascader'
      - 
      -export default Cascader;
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/interface.ts.html b/coverage/lcov-report/src/interface.ts.html deleted file mode 100644 index a3ecf1f4..00000000 --- a/coverage/lcov-report/src/interface.ts.html +++ /dev/null @@ -1,235 +0,0 @@ - - - - - - Code coverage report for src/interface.ts - - - - - - - - -
      -
      -

      All files / src interface.ts

      -
      - -
      - 0% - Statements - 0/0 -
      - - -
      - 0% - Branches - 0/0 -
      - - -
      - 0% - Functions - 0/0 -
      - - -
      - 0% - Lines - 0/0 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
      import type * as React from 'react';
      - 
      -export interface FieldNames {
      -  value?: string;
      -  label?: string;
      -  children?: string;
      -}
      - 
      -export type CascaderValueType = React.Key[];
      - 
      -export interface DataNode {
      -  label: React.ReactNode;
      -  /** Customize hover title */
      -  title?: string;
      -  value: string;
      -  disabled?: boolean;
      -  children?: DataNode[];
      -  isLeaf?: boolean;
      -}
      - 
      -export interface InternalDataNode extends DataNode {
      -  node: DataNode;
      -}
      - 
      -export interface OptionDataNode extends Omit<InternalDataNode, 'label' | 'children' | 'title'> {
      -  title: React.ReactNode;
      -  children?: OptionDataNode[];
      -}
      -export interface FlattenDataNode {
      -  key: React.Key;
      -  data: DataNode;
      -  path: React.Key[];
      -  parent?: FlattenDataNode;
      -}
      - 
      -export interface ShowSearchType {
      -  filter?: (inputValue: string, options: DataNode[], fieldNames: FieldNames) => boolean;
      -  render?: (
      -    inputValue: string,
      -    path: DataNode[],
      -    prefixCls: string,
      -    fieldNames: FieldNames,
      -  ) => React.ReactNode;
      -  sort?: (
      -    a: DataNode[],
      -    b: DataNode[],
      -    inputValue: string,
      -    fieldNames: FieldNames,
      -  ) => number;
      -  matchInputWidth?: boolean;
      -  limit?: number | false;
      -}
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov-report/src/util.ts.html b/coverage/lcov-report/src/util.ts.html deleted file mode 100644 index 348481e4..00000000 --- a/coverage/lcov-report/src/util.ts.html +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - Code coverage report for src/util.ts - - - - - - - - -
      -
      -

      All files / src util.ts

      -
      - -
      - 100% - Statements - 20/20 -
      - - -
      - 90.91% - Branches - 10/11 -
      - - -
      - 100% - Functions - 6/6 -
      - - -
      - 100% - Lines - 20/20 -
      - - -
      -

      - Press n or j to go to the next uncovered block, b, p or k for the previous block. -

      -
      -
      -
      
      -
      1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86  -  -  -  -  -  -  -  -  -4x -  -  -  -  -  -  -  -  -  -  -  -  -61x -61x -  -61x -  -61x -133x -133x -  -133x -  -  -61x -  -  -  -519x -  -519x -  -519x -  -  -  -  -  -  -  -  -  -187x -  -  -  -  -  -  -  -  -  -  -  -  -162x -267x -  -  -267x -  -  -  -  -  -267x -127x -  -  -267x -  -  -  -35x -  - 
      import type { FlattenDataNode } from 'rc-tree-select/lib/interface';
      -import type {
      -  CascaderValueType,
      -  DataNode,
      -  FieldNames,
      -  InternalDataNode,
      -  OptionDataNode,
      -} from './interface';
      - 
      -const VALUE_SPLIT = '__RC_CASCADER_SPLIT__';
      - 
      -/**
      - * Convert entity back to path & options
      - * @returns
      - */
      -export function restoreCompatibleValue(
      -  entity: FlattenDataNode,
      -  fieldNames: FieldNames,
      -): {
      -  path: CascaderValueType;
      -  options: InternalDataNode[];
      -} {
      -  const path: CascaderValueType = [];
      -  const options: InternalDataNode[] = [];
      - 
      -  let current = entity;
      - 
      -  while (current) {
      -    path.unshift(current.data.node[fieldNames.value]);
      -    options.unshift(current.data.node as any as InternalDataNode);
      - 
      -    current = current.parent;
      -  }
      - 
      -  return { path, options };
      -}
      - 
      -export function isLeaf(option: OptionDataNode) {
      -  const { children, node } = option;
      - 
      -  const leaf = node?.isLeaf;
      - 
      -  return leaf !== undefined ? leaf : !children?.length;
      -}
      - 
      -/**
      - * We will connect path value to a string. e.g.
      - * ['little', 'bamboo'] => 'little__bamboo'
      - *
      - * zombieJ: It's better to deprecate the same key in the nest tree. Maybe next major version.
      - */
      -export function connectValue(value: React.Key[]) {
      -  return (value || []).join(VALUE_SPLIT);
      -}
      - 
      -/**
      - * Fill options with fully value by path to avoid nest entity with same value.
      - * Which means we need another round to get origin node back!
      - * This is slow perf on large list. We should abandon same value in nest in future.
      - */
      -export function convertOptions(
      -  options: DataNode[],
      -  { value: fieldValue, children: fieldChildren }: FieldNames,
      -): InternalDataNode[] {
      -  function injectValue(list: DataNode[], parentValue = ''): InternalDataNode[] {
      -    return (list || []).map(option => {
      -      const newValue = parentValue
      -        ? connectValue([parentValue, option[fieldValue]])
      -        : option[fieldValue];
      -      const cloneOption = {
      -        ...option,
      -        [fieldValue]: newValue,
      -        node: option,
      -      };
      - 
      -      if (cloneOption[fieldChildren]) {
      -        cloneOption[fieldChildren] = injectValue(cloneOption[fieldChildren], newValue);
      -      }
      - 
      -      return cloneOption;
      -    });
      -  }
      - 
      -  return injectValue(options);
      -}
      - 
      - -
      -
      - - - - - - - - - \ No newline at end of file diff --git a/coverage/lcov.info b/coverage/lcov.info deleted file mode 100644 index 371acdf3..00000000 --- a/coverage/lcov.info +++ /dev/null @@ -1,688 +0,0 @@ -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/Cascader.tsx -FN:120,(anonymous_0) -FN:150,(anonymous_1) -FN:155,(anonymous_2) -FN:156,(anonymous_3) -FN:159,(anonymous_4) -FN:164,(anonymous_5) -FN:176,(anonymous_6) -FN:186,(anonymous_7) -FN:197,(anonymous_8) -FN:201,(anonymous_9) -FN:206,(anonymous_10) -FN:214,(anonymous_11) -FN:219,(anonymous_12) -FN:228,(anonymous_13) -FN:230,(anonymous_14) -FN:232,(anonymous_15) -FN:234,(anonymous_16) -FN:265,(anonymous_17) -FN:272,(anonymous_18) -FN:322,(anonymous_19) -FN:327,(anonymous_20) -FNF:21 -FNH:18 -FNDA:51,(anonymous_0) -FNDA:35,(anonymous_1) -FNDA:0,(anonymous_2) -FNDA:0,(anonymous_3) -FNDA:0,(anonymous_4) -FNDA:7,(anonymous_5) -FNDA:35,(anonymous_6) -FNDA:36,(anonymous_7) -FNDA:35,(anonymous_8) -FNDA:1,(anonymous_9) -FNDA:13,(anonymous_10) -FNDA:32,(anonymous_11) -FNDA:7,(anonymous_12) -FNDA:7,(anonymous_13) -FNDA:7,(anonymous_14) -FNDA:17,(anonymous_15) -FNDA:17,(anonymous_16) -FNDA:50,(anonymous_17) -FNDA:35,(anonymous_18) -FNDA:12,(anonymous_19) -FNDA:151,(anonymous_20) -DA:36,4 -DA:120,4 -DA:146,51 -DA:148,51 -DA:150,51 -DA:153,51 -DA:155,51 -DA:157,0 -DA:160,0 -DA:164,51 -DA:165,7 -DA:168,51 -DA:173,51 -DA:176,51 -DA:177,35 -DA:186,51 -DA:189,36 -DA:190,36 -DA:191,9 -DA:194,36 -DA:197,51 -DA:198,35 -DA:201,51 -DA:202,1 -DA:206,51 -DA:207,13 -DA:209,13 -DA:210,0 -DA:213,13 -DA:214,32 -DA:219,51 -DA:221,7 -DA:223,7 -DA:225,7 -DA:226,7 -DA:228,7 -DA:230,7 -DA:231,7 -DA:232,17 -DA:234,17 -DA:235,7 -DA:239,7 -DA:240,6 -DA:243,7 -DA:244,6 -DA:245,0 -DA:249,6 -DA:255,51 -DA:256,51 -DA:260,51 -DA:263,51 -DA:265,51 -DA:266,50 -DA:267,50 -DA:271,51 -DA:272,35 -DA:297,51 -DA:305,51 -DA:322,12 -DA:327,151 -DA:334,4 -LF:61 -LH:57 -BRDA:139,0,0,40 -BRDA:190,1,0,9 -BRDA:190,1,1,27 -BRDA:191,2,0,0 -BRDA:191,2,1,9 -BRDA:198,3,0,35 -BRDA:198,3,1,33 -BRDA:209,4,0,0 -BRDA:209,4,1,13 -BRDA:223,5,0,0 -BRDA:223,5,1,7 -BRDA:239,6,0,6 -BRDA:239,6,1,1 -BRDA:243,7,0,6 -BRDA:243,7,1,1 -BRDA:244,8,0,0 -BRDA:244,8,1,6 -BRDA:249,9,0,6 -BRDA:249,9,1,0 -BRDA:249,10,0,6 -BRDA:249,10,1,0 -BRDA:255,11,0,51 -BRDA:255,11,1,0 -BRDA:263,12,0,12 -BRDA:263,12,1,39 -BRDA:297,13,0,9 -BRDA:297,13,1,42 -BRDA:297,14,0,51 -BRDA:297,14,1,6 -BRDA:297,14,2,45 -BRDA:310,15,0,0 -BRDA:310,15,1,51 -BRF:32 -BRH:24 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/context.ts -FNF:0 -FNH:0 -DA:5,4 -LF:1 -LH:1 -BRF:0 -BRH:0 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/index.tsx -FNF:0 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/interface.ts -FNF:0 -FNH:0 -LF:0 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/util.ts -FN:16,restoreCompatibleValue -FN:38,isLeaf -FN:52,connectValue -FN:61,convertOptions -FN:65,injectValue -FN:66,(anonymous_5) -FNF:6 -FNH:6 -FNDA:61,restoreCompatibleValue -FNDA:519,isLeaf -FNDA:187,connectValue -FNDA:35,convertOptions -FNDA:162,injectValue -FNDA:267,(anonymous_5) -DA:10,4 -DA:23,61 -DA:24,61 -DA:26,61 -DA:28,61 -DA:29,133 -DA:30,133 -DA:32,133 -DA:35,61 -DA:39,519 -DA:41,519 -DA:43,519 -DA:53,187 -DA:66,162 -DA:67,267 -DA:70,267 -DA:76,267 -DA:77,127 -DA:80,267 -DA:84,35 -LF:20 -LH:20 -BRDA:43,0,0,6 -BRDA:43,0,1,513 -BRDA:53,1,0,187 -BRDA:53,1,1,0 -BRDA:65,2,0,35 -BRDA:66,3,0,162 -BRDA:66,3,1,1 -BRDA:67,4,0,178 -BRDA:67,4,1,89 -BRDA:76,5,0,127 -BRDA:76,5,1,140 -BRF:11 -BRH:10 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/OptionList/Column.tsx -FN:22,Column -FN:45,(anonymous_1) -FN:55,(anonymous_2) -FN:62,(anonymous_3) -FN:89,(anonymous_4) -FN:105,(anonymous_5) -FN:112,(anonymous_6) -FN:118,(anonymous_7) -FNF:8 -FNH:7 -FNDA:217,Column -FNDA:493,(anonymous_1) -FNDA:19,(anonymous_2) -FNDA:15,(anonymous_3) -FNDA:4,(anonymous_4) -FNDA:0,(anonymous_5) -FNDA:15,(anonymous_6) -FNDA:1,(anonymous_7) -DA:35,217 -DA:36,217 -DA:37,217 -DA:40,217 -DA:43,217 -DA:46,493 -DA:47,493 -DA:49,493 -DA:52,493 -DA:55,493 -DA:56,19 -DA:57,15 -DA:62,493 -DA:63,15 -DA:64,6 -DA:70,493 -DA:71,4 -DA:72,489 -DA:73,489 -DA:77,493 -DA:90,4 -DA:91,4 -DA:106,0 -DA:113,15 -DA:114,15 -DA:115,15 -DA:119,1 -DA:120,1 -LF:28 -LH:27 -BRDA:56,0,0,15 -BRDA:56,0,1,4 -BRDA:56,1,0,19 -BRDA:56,1,1,19 -BRDA:63,2,0,6 -BRDA:63,2,1,9 -BRDA:63,3,0,15 -BRDA:63,3,1,15 -BRDA:63,3,2,11 -BRDA:63,3,3,9 -BRDA:70,4,0,4 -BRDA:70,4,1,489 -BRDA:72,5,0,489 -BRDA:72,5,1,0 -BRDA:90,6,0,4 -BRDA:90,6,1,0 -BRDA:95,7,0,493 -BRDA:95,7,1,0 -BRDA:101,8,0,0 -BRDA:101,8,1,0 -BRDA:114,9,0,15 -BRDA:114,9,1,0 -BRDA:114,10,0,15 -BRDA:114,10,1,0 -BRDA:119,11,0,1 -BRDA:119,11,1,0 -BRDA:126,12,0,493 -BRDA:126,12,1,492 -BRDA:126,12,2,346 -BRDA:126,12,3,251 -BRDA:129,13,0,493 -BRDA:129,13,1,1 -BRDA:129,13,2,0 -BRF:33 -BRH:24 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/OptionList/index.tsx -FN:19,(anonymous_0) -FN:42,(anonymous_1) -FN:47,(anonymous_2) -FN:50,(anonymous_3) -FN:52,(anonymous_4) -FN:59,(anonymous_5) -FN:61,(anonymous_6) -FN:62,(anonymous_7) -FN:64,(anonymous_8) -FN:64,(anonymous_9) -FN:71,(anonymous_10) -FN:72,(anonymous_11) -FN:77,(anonymous_12) -FN:83,(anonymous_13) -FN:95,(anonymous_14) -FN:103,(anonymous_15) -FN:111,(anonymous_16) -FN:115,(anonymous_17) -FN:130,(anonymous_18) -FN:159,(anonymous_19) -FN:162,(anonymous_20) -FN:178,(anonymous_21) -FN:185,(anonymous_22) -FN:193,(anonymous_23) -FN:195,(anonymous_24) -FN:247,(anonymous_25) -FN:273,(anonymous_26) -FN:302,(anonymous_27) -FNF:28 -FNH:25 -FNDA:136,(anonymous_0) -FNDA:22,(anonymous_1) -FNDA:1,(anonymous_2) -FNDA:1,(anonymous_3) -FNDA:1,(anonymous_4) -FNDA:40,(anonymous_5) -FNDA:1,(anonymous_6) -FNDA:1,(anonymous_7) -FNDA:0,(anonymous_8) -FNDA:0,(anonymous_9) -FNDA:39,(anonymous_10) -FNDA:39,(anonymous_11) -FNDA:57,(anonymous_12) -FNDA:35,(anonymous_13) -FNDA:22,(anonymous_14) -FNDA:7,(anonymous_15) -FNDA:157,(anonymous_16) -FNDA:103,(anonymous_17) -FNDA:105,(anonymous_18) -FNDA:7,(anonymous_19) -FNDA:12,(anonymous_20) -FNDA:4,(anonymous_21) -FNDA:5,(anonymous_22) -FNDA:136,(anonymous_23) -FNDA:17,(anonymous_24) -FNDA:1,(anonymous_25) -FNDA:0,(anonymous_26) -FNDA:217,(anonymous_27) -DA:19,4 -DA:31,136 -DA:33,136 -DA:35,136 -DA:37,136 -DA:40,136 -DA:42,136 -DA:43,22 -DA:44,21 -DA:47,1 -DA:48,1 -DA:49,1 -DA:50,1 -DA:52,1 -DA:54,1 -DA:59,136 -DA:60,40 -DA:61,1 -DA:62,1 -DA:63,1 -DA:64,0 -DA:71,136 -DA:72,136 -DA:75,136 -DA:77,136 -DA:78,57 -DA:79,40 -DA:81,40 -DA:82,11 -DA:83,35 -DA:86,11 -DA:87,10 -DA:90,40 -DA:95,136 -DA:96,22 -DA:97,22 -DA:100,22 -DA:103,136 -DA:104,7 -DA:106,7 -DA:107,6 -DA:111,136 -DA:112,157 -DA:114,157 -DA:115,103 -DA:118,157 -DA:122,136 -DA:130,136 -DA:131,105 -DA:132,6 -DA:141,99 -DA:143,99 -DA:144,157 -DA:146,157 -DA:147,147 -DA:151,10 -DA:155,99 -DA:159,136 -DA:160,7 -DA:161,7 -DA:162,12 -DA:164,7 -DA:166,7 -DA:167,7 -DA:168,7 -DA:170,7 -DA:171,7 -DA:175,0 -DA:178,136 -DA:179,4 -DA:180,2 -DA:182,4 -DA:185,136 -DA:186,5 -DA:187,5 -DA:188,5 -DA:189,5 -DA:193,136 -DA:196,17 -DA:198,17 -DA:202,2 -DA:203,2 -DA:204,0 -DA:205,2 -DA:206,2 -DA:209,2 -DA:210,2 -DA:211,2 -DA:212,2 -DA:213,2 -DA:217,2 -DA:221,3 -DA:222,0 -DA:224,3 -DA:226,3 -DA:230,5 -DA:231,0 -DA:233,5 -DA:235,5 -DA:239,1 -DA:240,1 -DA:245,1 -DA:246,1 -DA:247,1 -DA:250,1 -DA:252,1 -DA:253,1 -DA:257,1 -DA:258,0 -DA:260,1 -DA:265,4 -DA:267,4 -DA:268,4 -DA:277,136 -DA:288,136 -DA:298,136 -DA:302,136 -DA:303,217 -DA:313,136 -LF:119 -LH:113 -BRDA:43,0,0,21 -BRDA:43,0,1,1 -BRDA:48,1,0,1 -BRDA:48,1,1,0 -BRDA:60,2,0,1 -BRDA:60,2,1,39 -BRDA:63,3,0,0 -BRDA:63,3,1,1 -BRDA:78,4,0,40 -BRDA:78,4,1,17 -BRDA:81,5,0,11 -BRDA:81,5,1,29 -BRDA:81,6,0,40 -BRDA:81,6,1,40 -BRDA:86,7,0,10 -BRDA:86,7,1,1 -BRDA:106,8,0,6 -BRDA:106,8,1,1 -BRDA:106,9,0,7 -BRDA:106,9,1,7 -BRDA:106,9,2,2 -BRDA:106,9,3,2 -BRDA:115,10,0,90 -BRDA:115,10,1,0 -BRDA:131,11,0,6 -BRDA:131,11,1,99 -BRDA:146,12,0,147 -BRDA:146,12,1,10 -BRDA:161,13,0,7 -BRDA:161,13,1,0 -BRDA:170,14,0,7 -BRDA:170,14,1,0 -BRDA:179,15,0,2 -BRDA:179,15,1,2 -BRDA:188,16,0,5 -BRDA:188,16,1,0 -BRDA:198,17,0,0 -BRDA:198,17,1,2 -BRDA:198,17,2,3 -BRDA:198,17,3,5 -BRDA:198,17,4,1 -BRDA:198,17,5,1 -BRDA:198,17,6,4 -BRDA:203,18,0,0 -BRDA:203,18,1,2 -BRDA:205,19,0,2 -BRDA:205,19,1,0 -BRDA:209,20,0,2 -BRDA:209,20,1,0 -BRDA:212,21,0,2 -BRDA:212,21,1,0 -BRDA:221,22,0,0 -BRDA:221,22,1,3 -BRDA:230,23,0,0 -BRDA:230,23,1,5 -BRDA:252,24,0,1 -BRDA:252,24,1,0 -BRDA:252,25,0,1 -BRDA:252,25,1,1 -BRDA:252,25,2,1 -BRDA:257,26,0,0 -BRDA:257,26,1,1 -BRDA:267,27,0,4 -BRDA:267,27,1,0 -BRDA:298,28,0,131 -BRDA:298,28,1,5 -BRF:66 -BRH:50 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchConfig.ts -FN:6,useSearchConfig -FN:7,(anonymous_1) -FNF:2 -FNH:2 -FNDA:51,useSearchConfig -FNDA:35,(anonymous_1) -DA:7,51 -DA:8,35 -DA:9,30 -DA:12,5 -DA:17,5 -DA:18,3 -DA:24,5 -LF:7 -LH:7 -BRDA:8,0,0,30 -BRDA:8,0,1,5 -BRDA:17,1,0,3 -BRDA:17,1,1,2 -BRDA:17,2,0,5 -BRDA:17,2,1,5 -BRF:6 -BRH:6 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/hooks/useSearchResult.ts -FN:6,(anonymous_0) -FN:7,(anonymous_1) -FN:9,(anonymous_2) -FN:10,(anonymous_3) -FN:21,useSearchResult -FN:31,(anonymous_5) -FN:37,(anonymous_6) -FN:39,(anonymous_7) -FN:48,(anonymous_8) -FN:68,(anonymous_9) -FN:78,(anonymous_10) -FN:79,(anonymous_11) -FNF:12 -FNH:12 -FNDA:19,(anonymous_0) -FNDA:38,(anonymous_1) -FNDA:10,(anonymous_2) -FNDA:29,(anonymous_3) -FNDA:136,useSearchResult -FNDA:39,(anonymous_5) -FNDA:30,(anonymous_6) -FNDA:60,(anonymous_7) -FNDA:25,(anonymous_8) -FNDA:1,(anonymous_9) -FNDA:39,(anonymous_10) -FNDA:12,(anonymous_11) -DA:6,4 -DA:7,38 -DA:9,4 -DA:10,29 -DA:23,136 -DA:26,136 -DA:27,136 -DA:31,136 -DA:32,39 -DA:33,33 -DA:37,6 -DA:38,30 -DA:39,60 -DA:40,30 -DA:47,6 -DA:48,25 -DA:52,6 -DA:53,6 -DA:55,20 -DA:56,1 -DA:60,19 -DA:61,19 -DA:62,12 -DA:67,6 -DA:68,1 -DA:69,1 -DA:73,6 -DA:78,136 -DA:79,39 -DA:80,12 -DA:81,12 -DA:88,136 -LF:32 -LH:32 -BRDA:26,0,0,136 -BRDA:26,0,1,136 -BRDA:27,1,0,136 -BRDA:27,1,1,133 -BRDA:32,2,0,33 -BRDA:32,2,1,6 -BRDA:47,3,0,5 -BRDA:47,3,1,1 -BRDA:55,4,0,1 -BRDA:55,4,1,19 -BRDA:55,5,0,20 -BRDA:55,5,1,17 -BRDA:61,6,0,12 -BRDA:61,6,1,7 -BRDA:67,7,0,1 -BRDA:67,7,1,5 -BRF:16 -BRH:16 -end_of_record -TN: -SF:/Users/jilin/projects/antd/rc-cascader/src/hooks/useUpdateEffect.ts -FN:6,useUpdateEffect -FN:9,(anonymous_1) -FNF:2 -FNH:2 -FNDA:51,useUpdateEffect -FNDA:36,(anonymous_1) -DA:7,51 -DA:9,51 -DA:10,36 -DA:11,35 -DA:12,35 -DA:16,1 -LF:6 -LH:6 -BRDA:10,0,0,35 -BRDA:10,0,1,1 -BRF:2 -BRH:2 -end_of_record From acca4f9b344aeb53dda3d8bc18e44c62d6e12c10 Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 26 Aug 2021 11:49:17 +0800 Subject: [PATCH 47/54] test: more coverage --- src/OptionList/index.tsx | 7 +- tests/demoOptions.js | 226 +++++++++++++++++++++++---------------- tests/keyboard.spec.js | 73 ++++++++++++- 3 files changed, 210 insertions(+), 96 deletions(-) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 76dfbdc2..a8f86a66 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -159,10 +159,15 @@ const RefOptionList = React.forwardRef((pro const getActiveOption = (activeColumnIndex: number, offset: number) => { const pathActiveValue = openPath[activeColumnIndex]; const currentOptions = optionColumns[activeColumnIndex]?.options || []; - const activeOptionIndex = currentOptions.findIndex(opt => opt.value === pathActiveValue); + let activeOptionIndex = currentOptions.findIndex(opt => opt.value === pathActiveValue); const len = currentOptions.length; + // Last one is special since -1 may back 2 offset + if (offset === -1 && activeOptionIndex === -1) { + activeOptionIndex = len; + } + for (let i = 1; i <= len; i += 1) { const current = (activeOptionIndex + i * offset + len) % len; const option = currentOptions[current]; diff --git a/tests/demoOptions.js b/tests/demoOptions.js index f454d58d..d121659e 100644 --- a/tests/demoOptions.js +++ b/tests/demoOptions.js @@ -1,98 +1,140 @@ -export const optionsForActiveMenuItems = [{ - value: '1', - label: '1', - children: [{ +export const optionsForActiveMenuItems = [ + { value: '1', label: '1', - }, { + children: [ + { + value: '1', + label: '1', + }, + { + value: '2', + label: '2', + }, + ], + }, + { value: '2', label: '2', - }], -}, { - value: '2', - label: '2', - children: [{ - value: '1', - label: '1', - }, { - value: '2', - label: '2', - }, { - value: '3', - label: '3', - }], -}]; + children: [ + { + value: '1', + label: '1', + }, + { + value: '2', + label: '2', + }, + { + value: '3', + label: '3', + }, + ], + }, +]; -export const addressOptions = [{ - label: '福建', - value: 'fj', - children: [{ - label: '福州', - value: 'fuzhou', - children: [{ - label: '马尾', - value: 'mawei', - }], - }, { - label: '泉州', - value: 'quanzhou', - }], -}, { - label: '浙江', - value: 'zj', - children: [{ - label: '杭州', - value: 'hangzhou', - children: [{ - label: '余杭', - value: 'yuhang', - }], - }], -}, { - label: '北京', - value: 'bj', - children: [{ - label: '朝阳区', - value: 'chaoyang', - }, { - label: '海淀区', - value: 'haidian', - }], -}]; +export const addressOptions = [ + { + label: '福建', + value: 'fj', + children: [ + { + label: '福州', + value: 'fuzhou', + children: [ + { + label: '马尾', + value: 'mawei', + }, + ], + }, + { + label: '泉州', + value: 'quanzhou', + }, + ], + }, + { + label: '浙江', + value: 'zj', + children: [ + { + label: '杭州', + value: 'hangzhou', + children: [ + { + label: '余杭', + value: 'yuhang', + }, + ], + }, + ], + }, + { + label: '北京', + value: 'bj', + children: [ + { + label: '朝阳区', + value: 'chaoyang', + }, + { + label: '海淀区', + value: 'haidian', + }, + ], + }, +]; -export const addressOptionsForFieldNames = [{ - name: '福建', - code: 'fj', - nodes: [{ - name: '福州', - code: 'fuzhou', - nodes: [{ - name: '马尾', - code: 'mawei', - }], - }, { - name: '泉州', - code: 'quanzhou', - }], -}, { - name: '浙江', - code: 'zj', - nodes: [{ - name: '杭州', - code: 'hangzhou', - nodes: [{ - name: '余杭', - code: 'yuhang', - }], - }], -}, { - name: '北京', - code: 'bj', - nodes: [{ - name: '朝阳区', - code: 'chaoyang', - }, { - name: '海淀区', - code: 'haidian', - }], -}]; +export const addressOptionsForFieldNames = [ + { + name: '福建', + code: 'fj', + nodes: [ + { + name: '福州', + code: 'fuzhou', + nodes: [ + { + name: '马尾', + code: 'mawei', + }, + ], + }, + { + name: '泉州', + code: 'quanzhou', + }, + ], + }, + { + name: '浙江', + code: 'zj', + nodes: [ + { + name: '杭州', + code: 'hangzhou', + nodes: [ + { + name: '余杭', + code: 'yuhang', + }, + ], + }, + ], + }, + { + name: '北京', + code: 'bj', + nodes: [ + { + name: '朝阳区', + code: 'chaoyang', + }, + { + name: '海淀区', + code: 'haidian', + }, + ], + }, +]; diff --git a/tests/keyboard.spec.js b/tests/keyboard.spec.js index 57d59277..65d9a9cb 100644 --- a/tests/keyboard.spec.js +++ b/tests/keyboard.spec.js @@ -81,18 +81,68 @@ describe('Cascader.Keyboard', () => { expect(wrapper.isOpen()).toBeTruthy(); wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); - expect(wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text()).toEqual('福建'); + expect( + wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text(), + ).toEqual('福建'); wrapper.find('input').simulate('keyDown', { which: KeyCode.LEFT }); - expect(wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text()).toEqual('福州'); + expect( + wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text(), + ).toEqual('福州'); wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); - expect(wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text()).toEqual('福建'); + expect( + wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text(), + ).toEqual('福建'); wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); expect(wrapper.isOpen()).toBeFalsy(); }); + describe('up', () => { + it('Select last enabled', () => { + wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); + expect(wrapper.isOpen()).toBeTruthy(); + + wrapper.find('input').simulate('keyDown', { which: KeyCode.UP }); + expect( + wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text(), + ).toEqual('北京'); + }); + + it('ignore disabled item', () => { + wrapper = mount( + , + ); + + wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); + wrapper.find('input').simulate('keyDown', { which: KeyCode.UP }); + expect( + wrapper.find('.rc-cascader-menu-item-active .rc-cascader-menu-item-content').last().text(), + ).toEqual('Little'); + }); + }); + it('should have close menu when press some keys', () => { wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); expect(wrapper.isOpen()).toBeTruthy(); @@ -124,6 +174,23 @@ describe('Cascader.Keyboard', () => { expect(onKeyDown).toHaveBeenCalledTimes(3); }); + it('changeOnSelect', () => { + wrapper = mount(); + wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); + expect(wrapper.isOpen()).toBeTruthy(); + + // 0-0 + wrapper.find('input').simulate('keyDown', { which: KeyCode.DOWN }); + + // 0-0-0 + wrapper.find('input').simulate('keyDown', { which: KeyCode.RIGHT }); + + // Select + wrapper.find('input').simulate('keyDown', { which: KeyCode.ENTER }); + expect(wrapper.isOpen()).toBeFalsy(); + expect(selectedValue).toEqual(['fj', 'fuzhou']); + }); + // TODO: This is strange that we need check on this it.skip('should not handle keyDown events when children specify the onKeyDown', () => { wrapper = mount( From b8edafd0c7ae88cc272f83a3960f7b0fab41b4bb Mon Sep 17 00:00:00 2001 From: zombiej Date: Thu, 26 Aug 2021 14:14:05 +0800 Subject: [PATCH 48/54] test: more test case --- src/OptionList/index.tsx | 3 +- tests/__snapshots__/index.spec.js.snap | 4 +- tests/index.spec.js | 104 +++++++++++++++++-------- tests/keyboard.spec.js | 16 ++++ 4 files changed, 91 insertions(+), 36 deletions(-) diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index a8f86a66..edbe3bbe 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -40,7 +40,8 @@ const RefOptionList = React.forwardRef((pro const [loadingKeys, setLoadingKeys] = React.useState([]); const internalLoadData = (pathValue: React.Key) => { - if (!loadData) { + // Do not load when search + if (!loadData || searchValue) { return; } diff --git a/tests/__snapshots__/index.spec.js.snap b/tests/__snapshots__/index.spec.js.snap index e5f19d37..1dad94b9 100644 --- a/tests/__snapshots__/index.spec.js.snap +++ b/tests/__snapshots__/index.spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Cascader should not show title when title is falsy 1`] = ` +exports[`Cascader.Basic should not show title when title is falsy 1`] = `