浏览代码

fix: single select will flush when filter is true, close #1207 (#1276)

* fix: single select will flush when filter is true, close #1207
Co-authored-by: zhangyumei.0319 <[email protected]>
pointhalo 2 年之前
父节点
当前提交
a1aba4ae8d

+ 4 - 29
.vscode/launch.json

@@ -11,9 +11,9 @@
             "runtimeArgs": [
                 "--inspect-brk",
                 "${workspaceRoot}/node_modules/.bin/jest",
-                "${workspaceRoot}/packages/semi-ui/form/", // 需要调试哪个组件,替换文件夹路径即可
+                "${workspaceRoot}/packages/semi-ui/select/", // 需要调试哪个组件,替换文件夹路径即可
                 "--runInBand",
-                "--coverage",
+                // "--coverage",
                 "--silent" // ignore warning such as componentWillReceiveProps will be abondon...
             ],
             "env": {
@@ -21,37 +21,12 @@
                 // "type": "story" // 调试snapshot快照的时候用这个
                 "type": "unit"     // 调试unitTest的时候用这个
             },
+            "runtimeExecutable": "/Users/bytedance/.nvm/versions/node/v16.17.1/bin/node",
             "console": "integratedTerminal",
             "internalConsoleOptions": "neverOpen",
             "port": 9229
         },
-        {
-            "type": "node",
-            "request": "launch",
-            "name": "compile css",
-            "env": {
-                "NODE_ENV": "dev"
-            },
-            "runtimeExecutable": "node",
-            "program": "${workspaceFolder}/scripts/build-css.js",
-            "restart": true,
-            "console": "integratedTerminal",
-            "internalConsoleOptions": "neverOpen"
-        },
-        {
-            "type": "node",
-            "request": "launch",
-            "runtimeArgs": [
-            ],
-            "name": "debug snapshot update",
-            "env": {
-                "NODE_ENV": "dev"
-            },
-            "runtimeExecutable": "node",
-            "program": "${workspaceFolder}/scripts/snapshotUpdate.js",
-            "restart": true,
-            "console": "integratedTerminal",
-        },
+
         {
             "name": "Debug StorySnapShot",
             "type": "node",

+ 34 - 14
packages/semi-foundation/select/foundation.ts

@@ -50,7 +50,11 @@ export interface SelectAdapter<P = Record<string, any>, S = Record<string, any>>
     getFocusableElements(node: any): any[];
     getActiveElement(): any;
     setIsFocusInContainer(isFocusInContainer: boolean): void;
-    getIsFocusInContainer(): boolean
+    getIsFocusInContainer(): boolean;
+    on(eventName: string, eventCallback: () => void): void;
+    off(eventName: string): void;
+    emit(eventName: string): void;
+    once(eventName: string, eventCallback: () => void): void
 }
 
 type LabelValue = string | number;
@@ -364,14 +368,8 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
         }
     }
 
