Browse Source

fix: Fixed when TreeSelect is in defaultOpen mode, the pop-up layer cannot be closed when clicking outside

zhangyumei.0319 1 year ago
parent
commit
771e7a0bcc

+ 86 - 0
cypress/e2e/treeSelect.spec.js

@@ -108,5 +108,91 @@ describe('treeSelect', () => {
         cy.get('.semi-tree-select').eq(1).click();
         cy.get('.semi-tree-select-popover').should('exist');
     });
+
+    it("unRelated + async load", () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=treeselect--un-related-and-async-load');
+        cy.get('.semi-checkbox').eq(0).get('.semi-checkbox-inner-checked').should("exist");
+        cy.get('.semi-icon-tree_triangle_down').eq(0).trigger('click');
+        // sync load, 1000ms
+        cy.wait(1000);
+        cy.get('.semi-checkbox').eq(0).get('.semi-checkbox-inner-checked').should("exist");
+    });
+
+    it('expanded controlled + showFilteredOnly', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=treeselect--issue-1542');
+        cy.get('.semi-tree-select-selection').eq(0).trigger('click');
+        cy.get('.semi-tree-select-inputTrigger').eq(0).children(".semi-input").eq(0).type('b');
+        // showFilteredOnly,因此搜索后的选项应该只有 3 项
+        cy.get('.semi-tree-option').should('have.length', 3);
+        // 清空搜索框,由 state 中的 expandedKeys 决定展示展示项,应该只有两项
+        cy.get('.semi-tree-select-inputTrigger').eq(0).children(".semi-input").eq(0).clear();
+        cy.get('.semi-tree-option').should('have.length', 2);
+        cy.get('.semi-tree-select-inputTrigger').eq(0).children(".semi-input").eq(0).type('s');
+        cy.get('.semi-tree-option').should('have.length', 9);
+        // 搜索状态下,输入框有值,被筛选的选项点击展开按钮行为正常
+        cy.get('.semi-tree-option').eq(1).children('.semi-icon-tree_triangle_down').eq(0).trigger('click');
+        cy.get('.semi-tree-option').should('have.length', 6);
+        cy.get('body').click();
+        // 等待弹出层收起
+        cy.wait(500);
+        cy.get('.semi-tree-select-selection').eq(0).trigger('click');
+        // 等待弹出层展开
+        cy.wait(500);
+        cy.get('.semi-tree-option').should('have.length', 2);
+        cy.get('.semi-tree-select-inputTrigger').eq(0).children(".semi-input").eq(0).type('o');
+        cy.get('.semi-tree-option').should('have.length', 4);
+        cy.get('.semi-tree-option').eq(3).trigger('click');
+        cy.wait(1000);
+        cy.get('.semi-tree-select-selection').eq(0).trigger('click');
+        cy.wait(1000);
+        // 此时展开项目由选中项和原来的 state 中的 expandedKeys 决定
+        cy.get('.semi-tree-option').should('have.length', 2);
+        cy.get('.semi-icon-tree_triangle_down').eq(0).trigger('click');
+        cy.get('.semi-tree-option').should('have.length', 4);
+    });
+
+    it('filterTreeNode + loadData', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=treeselect--load-data');
+        cy.get('.semi-tree-select-selection').eq(0).trigger('click');
+        cy.wait(1000);
+        cy.get('.semi-tree-option').eq(0).trigger('click');
+        cy.get('.semi-tree-option-list').should('not.exist');
+        cy.get('.semi-tree-select-selection').eq(0).trigger('click');
+        cy.wait(1000);
+        cy.get('.semi-tree-option.semi-tree-option-level-1.semi-tree-option-selected.semi-tree-option-collapsed').should('exist');
+    });
+
+    it('multiple, checkRelation = unRelated, triggerRender', () => {
+        cy.visit("http://127.0.0.1:6006/iframe.html?id=treeselect--trigger-render-add-method");
+        cy.get('.semi-tagInput').eq(1).trigger('click');
+        cy.get('.semi-tree-option').eq(2).trigger('click');
+        cy.get('.semi-tree-option').eq(3).trigger('click');
+        cy.get('.semi-tagInput-wrapper').eq(1).get('.semi-tag-content').should('have.length', 2);
+        cy.get('.semi-tagInput-wrapper').eq(1).get('.semi-tag-content').eq(0).contains('北京');
+        cy.get('.semi-tagInput-wrapper').eq(1).get('.semi-tag-content').eq(1).contains('上海');
+    });
+
+    it('multiple, checkRelation = related, triggerRender', () => {
+        cy.visit("http://127.0.0.1:6006/iframe.html?id=treeselect--trigger-render-add-method");
+        cy.get('.semi-tagInput').eq(0).trigger('click');
+        cy.get('.semi-tree-option').eq(2).trigger('click');
+        cy.get('.semi-tree-option').eq(3).trigger('click');
+        cy.get('.semi-tagInput-wrapper').eq(1).get('.semi-tag-content').should('have.length', 1);
+        cy.get('.semi-tagInput-wrapper').eq(1).get('.semi-tag-content').eq(0).contains('亚洲');
+    });
+
+    it('single, triggerRender', () => {
+        cy.visit("http://127.0.0.1:6006/iframe.html?id=treeselect--trigger-render-add-method");
+        cy.get('.semi-button').eq(0).trigger('click');
+        cy.get('.semi-tree-option').eq(0).trigger('click');
+        cy.get('.semi-button-content-left').eq(0).contains('亚洲');
+    })
+
+    it('default open', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=treeselect--multiple')
+        cy.get('.semi-tree-select-popover').should('have.length', 1);
+        cy.get('#invisible-span').eq(0).trigger('mousedown');
+        cy.get('.semi-tree-select-popover').should('not.exist');
+    })
 });
 

