Skip to content

Commit

Permalink
Create separate component for navigation items.
Browse files Browse the repository at this point in the history
  • Loading branch information
linuspahl committed Dec 18, 2024
1 parent 786559b commit 21e4294
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 102 deletions.
104 changes: 3 additions & 101 deletions graylog2-web-interface/src/components/navigation/MainNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>
},
topLevel?: boolean
}

const PluginRoute = ({
navigationItem: {
description,
path,
BadgeComponent,
},
topLevel = false,
}: PluginRouteProps) => (
<NavigationLink key={description}
description={BadgeComponent ? <BadgeComponent text={description} /> : 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 (
<NavDropdown key={title}
title={title}
badge={renderBadge ? BadgeComponent : null}
inactiveTitle={description}>
{children.map((childNavigationItem) => <PluginRoute navigationItem={childNavigationItem} key={childNavigationItem.description} />)}
</NavDropdown>
);
};

const _existingDropdownItemIndex = (existingNavigationItems: Array<PluginNavigation>, newNavigationItem: PluginNavigation) => {
if (!newNavigationItem.children) {
Expand Down Expand Up @@ -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 (
<PluginNavDropdown navigationItem={navigationItem}
key={navigationItem.description} />
);
}

return (
<PluginRoute navigationItem={navigationItem}
key={navigationItem.description}
topLevel />
);
};

const MainNavbar = () => {
const navigationItems = useNavigationItems();

return (
<Nav className="navbar-main">
{navigationItems.map((navigationItem) => (
<PluginNavItem navigationItem={navigationItem} />
<NavigationItem navigationItem={navigationItem} />
))}
</Nav>
);
Expand Down
123 changes: 123 additions & 0 deletions graylog2-web-interface/src/components/navigation/NavigationItem.tsx
Original file line number Diff line number Diff line change
@@ -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
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/

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) => (
<NavigationLink key={description}
description={BadgeComponent ? <BadgeComponent text={description} /> : 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 (
<NavDropdown key={title}
title={title}
badge={renderBadge ? BadgeComponent : null}
inactiveTitle={description}>
{children.map((childNavigationItem) => (
<PluginNavLink description={childNavigationItem.description}
path={childNavigationItem.path}
key={childNavigationItem.description} />
))}
</NavDropdown>
);
};

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 (
<PluginNavDropdown navigationItem={navigationItem}
key={navigationItem.description} />
);
}

return (
<PluginNavLink path={navigationItem.description}
description={navigationItem.description}
key={navigationItem.description}
topLevel />
);
};

export default NavigationItem;
1 change: 0 additions & 1 deletion graylog2-web-interface/src/views/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down

0 comments on commit 21e4294

Please sign in to comment.