From 08a3ff65c795802a9ab4c15309895fc5e4c28512 Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Tue, 13 Jun 2023 15:16:46 -0600 Subject: [PATCH] Updated menu component to use mds (#2866) Signed-off-by: Benjamin Perez --- portal-ui/src/screens/Console/Console.tsx | 4 +- .../screens/Console/Menu/ConsoleMenuList.tsx | 183 ------------- .../src/screens/Console/Menu/LicenseBadge.tsx | 65 ----- portal-ui/src/screens/Console/Menu/Menu.tsx | 126 --------- .../src/screens/Console/Menu/MenuWrapper.tsx | 74 ++++++ portal-ui/src/screens/Console/Menu/types.ts | 16 +- .../src/screens/Console/kbar-actions.tsx | 8 +- .../{valid-routes.ts => valid-routes.tsx} | 242 ++++++++---------- .../test-fix-ui-crash-for-policy.ts | 10 +- .../tests/permissions-2/site-replication.ts | 5 +- portal-ui/tests/utils/elements-menu.ts | 99 +++---- portal-ui/yarn.lock | 10 +- 12 files changed, 242 insertions(+), 600 deletions(-) delete mode 100644 portal-ui/src/screens/Console/Menu/ConsoleMenuList.tsx delete mode 100644 portal-ui/src/screens/Console/Menu/LicenseBadge.tsx delete mode 100644 portal-ui/src/screens/Console/Menu/Menu.tsx create mode 100644 portal-ui/src/screens/Console/Menu/MenuWrapper.tsx rename portal-ui/src/screens/Console/{valid-routes.ts => valid-routes.tsx} (55%) diff --git a/portal-ui/src/screens/Console/Console.tsx b/portal-ui/src/screens/Console/Console.tsx index b9573f22d..617fefbb7 100644 --- a/portal-ui/src/screens/Console/Console.tsx +++ b/portal-ui/src/screens/Console/Console.tsx @@ -33,7 +33,6 @@ import { useSelector } from "react-redux"; import { AppState, useAppDispatch } from "../../store"; import { snackBarCommon } from "./Common/FormComponents/common/styleLibrary"; import { ErrorResponseHandler } from "../../common/types"; -import Menu from "./Menu/Menu"; import api from "../../common/api"; import MainError from "./Common/MainError/MainError"; import { @@ -55,6 +54,7 @@ import { setSnackBarMessage, } from "../../systemSlice"; import { selFeatures, selSession } from "./consoleSlice"; +import MenuWrapper from "./Menu/MenuWrapper"; const Trace = React.lazy(() => import("./Trace/Trace")); const Heal = React.lazy(() => import("./Heal/Heal")); @@ -481,7 +481,7 @@ const Console = ({ classes }: IConsoleProps) => { {session && session.status === "ok" ? ( : } + menu={!hideMenu ? : } mobileModeAuto={false} > diff --git a/portal-ui/src/screens/Console/Menu/ConsoleMenuList.tsx b/portal-ui/src/screens/Console/Menu/ConsoleMenuList.tsx deleted file mode 100644 index e195969f3..000000000 --- a/portal-ui/src/screens/Console/Menu/ConsoleMenuList.tsx +++ /dev/null @@ -1,183 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment, useEffect, useState } from "react"; -import { Box } from "@mui/material"; -import { useLocation } from "react-router-dom"; -import ListItem from "@mui/material/ListItem"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import { LogoutIcon } from "mds"; -import ListItemText from "@mui/material/ListItemText"; -import List from "@mui/material/List"; -import { - LogoutItemIconStyle, - menuItemContainerStyles, - menuItemMiniStyles, - menuItemTextStyles, -} from "./MenuStyleUtils"; -import MenuItem from "./MenuItem"; - -import { IAM_PAGES } from "../../../common/SecureComponent/permissions"; -import MenuSectionHeader from "./MenuSectionHeader"; - -const ConsoleMenuList = ({ - menuItems, - isOpen, - displayHeaders = false, -}: { - menuItems: any[]; - isOpen: boolean; - displayHeaders?: boolean; -}) => { - const stateClsName = isOpen ? "wide" : "mini"; - const { pathname = "" } = useLocation(); - let groupToSelect = pathname.slice(1, pathname.length); //single path - if (groupToSelect.indexOf("/") !== -1) { - groupToSelect = groupToSelect.slice(0, groupToSelect.indexOf("/")); //nested path - } - - const [expandGroup, setExpandGroup] = useState(IAM_PAGES.BUCKETS); - const [selectedMenuItem, setSelectedMenuItem] = - useState(groupToSelect); - - const [previewMenuGroup, setPreviewMenuGroup] = useState(""); - - useEffect(() => { - setExpandGroup(groupToSelect); - setSelectedMenuItem(groupToSelect); - }, [groupToSelect]); - - let basename = document.baseURI.replace(window.location.origin, ""); - let header = ""; - - return ( - - - - {(menuItems || []).map((menuGroup: any, index) => { - if (menuGroup) { - let grHeader = null; - - if (menuGroup.group !== header && displayHeaders) { - grHeader = ; - header = menuGroup.group; - } - - return ( - - {grHeader} - - - ); - } - return null; - })} - - - {/* List of Bottom anchored menus */} - - - - - - - - - - ); -}; -export default ConsoleMenuList; diff --git a/portal-ui/src/screens/Console/Menu/LicenseBadge.tsx b/portal-ui/src/screens/Console/Menu/LicenseBadge.tsx deleted file mode 100644 index 03fcf3ac9..000000000 --- a/portal-ui/src/screens/Console/Menu/LicenseBadge.tsx +++ /dev/null @@ -1,65 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { useSelector } from "react-redux"; -import { AppState } from "../../../store"; -import { Box } from "@mui/material"; -import { CircleIcon } from "mds"; -import { getLicenseConsent } from "../License/utils"; -import { registeredCluster } from "../../../config"; - -const LicenseBadge = () => { - const licenseInfo = useSelector( - (state: AppState) => state?.system?.licenseInfo - ); - - const isAgplAckDone = getLicenseConsent(); - const clusterRegistered = registeredCluster(); - - const { plan = "" } = licenseInfo || {}; - - if (plan || isAgplAckDone || clusterRegistered) { - return null; - } - - return ( - - - - ); -}; - -export default LicenseBadge; diff --git a/portal-ui/src/screens/Console/Menu/Menu.tsx b/portal-ui/src/screens/Console/Menu/Menu.tsx deleted file mode 100644 index 6a4295f5b..000000000 --- a/portal-ui/src/screens/Console/Menu/Menu.tsx +++ /dev/null @@ -1,126 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { useSelector } from "react-redux"; -import { Drawer } from "@mui/material"; -import withStyles from "@mui/styles/withStyles"; -import { Theme } from "@mui/material/styles"; -import createStyles from "@mui/styles/createStyles"; -import clsx from "clsx"; -import { AppState, useAppDispatch } from "../../../store"; - -import MenuToggle from "./MenuToggle"; -import ConsoleMenuList from "./ConsoleMenuList"; -import { validRoutes } from "../valid-routes"; -import { menuOpen } from "../../../systemSlice"; -import { selFeatures } from "../consoleSlice"; - -const drawerWidth = 250; - -const styles = (theme: Theme) => - createStyles({ - drawer: { - width: drawerWidth, - flexShrink: 0, - whiteSpace: "nowrap", - background: - "transparent linear-gradient(90deg, #073052 0%, #081C42 100%) 0% 0% no-repeat padding-box !important", - boxShadow: "0px 3px 7px #00000014", - "& .MuiPaper-root": { - backgroundColor: "inherit", - }, - "& ::-webkit-scrollbar": { - width: "5px", - }, - "& ::-webkit-scrollbar-track": { - background: "#F0F0F0", - borderRadius: 0, - boxShadow: "inset 0px 0px 0px 0px #F0F0F0", - }, - "& ::-webkit-scrollbar-thumb": { - background: "#5A6375", - borderRadius: 0, - }, - "& ::-webkit-scrollbar-thumb:hover": { - background: "#081C42", - }, - }, - drawerOpen: { - width: drawerWidth, - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen, - }), - }, - drawerClose: { - transition: theme.transitions.create("width", { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - overflowX: "hidden", - [theme.breakpoints.up("xs")]: { - width: 75, - }, - }, - }); - -interface IMenuProps { - classes?: any; -} - -const Menu = ({ classes }: IMenuProps) => { - const dispatch = useAppDispatch(); - const features = useSelector(selFeatures); - - const sidebarOpen = useSelector( - (state: AppState) => state.system.sidebarOpen - ); - - const allowedMenuItems = validRoutes(features); - - return ( - - { - dispatch(menuOpen(nextState)); - }} - isOpen={sidebarOpen} - /> - - - - ); -}; - -export default withStyles(styles)(Menu); diff --git a/portal-ui/src/screens/Console/Menu/MenuWrapper.tsx b/portal-ui/src/screens/Console/Menu/MenuWrapper.tsx new file mode 100644 index 000000000..7f5327345 --- /dev/null +++ b/portal-ui/src/screens/Console/Menu/MenuWrapper.tsx @@ -0,0 +1,74 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2023 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React from "react"; +import { useSelector } from "react-redux"; +import { Menu } from "mds"; +import { AppState, useAppDispatch } from "../../../store"; +import { validRoutes } from "../valid-routes"; +import { menuOpen } from "../../../systemSlice"; +import { selFeatures } from "../consoleSlice"; +import { getLogoVar, registeredCluster } from "../../../config"; +import { useLocation, useNavigate } from "react-router-dom"; +import { getLicenseConsent } from "../License/utils"; + +const MenuWrapper = () => { + const dispatch = useAppDispatch(); + const features = useSelector(selFeatures); + const navigate = useNavigate(); + const { pathname = "" } = useLocation(); + + const sidebarOpen = useSelector( + (state: AppState) => state.system.sidebarOpen + ); + const licenseInfo = useSelector( + (state: AppState) => state?.system?.licenseInfo + ); + + const isAgplAckDone = getLicenseConsent(); + const clusterRegistered = registeredCluster(); + + const { plan = "" } = licenseInfo || {}; + + let licenseNotification = true; + if (plan || isAgplAckDone || clusterRegistered) { + licenseNotification = false; + } + + const allowedMenuItems = validRoutes(features, licenseNotification); + + return ( + { + navigate(path); + }} + signOutAction={() => { + navigate("/logout"); + }} + collapseAction={() => { + dispatch(menuOpen(!sidebarOpen)); + }} + currentPath={pathname} + mobileModeAuto={false} + /> + ); +}; + +export default MenuWrapper; diff --git a/portal-ui/src/screens/Console/Menu/types.ts b/portal-ui/src/screens/Console/Menu/types.ts index 07179d4ea..edc17d6f1 100644 --- a/portal-ui/src/screens/Console/Menu/types.ts +++ b/portal-ui/src/screens/Console/Menu/types.ts @@ -14,21 +14,13 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -export interface IMenuItem { - group?: string; - type?: string; - component?: any; - to?: string; - name: string; - id?: string; - icon: any; - onClick?: any; +import { MenuItemProps } from "mds"; + +export interface IMenuItem extends MenuItemProps { forceDisplay?: boolean; - extraMargin?: boolean; fsHidden?: boolean; - customPermissionFnc?: any; + customPermissionFnc?: () => boolean; children?: IMenuItem[]; - badge?: any; } export interface IRouteRule { diff --git a/portal-ui/src/screens/Console/kbar-actions.tsx b/portal-ui/src/screens/Console/kbar-actions.tsx index aec5ed139..d80976429 100644 --- a/portal-ui/src/screens/Console/kbar-actions.tsx +++ b/portal-ui/src/screens/Console/kbar-actions.tsx @@ -34,8 +34,8 @@ export const routesAsKbarActions = ( id: `${childI.id}`, name: childI.name, section: i.name, - perform: () => navigate(`${childI.to}`), - icon: , + perform: () => navigate(`${childI.path}`), + icon: childI.icon, }; initialActions.push(a); } @@ -44,8 +44,8 @@ export const routesAsKbarActions = ( id: `${i.id}`, name: i.name, section: "Navigation", - perform: () => navigate(`${i.to}`), - icon: , + perform: () => navigate(`${i.path}`), + icon: i.icon, }; initialActions.push(a); } diff --git a/portal-ui/src/screens/Console/valid-routes.ts b/portal-ui/src/screens/Console/valid-routes.tsx similarity index 55% rename from portal-ui/src/screens/Console/valid-routes.ts rename to portal-ui/src/screens/Console/valid-routes.tsx index 951475819..f192bb2be 100644 --- a/portal-ui/src/screens/Console/valid-routes.ts +++ b/portal-ui/src/screens/Console/valid-routes.tsx @@ -14,8 +14,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import React from "react"; import { IMenuItem } from "./Menu/types"; -import { NavLink } from "react-router-dom"; import { adminUserPermissions, CONSOLE_UI_RESOURCE, @@ -52,296 +52,272 @@ import { WatchIcon, } from "mds"; import { hasPermission } from "../../common/SecureComponent"; -import React from "react"; -import LicenseBadge from "./Menu/LicenseBadge"; import EncryptionIcon from "../../icons/SidebarMenus/EncryptionIcon"; import EncryptionStatusIcon from "../../icons/SidebarMenus/EncryptionStatusIcon"; import { LockOpen, Login } from "@mui/icons-material"; -export const validRoutes = (features: string[] | null | undefined) => { +const permissionsValidation = (item: IMenuItem) => { + return ( + ((item.customPermissionFnc + ? item.customPermissionFnc() + : hasPermission( + CONSOLE_UI_RESOURCE, + IAM_PAGES_PERMISSIONS[item.path ?? ""] + )) || + item.forceDisplay) && + !item.fsHidden + ); +}; + +const validateItem = (item: IMenuItem) => { + // We clean up child items first + if (item.children && item.children.length > 0) { + const childArray: IMenuItem[] = item.children.reduce( + (acc: IMenuItem[], item) => { + if (!validateItem(item)) { + return [...acc]; + } + + return [...acc, item]; + }, + [] + ); + + const ret = { ...item, children: childArray }; + + return ret; + } + + if (permissionsValidation(item)) { + return item; + } + + return false; +}; + +export const validRoutes = ( + features: string[] | null | undefined, + licenseNotification: boolean = false +) => { const ldapIsEnabled = (features && features.includes("ldap-idp")) || false; const kmsIsEnabled = (features && features.includes("kms")) || false; + let consoleMenus: IMenuItem[] = [ { group: "User", name: "Object Browser", id: "object-browser", - component: NavLink, - to: IAM_PAGES.OBJECT_BROWSER_VIEW, - icon: ObjectBrowserIcon, + path: IAM_PAGES.OBJECT_BROWSER_VIEW, + icon: , forceDisplay: true, - children: [], }, { group: "User", - component: NavLink, id: "nav-accesskeys", - to: IAM_PAGES.ACCOUNT, + path: IAM_PAGES.ACCOUNT, name: "Access Keys", - icon: AccountsMenuIcon, + icon: , forceDisplay: true, }, { group: "User", - type: "item", - component: NavLink, - to: IAM_PAGES.DOCUMENTATION, + path: "https://min.io/docs/minio/linux/index.html?ref=con", name: "Documentation", - icon: DocumentationIcon, + icon: , forceDisplay: true, - onClick: ( - e: - | React.MouseEvent - | React.MouseEvent - | React.MouseEvent - ) => { - e.preventDefault(); - window.open( - "https://min.io/docs/minio/linux/index.html?ref=con", - "_blank", - "noopener" - ); - }, }, { group: "Administrator", name: "Buckets", id: "buckets", - component: NavLink, - to: IAM_PAGES.BUCKETS, - icon: BucketsMenuIcon, + path: IAM_PAGES.BUCKETS, + icon: , forceDisplay: true, - children: [], }, { group: "Administrator", name: "Policies", - component: NavLink, id: "policies", - to: IAM_PAGES.POLICIES, - icon: AccessMenuIcon, + path: IAM_PAGES.POLICIES, + icon: , }, { group: "Administrator", name: "Identity", id: "identity", - icon: IdentityMenuIcon, + icon: , children: [ { - component: NavLink, id: "users", - to: IAM_PAGES.USERS, + path: IAM_PAGES.USERS, customPermissionFnc: () => hasPermission(CONSOLE_UI_RESOURCE, adminUserPermissions) || hasPermission(S3_ALL_RESOURCES, adminUserPermissions) || hasPermission(CONSOLE_UI_RESOURCE, [IAM_SCOPES.ADMIN_ALL_ACTIONS]), name: "Users", - icon: UsersMenuIcon, + icon: , fsHidden: ldapIsEnabled, }, { - component: NavLink, id: "groups", - to: IAM_PAGES.GROUPS, + path: IAM_PAGES.GROUPS, name: "Groups", - icon: GroupsMenuIcon, + icon: , fsHidden: ldapIsEnabled, }, { name: "OpenID", - component: NavLink, id: "openID", - to: IAM_PAGES.IDP_OPENID_CONFIGURATIONS, - icon: LockOpen, + path: IAM_PAGES.IDP_OPENID_CONFIGURATIONS, + icon: , }, { name: "LDAP", - component: NavLink, id: "ldap", - to: IAM_PAGES.IDP_LDAP_CONFIGURATIONS, - icon: Login, + path: IAM_PAGES.IDP_LDAP_CONFIGURATIONS, + icon: , }, ], }, - { group: "Administrator", name: "Monitoring", id: "tools", - icon: MonitoringMenuIcon, + icon: , children: [ { name: "Metrics", id: "monitorMetrics", - to: IAM_PAGES.DASHBOARD, - icon: MetricsMenuIcon, - component: NavLink, + path: IAM_PAGES.DASHBOARD, + icon: , }, { name: "Logs ", id: "monitorLogs", - to: IAM_PAGES.TOOLS_LOGS, - icon: LogsMenuIcon, - component: NavLink, + path: IAM_PAGES.TOOLS_LOGS, + icon: , }, { name: "Audit", id: "monitorAudit", - to: IAM_PAGES.TOOLS_AUDITLOGS, - icon: AuditLogsMenuIcon, - component: NavLink, + path: IAM_PAGES.TOOLS_AUDITLOGS, + icon: , }, { name: "Trace", id: "monitorTrace", - to: IAM_PAGES.TOOLS_TRACE, - icon: TraceMenuIcon, - component: NavLink, + path: IAM_PAGES.TOOLS_TRACE, + icon: , }, { name: "Watch", - id: "watch", - component: NavLink, - icon: WatchIcon, - to: IAM_PAGES.TOOLS_WATCH, + id: "monitorWatch", + icon: , + path: IAM_PAGES.TOOLS_WATCH, }, { name: "Drives", id: "monitorDrives", - to: IAM_PAGES.TOOLS_HEAL, - icon: DrivesMenuIcon, - component: NavLink, + path: IAM_PAGES.TOOLS_HEAL, + icon: , }, { name: "Encryption", id: "monitorEncryption", - to: IAM_PAGES.KMS_STATUS, - icon: EncryptionStatusIcon, - component: NavLink, + path: IAM_PAGES.KMS_STATUS, + icon: , fsHidden: !kmsIsEnabled, }, ], }, { group: "Administrator", - component: NavLink, - to: IAM_PAGES.EVENT_DESTINATIONS, + path: IAM_PAGES.EVENT_DESTINATIONS, name: "Events", - icon: LambdaIcon, + icon: , id: "lambda", }, { group: "Administrator", - component: NavLink, - to: IAM_PAGES.TIERS, + path: IAM_PAGES.TIERS, name: "Tiering", - icon: TiersIcon, + icon: , id: "tiers", }, { group: "Administrator", - component: NavLink, - to: IAM_PAGES.SITE_REPLICATION, + path: IAM_PAGES.SITE_REPLICATION, name: "Site Replication", - icon: RecoverIcon, + icon: , id: "sitereplication", }, { group: "Administrator", - component: NavLink, - to: IAM_PAGES.KMS_KEYS, + path: IAM_PAGES.KMS_KEYS, name: "Encryption", - icon: EncryptionIcon, + icon: , id: "encryption", fsHidden: !kmsIsEnabled, }, { group: "Administrator", - component: NavLink, - to: IAM_PAGES.SETTINGS, + path: IAM_PAGES.SETTINGS, name: "Settings", id: "configurations", - icon: SettingsIcon, + icon: , }, { group: "Subscription", - component: NavLink, - to: IAM_PAGES.LICENSE, + path: IAM_PAGES.LICENSE, name: "License", id: "license", - icon: LicenseIcon, - badge: LicenseBadge, + icon: , + badge: licenseNotification, forceDisplay: true, }, { group: "Subscription", name: "Health", id: "diagnostics", - component: NavLink, - icon: HealthMenuIcon, - to: IAM_PAGES.TOOLS_DIAGNOSTICS, + icon: , + path: IAM_PAGES.TOOLS_DIAGNOSTICS, }, { group: "Subscription", name: "Performance", id: "performance", - component: NavLink, - icon: PerformanceMenuIcon, - to: IAM_PAGES.TOOLS_SPEEDTEST, + icon: , + path: IAM_PAGES.TOOLS_SPEEDTEST, }, { group: "Subscription", name: "Profile", id: "profile", - component: NavLink, - icon: ProfileMenuIcon, - to: IAM_PAGES.PROFILE, + icon: , + path: IAM_PAGES.PROFILE, }, { group: "Subscription", name: "Inspect", id: "inspectObjects", - to: IAM_PAGES.SUPPORT_INSPECT, - icon: InspectMenuIcon, - component: NavLink, + path: IAM_PAGES.SUPPORT_INSPECT, + icon: , }, { group: "Subscription", name: "Call Home", id: "callhome", - component: NavLink, - icon: CallHomeMenuIcon, - to: IAM_PAGES.CALL_HOME, + icon: , + path: IAM_PAGES.CALL_HOME, }, ]; - const allowedItems = consoleMenus.filter((item: IMenuItem) => { - if (item.children && item.children.length > 0) { - const c = item.children?.filter((childItem: IMenuItem) => { - return ( - ((childItem.customPermissionFnc - ? childItem.customPermissionFnc() - : hasPermission( - CONSOLE_UI_RESOURCE, - IAM_PAGES_PERMISSIONS[childItem.to ?? ""] - )) || - childItem.forceDisplay) && - !childItem.fsHidden - ); - }); - return c.length > 0; + return consoleMenus.reduce((acc: IMenuItem[], item) => { + const validation = validateItem(item); + if (!validation) { + return [...acc]; } - const res = - ((item.customPermissionFnc - ? item.customPermissionFnc() - : hasPermission( - CONSOLE_UI_RESOURCE, - IAM_PAGES_PERMISSIONS[item.to ?? ""] - )) || - item.forceDisplay) && - !item.fsHidden; - return res; - }); - return allowedItems; + return [...acc, validation]; + }, []); }; diff --git a/portal-ui/tests/permissions-1/test-fix-ui-crash-for-policy.ts b/portal-ui/tests/permissions-1/test-fix-ui-crash-for-policy.ts index 38d2f69d2..8b958aba0 100644 --- a/portal-ui/tests/permissions-1/test-fix-ui-crash-for-policy.ts +++ b/portal-ui/tests/permissions-1/test-fix-ui-crash-for-policy.ts @@ -16,6 +16,7 @@ import { Role, Selector } from "testcafe"; import { readFileSync } from "fs"; +import { getMenuElement } from "../utils/elements-menu"; const data = readFileSync(__dirname + "/../constants/timestamp.txt", "utf-8"); const $TIMESTAMP = data.trim(); @@ -39,14 +40,7 @@ const bucketsScreenUrl = `${testDomainUrl}/buckets`; const loginSubmitBtn = Selector("button").withAttribute("id", "do-login"); -export const bucketsSidebarEl = Selector(".MuiPaper-root") - .find("ul") - .child("#buckets"); - -export const menuListchildren = Selector("#tools-children"); -export const bucketsEl = menuListchildren - .find("a") - .withAttribute("href", "/buckets"); +export const bucketsSidebarEl = getMenuElement("buckets"); export const inspectAllowedRole = Role( loginUrl, diff --git a/portal-ui/tests/permissions-2/site-replication.ts b/portal-ui/tests/permissions-2/site-replication.ts index 0cce58d47..8c838b9c4 100644 --- a/portal-ui/tests/permissions-2/site-replication.ts +++ b/portal-ui/tests/permissions-2/site-replication.ts @@ -17,12 +17,11 @@ import * as roles from "../utils/roles"; import { IAM_PAGES } from "../../src/common/SecureComponent/permissions"; import { Selector } from "testcafe"; +import { getMenuElement } from "../utils/elements-menu"; let testDomainUrl = "http://localhost:9090"; const screenUrl = `${testDomainUrl}${IAM_PAGES.SITE_REPLICATION}`; -const siteReplicationEl = Selector(".MuiPaper-root") - .find("ul") - .child("#sitereplication"); +const siteReplicationEl = getMenuElement("sitereplication"); export const addSitesBtn = Selector("button").withText("Add Sites"); /* Begin Local Testing config block */ diff --git a/portal-ui/tests/utils/elements-menu.ts b/portal-ui/tests/utils/elements-menu.ts index 1ed73ab23..62b210610 100644 --- a/portal-ui/tests/utils/elements-menu.ts +++ b/portal-ui/tests/utils/elements-menu.ts @@ -15,85 +15,70 @@ // along with this program. If not, see . import { Selector } from "testcafe"; -import { IAM_PAGES } from "../../src/common/SecureComponent/permissions"; + +//---------------------------------------------------- +// Functions to get elements +//---------------------------------------------------- + +export const getMenuElement = (item) => { + return Selector("div.menuItems").find("button").withAttribute("id", item); +}; + +export const getSubmenuBlock = (item) => { + return getMenuElement(item).sibling("div.subItemsBox"); +}; //---------------------------------------------------- // General sidebar element //---------------------------------------------------- -export const sidebarItem = Selector(".MuiPaper-root").find("ul").child("a"); -export const logoutItem = Selector(".MuiPaper-root").find("ul").child("div"); +export const logoutItem = getMenuElement("sign-out"); //---------------------------------------------------- // Specific sidebar elements //---------------------------------------------------- -export const monitoringElement = Selector(".MuiPaper-root") - .find("ul") - .child("#tools"); -export const monitoringChildren = Selector("#tools-children"); +export const monitoringElement = getMenuElement("tools"); +export const monitoringChildren = getSubmenuBlock("tools"); + export const dashboardElement = monitoringChildren - .find("a") - .withAttribute("href", IAM_PAGES.DASHBOARD); + .find("button") + .withAttribute("id", "monitorMetrics"); export const logsElement = monitoringChildren - .find("a") - .withAttribute("href", "/tools/logs"); + .find("button") + .withAttribute("id", "monitorLogs"); export const traceElement = monitoringChildren - .find("a") - .withAttribute("href", "/tools/trace"); + .find("button") + .withAttribute("id", "monitorTrace"); export const drivesElement = monitoringChildren - .find("a") - .withAttribute("href", "/tools/heal"); + .find("button") + .withAttribute("id", "monitorDrives"); export const watchElement = monitoringChildren - .find("a") - .withAttribute("href", "/tools/watch"); + .find("button") + .withAttribute("id", "monitorWatch"); -export const bucketsElement = sidebarItem.withAttribute("href", "/buckets"); +export const bucketsElement = getMenuElement("buckets"); -export const serviceAcctsElement = sidebarItem.withAttribute( - "href", - IAM_PAGES.ACCOUNT -); +export const serviceAcctsElement = getMenuElement("nav-accesskeys"); -export const identityElement = Selector(".MuiPaper-root") - .find("ul") - .child("#identity"); -export const identityChildren = Selector("#identity-children"); +export const identityElement = getMenuElement("identity"); +export const identityChildren = getSubmenuBlock("identity"); export const usersElement = identityChildren - .find("a") - .withAttribute("href", IAM_PAGES.USERS); + .find("button") + .withAttribute("id", "users"); export const groupsElement = identityChildren - .find("a") - .withAttribute("href", IAM_PAGES.GROUPS); + .find("button") + .withAttribute("id", "groups"); -export const iamPoliciesElement = sidebarItem.withAttribute( - "href", - IAM_PAGES.POLICIES -); +export const iamPoliciesElement = getMenuElement("policies"); -export const configurationsElement = Selector(".MuiPaper-root") - .find("ul") - .child("#configurations"); +export const configurationsElement = getMenuElement("configurations"); -export const notificationEndpointsElement = Selector(".MuiPaper-root") - .find("ul") - .child("#lambda"); +export const notificationEndpointsElement = getMenuElement("lambda"); -export const tiersElement = Selector(".MuiPaper-root") - .find("ul") - .child("#tiers"); +export const tiersElement = getMenuElement("tiers"); -export const diagnosticsElement = Selector(".MuiPaper-root") - .find("ul") - .child("#diagnostics"); -export const performanceElement = Selector(".MuiPaper-root") - .find("ul") - .child("#performance"); -export const profileElement = Selector(".MuiPaper-root") - .find("ul") - .child("#profile"); -export const inspectElement = sidebarItem.withAttribute( - "href", - "/support/inspect" -); +export const diagnosticsElement = getMenuElement("diagnostics"); +export const performanceElement = getMenuElement("performance"); +export const inspectElement = getMenuElement("inspectObjects"); -export const licenseElement = sidebarItem.withAttribute("href", "/license"); +export const licenseElement = getMenuElement("license"); diff --git a/portal-ui/yarn.lock b/portal-ui/yarn.lock index 9035169a2..8ba84ea6f 100644 --- a/portal-ui/yarn.lock +++ b/portal-ui/yarn.lock @@ -382,7 +382,6 @@ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" @@ -4873,9 +4872,9 @@ destroy@1.2.0: integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detect-gpu@^5.0.27: - version "5.0.28" - resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.28.tgz#e7762c04cc3b5a33d902eb5719add195494df60a" - integrity sha512-sdT5Ti9ZHBBq39mK0DRwnm/5xZOVAz2+vxYLdPcFP83+3DGkzucEK0lzw1XFwct4zWDAXYrSTFUjC33qsoRAoQ== + version "5.0.27" + resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.27.tgz#821d9331c87e32568c483d85e12a9adee43d7bb2" + integrity sha512-IDjjqTkS+f0xm/ntbD21IPYiF0srzpePC/hhUMmctEsoklZwJwStJiMi/KN0pnH0LjSsgjwbP+QwW7y+Qf4/SQ== dependencies: webgl-constants "^1.1.1" @@ -5093,7 +5092,6 @@ electron-to-chromium@^1.4.411: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz#399df13091b836d28283a545c25c8e4d9da86da8" integrity sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg== - elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -8294,7 +8292,6 @@ memfs@^3.1.2, memfs@^3.4.3: version "3.5.3" resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.5.3.tgz#d9b40fe4f8d5788c5f895bda804cd0d9eeee9f3b" integrity sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw== - dependencies: fs-monkey "^1.0.4" @@ -12534,7 +12531,6 @@ webpack@^5.64.4: version "5.86.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.86.0.tgz#b0eb81794b62aee0b7e7eb8c5073495217d9fc6d" integrity sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg== - dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0"