Jelajahi Sumber

fix: fix Nav openKeys bug when selectedKeys change #2115 (#2123)

Co-authored-by: shijia.me <[email protected]>
Shi Jia 1 tahun lalu
induk
melakukan
911c0cb5a8

+ 8 - 0
cypress/e2e/navigation.spec.js

@@ -21,4 +21,12 @@ describe('navigation', () => {
         cy.get('.semi-navigation-item-selected').should('contain.text', 'Config management');
         cy.get('.semi-navigation-sub-title.semi-navigation-sub-title-selected').should('exist');
     });
+
+    it('selected keys change + also use state openKeys', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=navigation--fixed-open-keys&viewMode=story');
+        cy.get('.semi-navigation-item-text').contains('任务平台').click();
+        cy.get('.semi-navigation-item-text').contains('任务管理').click();
+        cy.get('.semi-navigation-list > li.semi-navigation-item-sub').eq(1).should('have.attr', 'aria-expanded', 'true');
+        cy.get('.semi-navigation-list > li.semi-navigation-item-sub').eq(2).should('have.attr', 'aria-expanded', 'true');
+    });
 });

+ 6 - 4
packages/semi-foundation/navigation/foundation.ts

@@ -114,7 +114,7 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
      * @param {*} lifecycle
      * @returns
      */
-    init(lifecycle: string) {
+    init(lifecycle?: string) {
         const { defaultSelectedKeys, selectedKeys } = this.getProps();
         let willSelectedKeys = selectedKeys || defaultSelectedKeys || [];
         const { itemKeysMap, willOpenKeys, formattedItems } = this.getCalcState();
@@ -168,6 +168,7 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
      */
     getWillOpenKeys(itemKeysMap: ItemKey2ParentKeysMap) {
         const { defaultOpenKeys, openKeys, defaultSelectedKeys, selectedKeys, mode } = this.getProps();
+        const { openKeys: stateOpenKeys = [] } = this.getStates();
 
         let willOpenKeys = openKeys || defaultOpenKeys || [];
         if (
@@ -175,12 +176,13 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
             Array.isArray(openKeys)) && mode === strings.MODE_VERTICAL && (Array.isArray(defaultSelectedKeys) || Array.isArray(selectedKeys))
         ) {
             const currentSelectedKeys = Array.isArray(selectedKeys) ? selectedKeys : defaultSelectedKeys;
-            willOpenKeys = this.getShouldOpenKeys(itemKeysMap, currentSelectedKeys);
+            willOpenKeys = stateOpenKeys.concat(this.getShouldOpenKeys(itemKeysMap, currentSelectedKeys));
+            willOpenKeys = Array.from(new Set(willOpenKeys));
         }
         return [...willOpenKeys];
     }
 
-    getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: string | number[]= []) {
+    getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: (string | number)[] = []) {
         const willOpenKeySet = new Set();
 
         if (Array.isArray(selectedKeys) && selectedKeys.length) {
@@ -200,7 +202,7 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
 
     destroy() {}
 
-    selectLevelZeroParentKeys(itemKeysMap: ItemKey2ParentKeysMap, ...itemKeys: (string | number)[]) {
+    selectLevelZeroParentKeys(itemKeysMap: ItemKey2ParentKeysMap, itemKeys: (string | number)[]) {
         const _itemKeysMap = isNullOrUndefined(itemKeysMap) ? this.getState('itemKeysMap') : itemKeysMap;
         // console.log(itemKeysMap);
 

+ 81 - 0
packages/semi-ui/navigation/_story/FixedOpenKeys/index.tsx

@@ -0,0 +1,81 @@
+import React, { useState, useMemo, useCallback } from 'react';
+import { IconUser, IconStar, IconUserGroup, IconEdit, IconSetting } from '@douyinfe/semi-icons';
+import { Nav, Button } from '../../../index';
+
+/**
+ * test with cypress
+ */
+export default function Demo() {
+    const [selectedKeys, setSelectedKeys] = useState(['入驻审核']);
+    const navItems = useMemo(
+        () => [
+            { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
+            { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+            {
+                itemKey: 'union-management',
+                text: '公会管理',
+                icon: <IconUserGroup />,
+                items: ['公告设置', '公会查询', '信息录入'],
+            },
+            {
+                itemKey: 'approve-management',
+                text: '审批管理',
+                icon: <IconEdit />,
+                items: [
+                    '入驻审核',
+                    {
+                        itemKey: 'operation-management',
+                        text: '运营管理',
+                        items: ['人员管理', '人员变更'],
+                    },
+                ],
+            },
+            {
+                text: '任务平台',
+                icon: <IconSetting />,
+                itemKey: 'job',
+                items: ['任务管理', '用户任务查询'],
+            },
+        ],
+        []
+    );
+    const navItemKeys = useMemo(
+        () =>
+            navItems.reduce((prev, item) => {
+                const que = [item];
+                while (que.length) {
+                    const cur = que.pop();
+                    if (cur) {
+                        if (typeof cur === 'object') {
+                            if (Array.isArray(cur.items) && cur.items.length) {
+                                que.push(...cur.items);
+                            } else if (cur.itemKey) {
+                                prev.push(cur.itemKey);
+                            }
+                        } else {
+                            prev.push(cur);
+                        }
+                    }
+                }
+                return prev;
+            }, []),
+        [navItems]
+    );
+
+    const randomSelect = useCallback(() => {
+        const randomIndex = Math.floor(Math.random() * navItemKeys.length);
+        setSelectedKeys([navItemKeys[randomIndex]]);
+    }, [navItemKeys]);
+
+    const handleSelect = (...args) => {
+        console.log(...args);
+        setSelectedKeys(args[0].selectedKeys);
+    };
+
+    return (
+        <div style={{ height: '100vh', display: 'inline-block' }}>
+            <Button onClick={randomSelect}>随机选中</Button>
+            <Nav onSelect={handleSelect} selectedKeys={selectedKeys} items={navItems} />
+        </div>
+    );
+}

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

@@ -14,6 +14,7 @@ import Button from '../../button';
 import GetPopupNav from './Popup';
 import CustomArrowIcon from './CustomIcon';
 import FixedSelectedKeys from './FixedSelectedKeys';
+import FixedOpenKeys from './FixedOpenKeys';
 
 import {
   IconMail,
@@ -40,6 +41,7 @@ export default {
 
 export {
   FixedSelectedKeys,
+  FixedOpenKeys
 }
 
 export const Default = () => {

+ 1 - 0
packages/semi-ui/navigation/index.tsx

@@ -177,6 +177,7 @@ class Nav extends BaseComponent<NavProps, NavState> {
     });
 
     itemsChanged: boolean;
+    foundation: NavigationFoundation;
     constructor(props: NavProps) {
         super(props);
         this.foundation = new NavigationFoundation(this.adapter);