Browse Source

Fix/cascader rendertrigger (#2051)

* fix: fix the value parameter of triggerRender is incorrect after key composition in keyEntities change

* fix: fix the value parameter of triggerRender is incorrect after key composition in keyEntities change

* fix: remove useless code

* docs: modify example explanation
YyumeiZhang 1 year ago
parent
commit
60919672c3

+ 1 - 1
content/input/cascader/index.md

@@ -1666,7 +1666,7 @@ interface TriggerRenderProps {
     disabled: boolean;
     /**
      * 已选中的 node 在 treeData 中的层级位置,如下例子,
-     * 当选中浙江省-杭州市-萧山区时,此处 value 为 '0-0-0'
+     * 当选中浙江省-杭州市-萧山区时,此处 value 为 '0-0-1'
      */
     value?: string | Set<string>;
     /* 当前 Input 框的输入值 */

+ 13 - 7
packages/semi-foundation/cascader/foundation.ts

@@ -15,7 +15,8 @@ import {
     normalizedArr,
     isValid,
     calcMergeType,
-    getKeysByValuePath
+    getKeysByValuePath,
+    getKeyByPos
 } from './util';
 import { strings } from './constants';
 import isEnterPress from '../utils/isEnterPress';
@@ -56,6 +57,8 @@ export interface BasicEntity {
     parentKey?: string;
     /* key path */
     path: Array<string>;
+    /* pos in treeData */
+    pos: string;
     /* value path */
     valuePath: Array<string>
 }
@@ -1026,17 +1029,20 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
         this._adapter.notifyListScroll(e, { panelIndex: ind, activeNode: data });
     }
 
-    handleTagRemove(e: any, tagValuePath: string[]) {
+    handleTagRemoveByKey = (key: string) => {
         const { keyEntities } = this.getStates();
         const { disabled } = this.getProps();
         if (disabled) {
             /* istanbul ignore next */
             return;
         }
-        const removedItem = (Object.values(keyEntities) as BasicEntity[])
-            .filter(item => isEqual(item.valuePath, tagValuePath))[0];
-        !isEmpty(removedItem) &&
-        !removedItem.data.disabled &&
-        this._handleMultipleSelect(removedItem);
+        const removedItem = keyEntities[key] ?? {};
+        !removedItem?.data?.disable && this._handleMultipleSelect(removedItem);
+    }
+
+    handleTagRemoveInTrigger = (pos: string) => {
+        const { treeData } = this.getStates();
+        const key = getKeyByPos(pos, treeData);
+        this.handleTagRemoveByKey(key);
     }
 }

+ 13 - 0
packages/semi-foundation/cascader/util.ts

@@ -31,10 +31,12 @@ function traverseDataNodes(treeNodes: any, callback: any) {
         // Process node if is not root
         if (node) {
             const key = parent ? `${parent.key}${VALUE_SPLIT}${node.value}` : node.value;
+            const pos = parent ? getPosition(parent.pos, ind) : `${ind}`;
             item = {
                 data: { ...node },
                 ind,
                 key,
+                pos,
                 level: parent ? parent.level + 1 : 0,
                 parentKey: parent ? parent.key : null,
                 path: parent ? [...parent.path, key] : [key],
@@ -74,6 +76,17 @@ export function getValuePathByKey(key: string) {
     return key.split(VALUE_SPLIT);
 }
 
+export function getKeyByPos(pos: string, treeData: any) {
+    const posArr = pos.split('-').map(item => Number(item));
+    let resultData = treeData;
+    let valuePath = [];
+    posArr.forEach((item, index) => {
+        resultData = index === 0 ? resultData[item] : resultData?.children?.[item];
+        valuePath.push(resultData?.value);
+    });
+    return getKeyByValuePath(valuePath);
+}
+
 export function convertDataToEntities(dataNodes: any) {
     const keyEntities: any = {};
 

+ 2 - 2
packages/semi-ui/cascader/__test__/cascader.test.js

@@ -1257,7 +1257,7 @@ describe('Cascader', () => {
         const args = firstCall.args[0]; 
         /* check arguments of triggerRender */
         expect(args.value.size).toEqual(1);
-        expect(args.value).toEqual(new Set(['Asia']));
+        expect(args.value).toEqual(new Set('0'));
         cascaderAutoMerge.unmount();
 
         const spyTriggerRender2 = sinon.spy(() => <span>123</span>);
@@ -1272,7 +1272,7 @@ describe('Cascader', () => {
         const args2 = firstCall2.args[0]; 
         /* check arguments of triggerRender */
         expect(args2.value.size).toEqual(4);
-        expect(args2.value).toEqual(new Set(["Asia","Asia_SEMI_CASCADER_SPLIT_China","Asia_SEMI_CASCADER_SPLIT_China_SEMI_CASCADER_SPLIT_Beijing","Asia_SEMI_CASCADER_SPLIT_China_SEMI_CASCADER_SPLIT_Shanghai"]));
+        expect(args2.value).toEqual(new Set(['0','0-0','0-0-1','0-0-0']));
         cascaderNoAutoMerge.unmount();
     });
 

+ 23 - 19
packages/semi-ui/cascader/index.tsx

@@ -17,7 +17,7 @@ import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/popover/con
 import { isSet, isEqual, isString, isEmpty, isFunction, isNumber, noop, flatten, isObject } from 'lodash';
 import '@douyinfe/semi-foundation/cascader/cascader.scss';
 import { IconClear, IconChevronDown } from '@douyinfe/semi-icons';
-import { convertDataToEntities, calcMergeType, getKeyByValuePath } from '@douyinfe/semi-foundation/cascader/util';
+import { convertDataToEntities, calcMergeType, getKeyByValuePath, getKeyByPos } from '@douyinfe/semi-foundation/cascader/util';
 import { calcCheckedKeys, normalizeKeyList, calcDisabledKeys } from '@douyinfe/semi-foundation/tree/treeUtil';
 import ConfigContext, { ContextValue } from '../configProvider/context';
 import BaseComponent, { ValidateStatus } from '../_base/baseComponent';
@@ -514,13 +514,14 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
         this.foundation.handleInputChange(value);
     };
 
-    handleTagRemove = (e: any, tagValuePath: Array<string | number>) => {
-        this.foundation.handleTagRemove(e, tagValuePath);
-    };
+    handleTagRemoveInTrigger = (pos: string) => {
+        this.foundation.handleTagRemoveInTrigger(pos);
+    }
 
-    handleRemoveByKey = (key) => {
-        const { keyEntities } = this.state;
-        this.handleTagRemove(null, keyEntities[key].valuePath);
+    handleTagClose = (tagChildren: React.ReactNode, e: React.MouseEvent<HTMLElement>, tagKey: string | number) => {
+        // When value has not changed, prevent clicking tag closeBtn to close tag
+        e.preventDefault();
+        this.foundation.handleTagRemoveByKey(tagKey);
     }
 
     renderTagItem = (nodeKey: string, idx: number) => {
@@ -542,13 +543,10 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                         size={size === 'default' ? 'large' : size}
                         key={`tag-${nodeKey}-${idx}`}
                         color="white"
+                        tagKey={nodeKey}
                         className={tagCls}
                         closable
-                        onClose={(tagChildren, e) => {
-                            // When value has not changed, prevent clicking tag closeBtn to close tag
-                            e.preventDefault();
-                            this.handleTagRemove(e, keyEntities[nodeKey].valuePath);
-                        }}
+                        onClose={this.handleTagClose}
                     >
                         {keyEntities[nodeKey].data[displayProp]}
                     </Tag>
@@ -558,6 +556,10 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
         return null;
     };
 
+    onRemoveInTagInput = (v: string) => {
+        this.foundation.handleTagRemoveByKey(v);
+    };
+
     renderTagInput() {
         const { size, disabled, placeholder, maxTagCount, showRestTagsPopover, restTagsPopoverProps } = this.props;
         const { inputValue, checkedKeys, keyEntities, resolvedCheckedKeys } = this.state;
@@ -573,11 +575,11 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                 showRestTagsPopover={showRestTagsPopover}
                 restTagsPopoverProps={restTagsPopoverProps}
                 maxTagCount={maxTagCount}
-                renderTagItem={(value, index) => this.renderTagItem(value, index)}
+                renderTagItem={this.renderTagItem}
                 inputValue={inputValue}
                 onInputChange={this.handleInputChange}
                 // TODO Modify logic, not modify type
-                onRemove={v => this.handleTagRemove(null, (v as unknown) as (string | number)[])}
+                onRemove={this.onRemoveInTagInput}
                 placeholder={placeholder}
                 expandRestTagsOnClick={false}
             />
@@ -834,16 +836,18 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
 
     renderCustomTrigger = () => {
         const { disabled, triggerRender, multiple } = this.props;
-        const { selectedKeys, inputValue, inputPlaceHolder, resolvedCheckedKeys, checkedKeys } = this.state;
+        const { selectedKeys, inputValue, inputPlaceHolder, resolvedCheckedKeys, checkedKeys, keyEntities } = this.state;
         let realValue;
         if (multiple) {
             if (this.mergeType === strings.NONE_MERGE_TYPE) {
-                realValue = checkedKeys;
+                realValue = new Set();
+                checkedKeys.forEach(key => { realValue.add(keyEntities[key]?.pos); });
             } else {
-                realValue = resolvedCheckedKeys;
+                realValue = new Set();
+                resolvedCheckedKeys.forEach(key => { realValue.add(keyEntities[key]?.pos); });
             }
         } else {
-            realValue = [...selectedKeys][0];
+            realValue = keyEntities[[...selectedKeys][0]]?.pos;
         }
         return (
             <Trigger
@@ -857,7 +861,7 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                 componentName={'Cascader'}
                 componentProps={{ ...this.props }}
                 onSearch={this.handleInputChange}
-                onRemove={this.handleRemoveByKey}
+                onRemove={this.handleTagRemoveInTrigger}
             />
         );
     };