-    close(e?: any) {
+    close(e?: any, closeCb?: () => void) {
         // to support A11y, closing the panel trigger does not necessarily lose focus
-        const isFilterable = this._isFilterable();
-        if (isFilterable) {
-            // this.unBindKeyBoardEvent();
-            this.clearInput();
-            this.toggle2SearchInput(false);
-        }
 
         this._adapter.closeMenu();
         this._adapter.notifyDropdownVisibleChange(false);
@@ -380,6 +378,20 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
         // this._notifyBlur(e);
         this._adapter.unregisterClickOutsideHandler();
         // this._adapter.updateFocusState(false);
+
+        const isFilterable = this._isFilterable();
+        if (isFilterable) {
+            this.toggle2SearchInput(false);
+        }
+
+        this._adapter.once('popoverClose', () => {
+            if (isFilterable) {
+                this.clearInput();
+            }
+            if (closeCb) {
+                closeCb();
+            }
+        });
     }
 
     onSelect(option: BasicOptionProps, optionIndex: number, event: MouseEvent | KeyboardEvent) {
@@ -407,18 +419,22 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
         const selections = new Map().set(label, { value, label, ...rest });
         // First trigger onSelect, then trigger onChange
         this._notifySelect(value, { value, label, ...rest });
-
         // If it is a controlled component, directly notify
+        // Make sure that the operations of updating updateOptions are done after the animation ends
+        // otherwise the content will be updated when the popup layer is not collapsed, and it looks like it will flash once when it is closed
         if (this._isControlledComponent()) {
-            this._notifyChange(selections);
-            this.close(event);
+            this.close(event, () => {
+                this._notifyChange(selections);
+            });
         } else {
             this._adapter.updateSelection(selections);
             // notify user
             this._notifyChange(selections);
-            // Update the selected item in the drop-down box
-            this.close(event);
-            this.updateOptionsActiveStatus(selections);
+
+            this.close(event, () => {
+                // Update the selected item in the drop-down box
+                this.updateOptionsActiveStatus(selections);
+            });
         }
     }
 
@@ -1093,4 +1109,8 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
             });
         }
     }
+
+    handlePopoverClose() {
+        this._adapter.emit('popoverClose');
+    }
 }

+ 1 - 1
packages/semi-foundation/utils/Event.ts

@@ -23,7 +23,7 @@ export default class Event {
         }
     }
 
-    off(event: string, callback: null | (() => void)) {
+    off(event: string, callback?: null | (() => void)) {
         if (event) {
             if (typeof callback === 'function') {
                 const callbacks = this._eventMap.get(event);

+ 2 - 0
packages/semi-ui/select/_story/select.stories.jsx

@@ -397,6 +397,8 @@ export const WithPrefixSuffixInsetLabelShowClearShowArrow = () => (
       style={{
         width: '250px',
       }}
+      motion={false}
+      filter
       optionList={options}
       prefix={<IconSearch />}
       suffix={<IconGift></IconGift>}

+ 8 - 0
packages/semi-ui/select/index.tsx

@@ -14,6 +14,7 @@ import TagGroup from '../tag/group';
 import LocaleConsumer from '../locale/localeConsumer';
 import Popover, { PopoverProps } from '../popover/index';
 import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/popover/constants';
+import Event from '@douyinfe/semi-foundation/utils/Event';
 import { FixedSizeList as List } from 'react-window';
 import { getOptionsFromGroup } from './utils';
 import VirtualRow from './virtualRow';
@@ -333,6 +334,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
     clickOutsideHandler: (e: MouseEvent) => void;
     foundation: SelectFoundation;
     context: ContextValue;
+    eventManager: Event;
 
     constructor(props: SelectProps) {
         super(props);
@@ -367,6 +369,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         this.onMouseLeave = this.onMouseLeave.bind(this);
         this.renderOption = this.renderOption.bind(this);
         this.onKeyPress = this.onKeyPress.bind(this);
+        this.eventManager = new Event();
 
         this.foundation = new SelectFoundation(this.adapter);
 
@@ -457,6 +460,10 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             ...keyboardAdapter,
             ...filterAdapter,
             ...multipleAdapter,
+            on: (eventName, eventCallback) => this.eventManager.on(eventName, eventCallback),
+            off: (eventName) => this.eventManager.off(eventName),
+            once: (eventName, eventCallback) => this.eventManager.once(eventName, eventCallback),
+            emit: (eventName) => this.eventManager.emit(eventName),
             // Collect all subitems, each item is visible by default when collected, and is not selected
             getOptionsFromChildren: (children = this.props.children) => {
                 let optionGroups = [];
@@ -1285,6 +1292,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                 stopPropagation={stopPropagation}
                 disableArrowKeyDown={true}
                 onVisibleChange={status => this.handlePopoverVisibleChange(status)}
+                afterClose={() => this.foundation.handlePopoverClose()}
             >
                 {selection}
             </Popover>