Browse Source

fix: [TreeSelect] value will be cleared unexpectedly when uncontrolled single selection mode #515 (#590)

chore: [TreeSelect] add unit testing

Co-authored-by: chenyuling <[email protected]>
boomboomchen 3 years ago
parent
commit
00597dab17

+ 1 - 1
packages/semi-foundation/tree/treeUtil.ts

@@ -601,7 +601,7 @@ export function normalizeValue(value: any, withObject: boolean) {
     }
 }
 
-export function updateKeys(keySet: Set<string>, keyEntities: KeyEntities) {
+export function updateKeys(keySet: Set<string> | string[], keyEntities: KeyEntities) {
     const keyArr = [...keySet];
     return keyArr.filter(key => key in keyEntities);
 }

+ 157 - 0
packages/semi-ui/treeSelect/__test__/treeSelect.test.js

@@ -97,6 +97,26 @@ const treeData2 = [
     }
 ];
 
+const treeData3 = [
+    {
+        label: '亚洲',
+        value: 'Asia',
+        key: '0',
+        children: [
+            {
+                label: '中国',
+                value: 'China',
+                key: '0-0',
+            },
+        ],
+    },
+    {
+        label: '北美洲',
+        value: 'North America',
+        key: '1',
+    }
+];
+
 let commonProps = {
     motion: false,
     motionExpand: false,
@@ -780,4 +800,141 @@ describe('TreeSelect', () => {
             done();
         }, 100);
     });
+
+    it('treeData is updated should not clear value when uncontrolled mode and single selection', () => {
+        const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
+        const treeSelect = getTreeSelect({
+            defaultExpandAll: true
+        });
+        treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-option-list .${BASE_CLASS_PREFIX}-tree-option`)
+            .at(2)
+            .simulate('click', nativeEvent);
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeChildren});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeData2});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('');
+    });
+
+    it('treeData is updated should not clear value when uncontrolled mode and multiple selection', () => {
+        const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => { } } }
+        const treeSelect = getTreeSelect({
+            defaultExpandAll: true,
+            multiple: true,
+        });
+        treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-option-list .${BASE_CLASS_PREFIX}-tree-option`)
+            .at(2)
+            .simulate('click', nativeEvent);
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select-selection .${BASE_CLASS_PREFIX}-tag-group .${BASE_CLASS_PREFIX}-tag`)
+            .at(0)
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeChildren});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select-selection .${BASE_CLASS_PREFIX}-tag-group .${BASE_CLASS_PREFIX}-tag`)
+            .at(0)
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeData2});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select-selection .${BASE_CLASS_PREFIX}-tag-group .${BASE_CLASS_PREFIX}-tag`)
+            .at(0)
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .length
+        ).toEqual(0);
+    });
+
+    it('treeData is updated should not clear value when controlled mode and single selection', () => {
+        const treeSelect = getTreeSelect({
+            defaultExpandAll: true,
+            value: 'Beijing'
+        });
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeChildren});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeData3});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select .${BASE_CLASS_PREFIX}-tree-select-selection span`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('');
+    });
+
+    it('treeData is updated should not clear value when controlled mode and multiple selection', () => {
+        const treeSelect = getTreeSelect({
+            defaultExpandAll: true,
+            multiple: true,
+            value: 'Beijing'
+        });
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select-selection .${BASE_CLASS_PREFIX}-tag-group .${BASE_CLASS_PREFIX}-tag`)
+            .at(0)
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeChildren});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select-selection .${BASE_CLASS_PREFIX}-tag-group .${BASE_CLASS_PREFIX}-tag`)
+            .at(0)
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .getDOMNode()
+            .textContent
+        ).toEqual('北京');
+        treeSelect.setProps({ treeData: treeData3});
+        treeSelect.update();
+        expect(
+            treeSelect
+            .find(`.${BASE_CLASS_PREFIX}-tree-select-selection .${BASE_CLASS_PREFIX}-tag-group .${BASE_CLASS_PREFIX}-tag`)
+            .at(0)
+            .find(`.${BASE_CLASS_PREFIX}-tag-content`)
+            .length
+        ).toEqual(0);
+    });
+
 })

+ 20 - 11
packages/semi-ui/treeSelect/index.tsx

@@ -23,7 +23,8 @@ import {
     getValueOrKey,
     normalizeKeyList,
     calcDisabledKeys,
-    normalizeValue
+    normalizeValue,
+    updateKeys,
 } from '@douyinfe/semi-foundation/tree/treeUtil';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/treeSelect/constants';
 import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/popover/constants';
@@ -432,11 +433,15 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 );
             } else if (treeData) {
                 // If `treeData` changed, we also need check it
-                newState.selectedKeys = findKeysForValues(
-                    normalizeValue(props.value, withObject) || '',
-                    valueEntities,
-                    isMultiple
-                );
+                if (props.value) {
+                    newState.selectedKeys = findKeysForValues(
+                        normalizeValue(props.value, withObject) || '',
+                        valueEntities,
+                        isMultiple
+                    );
+                } else {
+                    newState.selectedKeys = updateKeys(prevState.selectedKeys, keyEntities);
+                }
             }
         } else {
             // checkedKeys: multiple mode controlled || data changed
@@ -456,11 +461,15 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 );
             } else if (treeData) {
                 // If `treeData` changed, we also need check it
-                checkedKeyValues = findKeysForValues(
-                    normalizeValue(props.value, withObject) || [],
-                    valueEntities,
-                    isMultiple
-                );
+                if (props.value) {
+                    checkedKeyValues = findKeysForValues(
+                        normalizeValue(props.value, withObject) || [],
+                        valueEntities,
+                        isMultiple
+                    );
+                } else {
+                    checkedKeyValues = updateKeys(prevState.checkedKeys, keyEntities);
+                }
             }
 
             if (checkedKeyValues) {