From 21e4294d63f5f2c1f8ca4a76d663f0de3bd38b09 Mon Sep 17 00:00:00 2001 From: Linus Pahl Date: Wed, 18 Dec 2024 16:03:07 +0100 Subject: [PATCH] Create separate component for navigation items. --- .../src/components/navigation/MainNavbar.tsx | 104 +-------------- .../components/navigation/NavigationItem.tsx | 123 ++++++++++++++++++ .../src/views/components/Search.tsx | 1 - 3 files changed, 126 insertions(+), 102 deletions(-) create mode 100644 graylog2-web-interface/src/components/navigation/NavigationItem.tsx diff --git a/graylog2-web-interface/src/components/navigation/MainNavbar.tsx b/graylog2-web-interface/src/components/navigation/MainNavbar.tsx index 4b35ea3a9c10..669543a78144 100644 --- a/graylog2-web-interface/src/components/navigation/MainNavbar.tsx +++ b/graylog2-web-interface/src/components/navigation/MainNavbar.tsx @@ -19,83 +19,17 @@ import * as React from 'react'; import type { PluginNavigation } from 'graylog-web-plugin'; import { useMemo } from 'react'; -import { Nav, NavDropdown } from 'components/bootstrap'; +import { Nav } from 'components/bootstrap'; import { isPermitted } from 'util/PermissionsMixin'; import Routes, { ENTERPRISE_ROUTE_DESCRIPTION, SECURITY_ROUTE_DESCRIPTION } from 'routing/Routes'; import filterByPerspective from 'components/perspectives/util/filterByPerspective'; import useCurrentUser from 'hooks/useCurrentUser'; import useActivePerspective from 'components/perspectives/hooks/useActivePerspective'; import usePluginEntities from 'hooks/usePluginEntities'; -import AppConfig from 'util/AppConfig'; -import isActiveRoute from 'components/navigation/util/isActiveRoute'; import { navigation as securityNavigation } from 'components/security/bindings'; -import useLocation from 'routing/useLocation'; - -import NavigationLink from './NavigationLink'; +import NavigationItem from 'components/navigation/NavigationItem'; const LAST_POSITION = 'last'; -const requiredFeatureFlagIsEnabled = (requiredFeatureFlag: undefined | string) => (requiredFeatureFlag ? AppConfig.isFeatureEnabled(requiredFeatureFlag) : true); - -type PluginRouteProps = { - navigationItem: { - path: string; - description: string; - requiredFeatureFlag?: string; - BadgeComponent?: React.ComponentType<{ text: string }> - permissions?: string | Array - }, - topLevel?: boolean -} - -const PluginRoute = ({ - navigationItem: { - description, - path, - BadgeComponent, - }, - topLevel = false, -}: PluginRouteProps) => ( - : description} - path={path} - topLevel={topLevel} /> -); - -type PluginNavDropdownProps = { - navigationItem: PluginNavigation, -} - -const PluginNavDropdown = ({ - navigationItem: { - children, - description, - BadgeComponent, - }, -}: PluginNavDropdownProps) => { - const { pathname } = useLocation(); - const currentUser = useCurrentUser(); - - const activeChild = children.filter(({ path, end }) => (path && isActiveRoute(pathname, path, end))); - const title = activeChild.length > 0 ? `${description} / ${activeChild[0].description}` : description; - const isEmpty = !children.some((child) => ( - isPermitted(currentUser.permissions, child.permissions) && requiredFeatureFlagIsEnabled(child.requiredFeatureFlag)), - ); - - if (isEmpty) { - return null; - } - - const renderBadge = children.some((child) => isPermitted(currentUser.permissions, child.permissions) && child?.BadgeComponent); - - return ( - - {children.map((childNavigationItem) => )} - - ); -}; const _existingDropdownItemIndex = (existingNavigationItems: Array, newNavigationItem: PluginNavigation) => { if (!newNavigationItem.children) { @@ -190,45 +124,13 @@ const useNavigationItems = () => { }, [activePerspective, allNavigationItems, permissions]); }; -const PluginNavItem = ({ navigationItem }: { navigationItem: PluginNavigation }) => { - const currentUser = useCurrentUser(); - - if (!requiredFeatureFlagIsEnabled(navigationItem.requiredFeatureFlag)) { - return null; - } - - if (navigationItem.permissions && !isPermitted(currentUser.permissions, navigationItem.permissions)) return null; - - if (navigationItem.useCondition) { - // eslint-disable-next-line react-hooks/rules-of-hooks - const shouldBeVisible = navigationItem.useCondition(); - - if (!shouldBeVisible) { - return false; - } - } - - if (navigationItem.children) { - return ( - - ); - } - - return ( - - ); -}; - const MainNavbar = () => { const navigationItems = useNavigationItems(); return ( ); diff --git a/graylog2-web-interface/src/components/navigation/NavigationItem.tsx b/graylog2-web-interface/src/components/navigation/NavigationItem.tsx new file mode 100644 index 000000000000..5c645b781a3a --- /dev/null +++ b/graylog2-web-interface/src/components/navigation/NavigationItem.tsx @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * 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 + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ + +import * as React from 'react'; +import type { PluginNavigation } from 'graylog-web-plugin'; + +import useCurrentUser from 'hooks/useCurrentUser'; +import { isPermitted } from 'util/PermissionsMixin'; +import NavigationLink from 'components/navigation/NavigationLink'; +import useLocation from 'routing/useLocation'; +import isActiveRoute from 'components/navigation/util/isActiveRoute'; +import { NavDropdown } from 'components/bootstrap'; +import AppConfig from 'util/AppConfig'; + +const requiredFeatureFlagIsEnabled = (requiredFeatureFlag: undefined | string) => (requiredFeatureFlag ? AppConfig.isFeatureEnabled(requiredFeatureFlag) : true); + +type PluginNavLinkProps = { + path: string; + description: string; + BadgeComponent?: React.ComponentType<{ text: string }> + topLevel?: boolean +} + +const PluginNavLink = ({ + description, + path, + BadgeComponent = undefined, + topLevel = false, +}: PluginNavLinkProps) => ( + : description} + path={path} + topLevel={topLevel} /> +); + +type PluginNavDropdownProps = { + navigationItem: PluginNavigation, +} + +const PluginNavDropdown = ({ + navigationItem: { + children, + description, + BadgeComponent, + }, +}: PluginNavDropdownProps) => { + const { pathname } = useLocation(); + const currentUser = useCurrentUser(); + + const activeChild = children.filter(({ path, end }) => (path && isActiveRoute(pathname, path, end))); + const title = activeChild.length > 0 ? `${description} / ${activeChild[0].description}` : description; + const isEmpty = !children.some((child) => ( + isPermitted(currentUser.permissions, child.permissions) && requiredFeatureFlagIsEnabled(child.requiredFeatureFlag)), + ); + + if (isEmpty) { + return null; + } + + const renderBadge = children.some((child) => isPermitted(currentUser.permissions, child.permissions) && child?.BadgeComponent); + + return ( + + {children.map((childNavigationItem) => ( + + ))} + + ); +}; + +const NavigationItem = ({ navigationItem }: { navigationItem: PluginNavigation }) => { + const currentUser = useCurrentUser(); + + if (!requiredFeatureFlagIsEnabled(navigationItem.requiredFeatureFlag)) { + return null; + } + + if (navigationItem.permissions && !isPermitted(currentUser.permissions, navigationItem.permissions)) return null; + + if (navigationItem.useCondition) { + // eslint-disable-next-line react-hooks/rules-of-hooks + const shouldBeVisible = navigationItem.useCondition(); + + if (!shouldBeVisible) { + return false; + } + } + + if (navigationItem.children) { + return ( + + ); + } + + return ( + + ); +}; + +export default NavigationItem; diff --git a/graylog2-web-interface/src/views/components/Search.tsx b/graylog2-web-interface/src/views/components/Search.tsx index fae20b6946f5..1a06ff00d2bb 100644 --- a/graylog2-web-interface/src/views/components/Search.tsx +++ b/graylog2-web-interface/src/views/components/Search.tsx @@ -51,7 +51,6 @@ import { selectCurrentQueryResults } from 'views/logic/slices/viewSelectors'; import useAppSelector from 'stores/useAppSelector'; import useParameters from 'views/hooks/useParameters'; import useSearchConfiguration from 'hooks/useSearchConfiguration'; -import { Button } from 'components/bootstrap'; import ExternalValueActionsProvider from './ExternalValueActionsProvider';