Browse Source

fix: [Cascader] in unsearchable and single-selection mode, the trigge… (#592)

* fix: [Cascader] in unsearchable and single-selection mode, the trigger will unexpectedly display [object object] when label is reactNode #514

* fix: [Cascader]fix the ts type of displayText length

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

+ 157 - 79
packages/semi-ui/cascader/__test__/cascader.test.js

@@ -16,13 +16,13 @@ const getPopupContainer = () => document.querySelector(`.${BASE_CLASS_PREFIX}-ca
 const treeData = [
     {
         label: '亚洲',
-        value: 'Yazhou',
-        key: 'yazhou',
+        value: 'Asia',
+        key: 'Asia',
         children: [
             {
                 label: '中国',
-                value: 'Zhongguo',
-                key: 'zhongguo',
+                value: 'China',
+                key: 'China',
                 children: [
                     {
                         label: '北京',
@@ -40,18 +40,18 @@ const treeData = [
     },
     {
         label: '北美洲',
-        value: 'Beimeizhou',
-        key: 'beimeizhou',
+        value: 'North America',
+        key: 'North America',
         children: [
             {
                 label: '美国',
-                value: 'Meiguo',
-                key: 'meiguo',
+                value: 'United States',
+                key: 'United States',
             },
             {
                 label: '加拿大',
-                value: 'Jianada',
-                key: 'jianada',
+                value: 'Canada',
+                key: 'Canada',
             },
         ],
     },
@@ -60,13 +60,13 @@ const treeData = [
 const treeDataWithDisabled = [
     {
         label: '亚洲',
-        value: 'Yazhou',
-        key: 'yazhou',
+        value: 'Asia',
+        key: 'Asia',
         children: [
             {
                 label: '中国',
-                value: 'Zhongguo',
-                key: 'zhongguo',
+                value: 'China',
+                key: 'China',
                 disabled: true,
                 children: [
                     {
@@ -90,6 +90,38 @@ const treeDataWithDisabled = [
     }
 ];
 
+const treeDataWithReactNode = [
+    {
+        label: <strong>亚洲</strong>,
+        value: 'Asia',
+        key: 'Asia',
+        children: [
+            {
+                label: '中国',
+                value: 'China',
+                key: 'China',
+                children: [
+                    {
+                        label: '北京',
+                        value: 'Beijing',
+                        key: 'beijing',
+                    },
+                    {
+                        label: <div>上海</div>,
+                        value: 'Shanghai',
+                        key: 'shanghai',
+                    },
+                ],
+            }
+        ],
+    },
+    {
+        label: <p>北美洲</p>,
+        value: 'North America',
+        key: 'North America',
+    }
+];
+
 let commonProps = {
     // Cascader use Popup Layer to show candidate option,
     // but all Popup Layer which extends from Tooltip (eg Popover, Dropdown) have animation and delay.
@@ -260,7 +292,7 @@ describe('Cascader', () => {
 
     it('defaultValue', () => {
         let cascader = render({
-            defaultValue: ['Yazhou', 'Zhongguo', 'Beijing'],
+            defaultValue: ['Asia', 'China', 'Beijing'],
             defaultOpen: true,
         });
         // await sleep();
@@ -282,25 +314,25 @@ describe('Cascader', () => {
 
     it('displayProp', () => {
         let cascader = render({
-            defaultValue: ['Yazhou', 'Zhongguo', 'Beijing'],
+            defaultValue: ['Asia', 'China', 'Beijing'],
             displayProp: 'value',
         });
         expect(cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection`).getDOMNode().textContent).toEqual(
-            'Yazhou / Zhongguo / Beijing'
+            'Asia / China / Beijing'
         );
     });
 
     it('displayRender', () => {
         let spyOnRender = sinon.spy(() => {});
         let cascader = render({
-            defaultValue: ['Yazhou', 'Zhongguo', 'Beijing'],
+            defaultValue: ['Asia', 'China', 'Beijing'],
             displayRender: spyOnRender,
         });
         expect(spyOnRender.calledWithMatch(['亚洲', '中国', '北京'])).toEqual(true);
         cascader.unmount();
 
         let cusRender = render({
-            defaultValue: ['Yazhou', 'Zhongguo', 'Beijing'],
+            defaultValue: ['Asia', 'China', 'Beijing'],
             displayRender: list => '已选择:' + list.join(' -> '),
         });
         expect(cusRender.find(`.${BASE_CLASS_PREFIX}-cascader-selection`).getDOMNode().textContent).toEqual(
@@ -308,6 +340,52 @@ describe('Cascader', () => {
         );
     });
 
+    it('label is ReactNode when unsearchable and single-selection mode', () => {
+        const cascader = render({
+            defaultValue: ['Asia', 'China', 'Shanghai'],
+            treeData: treeDataWithReactNode
+        });
+        expect(cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection span strong`).getDOMNode().textContent).toEqual(
+            '亚洲'
+        );
+        expect(cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection span div`).getDOMNode().textContent).toEqual(
+            '上海'
+        );
+        expect(cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection span`).getDOMNode().textContent).toEqual(
+            '亚洲 / 中国 / 上海'
+        );
+
+        cascader.unmount();
+
+        const cascader2 = render({
+            defaultValue: ['North America'],
+            treeData: treeDataWithReactNode,
+        });
+        expect(cascader2.find(`.${BASE_CLASS_PREFIX}-cascader-selection span p`).getDOMNode().textContent).toEqual(
+            '北美洲'
+        );
+        expect(cascader2.find(`.${BASE_CLASS_PREFIX}-cascader-selection span`).getDOMNode().textContent).toEqual(
+            '北美洲'
+        );
+        cascader2.unmount();
+
+        const cascader3 = render({
+            defaultValue: ['Asia', 'China', 'Shanghai'],
+            treeData: treeDataWithReactNode,
+            separator: ' - '
+        });
+        expect(cascader3.find(`.${BASE_CLASS_PREFIX}-cascader-selection span strong`).getDOMNode().textContent).toEqual(
+            '亚洲'
+        );
+        expect(cascader3.find(`.${BASE_CLASS_PREFIX}-cascader-selection span div`).getDOMNode().textContent).toEqual(
+            '上海'
+        );
+        expect(cascader3.find(`.${BASE_CLASS_PREFIX}-cascader-selection span`).getDOMNode().textContent).toEqual(
+            '亚洲 - 中国 - 上海'
+        );
+        cascader3.unmount();
+    });
+
     it('disabled', () => {
         let cascader = render({
             disabled: true,
@@ -330,7 +408,7 @@ describe('Cascader', () => {
         const cascaderWithSingle = render({
             treeData: treeDataWithDisabled,
             changeOnSelect: true,
-            defaultValue:['Yazhou', 'Zhongguo']
+            defaultValue:['Asia', 'China']
         });
         expect(
             cascaderWithSingle
@@ -345,7 +423,7 @@ describe('Cascader', () => {
             treeData: treeDataWithDisabled,
             changeOnSelect: true,
             filterTreeNode: true,
-            defaultValue:['Yazhou', 'Zhongguo']
+            defaultValue:['Asia', 'China']
         });
         expect(
             cascaderWithSingleFilter
@@ -358,7 +436,7 @@ describe('Cascader', () => {
         const cascaderWithSingleControlled = render({
             treeData: treeDataWithDisabled,
             changeOnSelect: true,
-            value: ['Yazhou', 'Zhongguo'],
+            value: ['Asia', 'China'],
         });
         expect(cascaderWithSingleControlled.find(`.${BASE_CLASS_PREFIX}-cascader-selection span`).getDOMNode().textContent).toEqual(
             '亚洲 / 中国'
@@ -368,7 +446,7 @@ describe('Cascader', () => {
         const cascaderWithMultiple = render({
             treeData: treeDataWithDisabled,
             multiple: true,
-            defaultValue:['Yazhou', 'Zhongguo']
+            defaultValue:['Asia', 'China']
         });
         expect(
             cascaderWithMultiple
@@ -383,7 +461,7 @@ describe('Cascader', () => {
             treeData: treeDataWithDisabled,
             multiple: true,
             filterTreeNode: true,
-            defaultValue:['Yazhou', 'Zhongguo']
+            defaultValue:['Asia', 'China']
         });
         expect(
             cascaderWithMultipleFilter
@@ -397,7 +475,7 @@ describe('Cascader', () => {
         const cascaderWithMultipleControlled = render({
             treeData: treeDataWithDisabled,
             multiple: true,
-            value:['Yazhou', 'Zhongguo']
+            value:['Asia', 'China']
         });
         expect(
             cascaderWithMultipleControlled
@@ -450,7 +528,7 @@ describe('Cascader', () => {
         expect(spyOnSelect.calledOnce).toBe(true);
         expect(spyOnChange.calledOnce).toBe(true);
         expect(spyOnSelect.calledWithMatch('Beijing')).toEqual(true);
-        expect(spyOnChange.calledWithMatch(['Yazhou', 'Zhongguo', 'Beijing'])).toEqual(true);
+        expect(spyOnChange.calledWithMatch(['Asia', 'China', 'Beijing'])).toEqual(true);
         expect(
             lists[2]
                 .querySelectorAll(`.${BASE_CLASS_PREFIX}-cascader-option`)[0]
@@ -507,8 +585,8 @@ describe('Cascader', () => {
         lists[0].querySelectorAll('li')[0].click();
         expect(spyOnSelect.calledOnce).toBe(true);
         expect(spyOnChange.calledOnce).toBe(true);
-        expect(spyOnSelect.calledWithMatch('Yazhou')).toEqual(true);
-        expect(spyOnChange.calledWithMatch(['Yazhou'])).toEqual(true);
+        expect(spyOnSelect.calledWithMatch('Asia')).toEqual(true);
+        expect(spyOnChange.calledWithMatch(['Asia'])).toEqual(true);
         expect(
             lists[0]
                 .querySelectorAll(`.${BASE_CLASS_PREFIX}-cascader-option`)[0]
@@ -518,8 +596,8 @@ describe('Cascader', () => {
 
         lists = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-cascader-option-list`);
         lists[1].querySelectorAll('li')[0].click();
-        expect(spyOnSelect.calledWithMatch('Zhongguo')).toEqual(true);
-        expect(spyOnChange.calledWithMatch(['Yazhou', 'Zhongguo'])).toEqual(true);
+        expect(spyOnSelect.calledWithMatch('China')).toEqual(true);
+        expect(spyOnChange.calledWithMatch(['Asia', 'China'])).toEqual(true);
         expect(
             lists[0]
                 .querySelectorAll(`.${BASE_CLASS_PREFIX}-cascader-option`)[0]
@@ -574,7 +652,7 @@ describe('Cascader', () => {
 
         // await sleep();
         let resList = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-cascader-option-label-highlight`);
-        expect(resList.length).toEqual(3);
+        expect(resList.length).toEqual(1);
         // debugger
         // expect(resList[0].textContent).toEqual('亚洲/中国/北京');
         // done();
@@ -615,7 +693,7 @@ describe('Cascader', () => {
 
     it('controlled: value shows correct', () => {
         let cascader = render({
-            value: ['Yazhou', 'Zhongguo', 'Beijing'],
+            value: ['Asia', 'China', 'Beijing'],
         });
         expect(cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection`).getDOMNode().textContent).toEqual(
             '亚洲 / 中国 / 北京'
@@ -625,7 +703,7 @@ describe('Cascader', () => {
     it('controlled: fire onChange and ui not update', () => {
         let spyOnChange = sinon.spy(() => {});
         let cascader = render({
-            value: ['Yazhou', 'Zhongguo', 'Beijing'],
+            value: ['Asia', 'China', 'Beijing'],
             defaultOpen: true,
             onChange: spyOnChange,
             changeOnSelect: true,
@@ -681,13 +759,13 @@ describe('Cascader', () => {
             spyOnChange.calledWithMatch([
                 {
                     label: '亚洲',
-                    value: 'Yazhou',
-                    key: 'yazhou',
+                    value: 'Asia',
+                    key: 'Asia',
                     children: [
                         {
                             label: '中国',
-                            value: 'Zhongguo',
-                            key: 'zhongguo',
+                            value: 'China',
+                            key: 'China',
                             children: [
                                 {
                                     label: '北京',
@@ -705,8 +783,8 @@ describe('Cascader', () => {
                 },
                 {
                     label: '中国',
-                    value: 'Zhongguo',
-                    key: 'zhongguo',
+                    value: 'China',
+                    key: 'China',
                     children: [
                         {
                             label: '北京',
@@ -753,13 +831,13 @@ describe('Cascader', () => {
             spyOnChange.calledWithMatch([
                 {
                     label: '亚洲',
-                    value: 'Yazhou',
-                    key: 'yazhou',
+                    value: 'Asia',
+                    key: 'Asia',
                     children: [
                         {
                             label: '中国',
-                            value: 'Zhongguo',
-                            key: 'zhongguo',
+                            value: 'China',
+                            key: 'China',
                             children: [
                                 {
                                     label: '北京',
@@ -790,13 +868,13 @@ describe('Cascader', () => {
             value: [
                 {
                     label: '亚洲',
-                    value: 'Yazhou',
-                    key: 'yazhou',
+                    value: 'Asia',
+                    key: 'Asia',
                     children: [
                         {
                             label: '中国',
-                            value: 'Zhongguo',
-                            key: 'zhongguo',
+                            value: 'China',
+                            key: 'China',
                             children: [
                                 {
                                     label: '北京',
@@ -814,8 +892,8 @@ describe('Cascader', () => {
                 },
                 {
                     label: '中国',
-                    value: 'Zhongguo',
-                    key: 'zhongguo',
+                    value: 'China',
+                    key: 'China',
                     children: [
                         {
                             label: '北京',
@@ -845,7 +923,7 @@ describe('Cascader', () => {
     it('onClear and showClear', () => {
         const spyOnClear = sinon.spy(() => { });
         const cascader = render({
-            defaultValue: ['Yazhou', 'Zhongguo', 'Beijing'],
+            defaultValue: ['Asia', 'China', 'Beijing'],
             showClear: true,
             onClear: spyOnClear,
             placeholder: "请选择所在地区"
@@ -989,25 +1067,25 @@ describe('Cascader', () => {
             defaultValue: [
                 {
                     label: '北美洲',
-                    value: 'Beimeizhou',
-                    key: 'beimeizhou',
+                    value: 'North America',
+                    key: 'North America',
                     children: [
                         {
                             label: '美国',
-                            value: 'Meiguo',
-                            key: 'meiguo',
+                            value: 'United States',
+                            key: 'United States',
                         },
                         {
                             label: '加拿大',
-                            value: 'Jianada',
-                            key: 'jianada',
+                            value: 'Canada',
+                            key: 'Canada',
                         },
                     ],
                 },
                 {
                     label: '美国',
-                    value: 'Meiguo',
-                    key: 'meiguo',
+                    value: 'United States',
+                    key: 'United States',
                 }
             ]
         });
@@ -1028,18 +1106,18 @@ describe('Cascader', () => {
             treeData: [
                 {
                     label: '北美洲',
-                    value: 'Beimeizhou',
-                    key: 'beimeizhou',
+                    value: 'North America',
+                    key: 'North America',
                     children: [
                         {
                             label: '美国',
-                            value: 'Meiguo',
-                            key: 'meiguo',
+                            value: 'United States',
+                            key: 'United States',
                         },
                         {
                             label: '加拿大',
-                            value: 'Jianada',
-                            key: 'jianada',
+                            value: 'Canada',
+                            key: 'Canada',
                         },
                     ],
                 },
@@ -1096,18 +1174,18 @@ describe('Cascader', () => {
             treeData: [
                 {
                     label: '北美洲',
-                    value: 'Beimeizhou',
-                    key: 'beimeizhou',
+                    value: 'North America',
+                    key: 'North America',
                     children: [
                         {
                             label: '美国',
-                            value: 'Meiguo',
-                            key: 'meiguo',
+                            value: 'United States',
+                            key: 'United States',
                         },
                         {
                             label: '加拿大',
-                            value: 'Jianada',
-                            key: 'jianada',
+                            value: 'Canada',
+                            key: 'Canada',
                         },
                     ],
                 },
@@ -1146,7 +1224,7 @@ describe('Cascader', () => {
     it('separator', () => {
         const cascader = render({
             filterTreeNode: true,
-            defaultValue: ['Yazhou', 'Zhongguo', 'Beijing'],
+            defaultValue: ['Asia', 'China', 'Beijing'],
             separator: ' > ',
             defaultOpen: true,
         });
@@ -1169,7 +1247,7 @@ describe('Cascader', () => {
         const cascaderAutoMerge = render({
             multiple: true,
             triggerRender: spyTriggerRender,
-            defaultValue: 'Yazhou'
+            defaultValue: 'Asia'
         });
         cascaderAutoMerge.simulate('click');
         const firstCall = spyTriggerRender.getCall(0);
@@ -1183,7 +1261,7 @@ describe('Cascader', () => {
         const cascaderNoAutoMerge = render({
             multiple: true,
             triggerRender: spyTriggerRender2,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
             autoMergeValue: false,
         });
         cascaderNoAutoMerge.simulate('click');
@@ -1199,7 +1277,7 @@ describe('Cascader', () => {
         const cascader = render({
             multiple: true,
             autoMergeValue: false,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
         });
         const tags = cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
         expect(tags.length).toEqual(4);
@@ -1208,7 +1286,7 @@ describe('Cascader', () => {
         const cascaderAutoMerge = render({
             multiple: true,
             autoMergeValue: true,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
         });
         const tags2 = cascaderAutoMerge.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
         expect(tags2.length).toEqual(1);
@@ -1227,7 +1305,7 @@ describe('Cascader', () => {
             multiple: true,
             autoMergeValue: false,
             leafOnly: false,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
         });
         const tags = cascader.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
         expect(tags.length).toEqual(4);
@@ -1238,7 +1316,7 @@ describe('Cascader', () => {
             multiple: true,
             autoMergeValue: true,
             leafOnly: true,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
         });
         const tags2 = cascader2.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
         expect(tags2.length).toEqual(2);
@@ -1249,7 +1327,7 @@ describe('Cascader', () => {
             multiple: true,
             autoMergeValue: false,
             leafOnly: true,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
         });
         const tags3 = cascader3.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
         expect(tags3.length).toEqual(2);
@@ -1260,7 +1338,7 @@ describe('Cascader', () => {
             multiple: true,
             autoMergeValue: true,
             leafOnly: false,
-            defaultValue: 'Yazhou',
+            defaultValue: 'Asia',
         });
         const tags4 = cascader4.find(`.${BASE_CLASS_PREFIX}-cascader-selection .${BASE_CLASS_PREFIX}-tag`)
         expect(tags4.length).toEqual(1);

+ 36 - 23
packages/semi-ui/cascader/_story/cascader.stories.js

@@ -346,37 +346,50 @@ export const Searchable = () => {
         filterLeafOnly={false}
         onChange={e => console.log(e)}
       />
+      <br />
+      <br />
+      <div>fix-1449,当 label 为 ReactNode 时,搜索显示结果[object object]</div>
+      <Cascader
+        style={{ width: 300 }}
+        treeData={treedataWithNodeLabel}
+        placeholder="宁波为 ReactNode"
+        filterTreeNode
+      />
+      <br />
+      <br />
       <div>
-        <br />
-        <br />
-        <div>fix-1449,当 label 为 ReactNode 时,搜索显示结果[object object]</div>
-        <Cascader
-          style={{ width: 300 }}
-          treeData={treedataWithNodeLabel}
-          placeholder="宁波为 ReactNode"
-          filterTreeNode
-        />
-        <div>
-          filterTreeNode=false,且 label 为 ReactNode
-          时,配合displayRender使用,使得回显到input的内容符合预期
+        filterTreeNode=false,且 label 为 ReactNode
+        时,配合displayRender使用,使得回显到input的内容符合预期
+      </div>
+      <Cascader
+        style={{ width: 300 }}
+        treeData={treedataWithNodeLabel}
+        placeholder="自定义回填时显示数据的格式"
+        displayRender={list =>
+          list.map((v, i) => {
+            return list.length - 1 === i ? (
+              <React.Fragment key={i}>{v}</React.Fragment>
+            ) : (
+              <React.Fragment key={i}>{v} / </React.Fragment>
+            );
+          })
+        }
+        defaultValue={['zhejiang', 'ningbo', 'jiangbei']}
+      />
+      <br />
+      <br />
+      <div>
+          v2.5 起,filterTreeNode=false,且 label 为 ReactNode
+          时,无配合displayRender 使用,回显到input的内容也是符合预期
         </div>
         <Cascader
           style={{ width: 300 }}
           treeData={treedataWithNodeLabel}
-          placeholder="自定义回填时显示数据的格式"
-          displayRender={list =>
-            list.map((v, i) => {
-              return list.length - 1 === i ? (
-                <React.Fragment key={i}>{v}</React.Fragment>
-              ) : (
-                <React.Fragment key={i}>{v} / </React.Fragment>
-              );
-            })
-          }
+          placeholder="宁波为 ReactNode"
+          
           defaultValue={['zhejiang', 'ningbo', 'jiangbei']}
         />
       </div>
-    </div>
   );
 };
 Searchable.parameters = {

+ 26 - 5
packages/semi-ui/cascader/index.tsx

@@ -711,9 +711,32 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
         );
     };
 
+    renderDisplayText = (): ReactNode => {
+        const { displayProp, separator, displayRender } = this.props;
+        const { selectedKeys } = this.state;
+        let displayText: ReactNode = '';
+        if (selectedKeys.size) {
+            const displayPath = this.foundation.getItemPropPath([...selectedKeys][0], displayProp);
+            if (displayRender && typeof displayRender === 'function') {
+                displayText = displayRender(displayPath);
+            } else {
+                displayText = displayPath.map((path: ReactNode, index: number)=>(
+                    <Fragment key={`${path}-${index}`}>
+                        {
+                            index<displayPath.length-1
+                                ? <>{path}{separator}</>
+                                : path
+                        }
+                    </Fragment>
+                ));
+            }
+        }
+        return displayText;
+    }
+
     renderSelectContent = () => {
         const { placeholder, filterTreeNode, multiple } = this.props;
-        const { selectedKeys, checkedKeys } = this.state;
+        const { checkedKeys } = this.state;
         const searchable = Boolean(filterTreeNode);
         if (!searchable) {
             if (multiple) {
@@ -722,11 +745,9 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
                 }
                 return this.renderMultipleTags();
             } else {
-                const displayText = selectedKeys.size ?
-                    this.foundation.renderDisplayText([...selectedKeys][0]) :
-                    '';
+                const displayText = this.renderDisplayText();
                 const spanCls = cls({
-                    [`${prefixcls}-selection-placeholder`]: !displayText || !displayText.length,
+                    [`${prefixcls}-selection-placeholder`]: !displayText,
                 });
                 return <span className={spanCls}>{displayText ? displayText : placeholder}</span>;
             }