+ 10 - 4
packages/semi-foundation/treeSelect/foundation.ts

@@ -212,11 +212,13 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
         const triggerSearch = searchPosition === strings.SEARCH_POSITION_TRIGGER && filterTreeNode;
         const triggerSearchAutoFocus = searchAutoFocus && triggerSearch;
         this._setDropdownWidth();
-        const isOpen = (this.getProp('defaultOpen') || triggerSearchAutoFocus) && !this._isDisabled();
+        const able = !this._isDisabled();
+        const isOpen = (this.getProp('defaultOpen') || triggerSearchAutoFocus) && able;
         if (isOpen) {
             this.open();
+            this._registerClickOutsideHandler();
         }
-        if (triggerSearchAutoFocus) {
+        if (triggerSearchAutoFocus && able) {
             this.handleTriggerFocus(null);
         }
     }
@@ -433,7 +435,7 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
         }
     }
 
-    _registerClickOutsideHandler = (e) => {
+    _registerClickOutsideHandler = () => {
         this._adapter.registerClickOutsideHandler(e => {
             this.handlerTriggerBlur(e);
             this.close(e);
@@ -449,7 +451,7 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
     handleTriggerFocus(e: any) {
         this._adapter.updateIsFocus(true);
         this._notifyFocus(e);
-        this._registerClickOutsideHandler(e);
+        this._registerClickOutsideHandler();
     }
 
     // Scenes that may trigger blur
@@ -461,6 +463,10 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
     }
 
     handlerTriggerBlur(e) {
+        const isFocus = this.getState('isFocus');
+        if (!isFocus) {
+            return;
+        }
         this._adapter.updateIsFocus(false);
         this._notifyBlur(e);
         this._adapter.unregisterClickOutsideHandler();

+ 1 - 0
packages/semi-ui/treeSelect/_story/treeSelect.stories.jsx

@@ -541,6 +541,7 @@ export const Multiple = () => (
       multiple
       placeholder="Please select"
     />
+    <span id={'invisible-span'} style={{ width: 10, height: 10, position: 'fixed', top: 0, right: 0 }} />
   </div>
 );
 

+ 4 - 0
packages/semi-ui/treeSelect/index.tsx

@@ -538,6 +538,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         | 'rePositionDropdown'
         > = {
             registerClickOutsideHandler: cb => {
+                this.adapter.unregisterClickOutsideHandler();
                 const clickOutsideHandler = (e: Event) => {
                     const optionInstance = this.optionsRef && this.optionsRef.current as React.ReactInstance;
                     const triggerDom = this.triggerRef && this.triggerRef.current;
@@ -560,6 +561,9 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 document.addEventListener('mousedown', clickOutsideHandler, false);
             },
             unregisterClickOutsideHandler: () => {
+                if (!this.clickOutsideHandler) {
+                    return;
+                }
                 document.removeEventListener('mousedown', this.clickOutsideHandler, false);
                 this.clickOutsideHandler = null;
             },