diff --git a/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx b/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx index 5c5eb5b8ce0..044d42ce440 100644 --- a/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx +++ b/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx @@ -1,18 +1,25 @@ import { css } from '@emotion/css'; import { i18n } from '@lingui/core'; +import { cloneDeep } from 'lodash'; import React from 'react'; +import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, NavModelItem } from '@grafana/data'; import { Menu, MenuItem, useStyles2 } from '@grafana/ui'; import menuItemTranslations from '../../NavBar/navBarItem-translations'; +import { enrichConfigItems, enrichWithInteractionTracking } from '../../NavBar/utils'; export interface TopNavBarMenuProps { node: NavModelItem; } -export function TopNavBarMenu({ node }: TopNavBarMenuProps) { +export function TopNavBarMenu({ node: nodePlain }: TopNavBarMenuProps) { const styles = useStyles2(getStyles); + const location = useLocation(); + const enriched = enrichConfigItems([cloneDeep(nodePlain)], location); + const node = enrichWithInteractionTracking(enriched[0], false); + if (!node) { return null; } diff --git a/public/app/core/components/AppChrome/TopSearchBar.tsx b/public/app/core/components/AppChrome/TopSearchBar.tsx index 87f7cff44af..94a505f1a78 100644 --- a/public/app/core/components/AppChrome/TopSearchBar.tsx +++ b/public/app/core/components/AppChrome/TopSearchBar.tsx @@ -1,51 +1,22 @@ import { css } from '@emotion/css'; -import { cloneDeep } from 'lodash'; -import React, { useState } from 'react'; -import { useLocation } from 'react-router-dom'; +import React from 'react'; -import { GrafanaTheme2, NavSection } from '@grafana/data'; -import { locationService } from '@grafana/runtime'; -import { Dropdown, FilterInput, Icon, Tooltip, useStyles2 } from '@grafana/ui'; +import { GrafanaTheme2 } from '@grafana/data'; +import { Dropdown, Icon, Tooltip, useStyles2 } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; -import { useSearchQuery } from 'app/features/search/hooks/useSearchQuery'; import { useSelector } from 'app/types'; -import { enrichConfigItems, enrichWithInteractionTracking } from '../NavBar/utils'; -import { OrgSwitcher } from '../OrgSwitcher'; - import { TopNavBarMenu } from './TopBar/TopNavBarMenu'; +import { TopSearchBarInput } from './TopSearchBarInput'; import { TOP_BAR_LEVEL_HEIGHT } from './types'; export function TopSearchBar() { const styles = useStyles2(getStyles); - const location = useLocation(); - const { query, onQueryChange } = useSearchQuery({}); - const navBarTree = useSelector((state) => state.navBarTree); - const navTree = cloneDeep(navBarTree); - const [showSwitcherModal, setShowSwitcherModal] = useState(false); - const toggleSwitcherModal = () => { - setShowSwitcherModal(!showSwitcherModal); - }; + const navIndex = useSelector((state) => state.navIndex); - const onOpenSearch = () => { - locationService.partial({ search: 'open' }); - }; - const onSearchChange = (value: string) => { - onQueryChange(value); - if (value) { - onOpenSearch(); - } - }; - - const configItems = enrichConfigItems( - navTree.filter((item) => item.section === NavSection.Config), - location, - toggleSwitcherModal - ).map((item) => enrichWithInteractionTracking(item, false)); - - const helpNode = configItems.find((item) => item.id === 'help'); - const profileNode = configItems.find((item) => item.id === 'profile'); - const signInNode = configItems.find((item) => item.id === 'signin'); + const helpNode = navIndex['help']; + const profileNode = navIndex['profile']; + const signInNode = navIndex['signin']; return (
@@ -55,17 +26,11 @@ export function TopSearchBar() {
- +
{helpNode && ( - }> + }> @@ -91,7 +56,6 @@ export function TopSearchBar() { )}
- {showSwitcherModal && } ); } diff --git a/public/app/core/components/AppChrome/TopSearchBarInput.tsx b/public/app/core/components/AppChrome/TopSearchBarInput.tsx new file mode 100644 index 00000000000..b4c1f4d0f38 --- /dev/null +++ b/public/app/core/components/AppChrome/TopSearchBarInput.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +import { locationService } from '@grafana/runtime'; +import { FilterInput } from '@grafana/ui'; +import { useSearchQuery } from 'app/features/search/hooks/useSearchQuery'; + +export function TopSearchBarInput() { + const { query, onQueryChange } = useSearchQuery({}); + + const onOpenSearch = () => { + locationService.partial({ search: 'open' }); + }; + + const onSearchChange = (value: string) => { + onQueryChange(value); + if (value) { + onOpenSearch(); + } + }; + return ( + + ); +} diff --git a/public/app/core/components/MegaMenu/MegaMenu.tsx b/public/app/core/components/MegaMenu/MegaMenu.tsx index c5be1d645e7..e1d6fea2309 100644 --- a/public/app/core/components/MegaMenu/MegaMenu.tsx +++ b/public/app/core/components/MegaMenu/MegaMenu.tsx @@ -1,6 +1,6 @@ import { css } from '@emotion/css'; import { cloneDeep } from 'lodash'; -import React, { useState } from 'react'; +import React from 'react'; import { useLocation } from 'react-router-dom'; import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data'; @@ -22,11 +22,6 @@ export const MegaMenu = React.memo(({ onClose, searchBarHidden }) => { const theme = useTheme2(); const styles = getStyles(theme); const location = useLocation(); - const [showSwitcherModal, setShowSwitcherModal] = useState(false); - - const toggleSwitcherModal = () => { - setShowSwitcherModal(!showSwitcherModal); - }; const homeItem: NavModelItem = enrichWithInteractionTracking( { @@ -48,8 +43,7 @@ export const MegaMenu = React.memo(({ onClose, searchBarHidden }) => { .map((item) => enrichWithInteractionTracking(item, true)); const configItems = enrichConfigItems( navTree.filter((item) => item.section === NavSection.Config && item && item.id !== 'help' && item.id !== 'profile'), - location, - toggleSwitcherModal + location ).map((item) => enrichWithInteractionTracking(item, true)); const activeItem = getActiveItem(navTree, location.pathname); diff --git a/public/app/core/components/NavBar/NavBar.tsx b/public/app/core/components/NavBar/NavBar.tsx index 66c084f5cb0..e1765c71d61 100644 --- a/public/app/core/components/NavBar/NavBar.tsx +++ b/public/app/core/components/NavBar/NavBar.tsx @@ -11,8 +11,6 @@ import { Icon, useTheme2, CustomScrollbar } from '@grafana/ui'; import { getKioskMode } from 'app/core/navigation/kiosk'; import { useSelector } from 'app/types'; -import { OrgSwitcher } from '../OrgSwitcher'; - import NavBarItem from './NavBarItem'; import { NavBarItemIcon } from './NavBarItemIcon'; import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu'; @@ -38,15 +36,10 @@ export const NavBar = React.memo(() => { const theme = useTheme2(); const styles = getStyles(theme); const location = useLocation(); - const [showSwitcherModal, setShowSwitcherModal] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [menuAnimationInProgress, setMenuAnimationInProgress] = useState(false); const [menuIdOpen, setMenuIdOpen] = useState(undefined); - const toggleSwitcherModal = () => { - setShowSwitcherModal(!showSwitcherModal); - }; - // Here we need to hack in a "home" and "search" NavModelItem since this is constructed in the frontend const searchItem: NavModelItem = enrichWithInteractionTracking( { @@ -78,8 +71,7 @@ export const NavBar = React.memo(() => { .map((item) => enrichWithInteractionTracking(item, menuOpen)); const configItems = enrichConfigItems( navTree.filter((item) => item.section === NavSection.Config), - location, - toggleSwitcherModal + location ).map((item) => enrichWithInteractionTracking(item, menuOpen)); const activeItem = isSearchActive(location) ? searchItem : getActiveItem(navTree, location.pathname); @@ -158,7 +150,6 @@ export const NavBar = React.memo(() => { - {showSwitcherModal && } {(menuOpen || menuAnimationInProgress) && (
{ const contextSrv = new ContextSrv(); contextSrv.user.isSignedIn = false; setContextSrv(contextSrv); - const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation, jest.fn()); + const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation); const signInNode = enrichedConfigItems.find((item) => item.id === 'signin'); expect(signInNode).toBeDefined(); }); @@ -71,7 +71,7 @@ describe('enrichConfigItems', () => { const contextSrv = new ContextSrv(); contextSrv.user.isSignedIn = true; setContextSrv(contextSrv); - const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation, jest.fn()); + const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation); const signInNode = enrichedConfigItems.find((item) => item.id === 'signin'); expect(signInNode).toBeDefined(); }); @@ -80,7 +80,7 @@ describe('enrichConfigItems', () => { const contextSrv = new ContextSrv(); contextSrv.user.orgCount = 1; setContextSrv(contextSrv); - const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation, jest.fn()); + const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation); const profileNode = enrichedConfigItems.find((item) => item.id === 'profile'); expect(profileNode!.children).toBeUndefined(); }); @@ -89,7 +89,7 @@ describe('enrichConfigItems', () => { const contextSrv = new ContextSrv(); contextSrv.user.orgCount = 2; setContextSrv(contextSrv); - const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation, jest.fn()); + const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation); const profileNode = enrichedConfigItems.find((item) => item.id === 'profile'); expect(profileNode!.children).toContainEqual( expect.objectContaining({ @@ -101,7 +101,7 @@ describe('enrichConfigItems', () => { it('enhances the help node with extra child links', () => { const contextSrv = new ContextSrv(); setContextSrv(contextSrv); - const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation, jest.fn()); + const enrichedConfigItems = enrichConfigItems(mockItems, mockLocation); const helpNode = enrichedConfigItems.find((item) => item.id === 'help'); expect(helpNode!.children).toContainEqual( expect.objectContaining({ diff --git a/public/app/core/components/NavBar/utils.ts b/public/app/core/components/NavBar/utils.ts index e1ad661cce8..05935e3603c 100644 --- a/public/app/core/components/NavBar/utils.ts +++ b/public/app/core/components/NavBar/utils.ts @@ -8,6 +8,7 @@ import { contextSrv } from 'app/core/services/context_srv'; import { ShowModalReactEvent } from '../../../types/events'; import appEvents from '../../app_events'; import { getFooterLinks } from '../Footer/Footer'; +import { OrgSwitcher } from '../OrgSwitcher'; import { HelpModal } from '../help/HelpModal'; export const SEARCH_ITEM_ID = 'search'; @@ -22,21 +23,21 @@ export const getForcedLoginUrl = (url: string) => { return `${getConfig().appSubUrl}${url.split('?')[0]}?${queryParams.toString()}`; }; -export const enrichConfigItems = ( - items: NavModelItem[], - location: Location, - toggleOrgSwitcher: () => void -) => { +export const enrichConfigItems = (items: NavModelItem[], location: Location) => { const { isSignedIn, user } = contextSrv; const onOpenShortcuts = () => { appEvents.publish(new ShowModalReactEvent({ component: HelpModal })); }; + const onOpenOrgSwitcher = () => { + appEvents.publish(new ShowModalReactEvent({ component: OrgSwitcher })); + }; + if (user && user.orgCount > 1) { const profileNode = items.find((bottomNavItem) => bottomNavItem.id === 'profile'); if (profileNode) { profileNode.showOrgSwitcher = true; - profileNode.subTitle = `Current Org.: ${user?.orgName}`; + profileNode.subTitle = `Organization: ${user?.orgName}`; } } @@ -53,7 +54,7 @@ export const enrichConfigItems = ( }); } - items.forEach((link, index) => { + items.forEach((link) => { let menuItems = link.children || []; if (link.id === 'help') { @@ -75,7 +76,7 @@ export const enrichConfigItems = ( id: 'switch-organization', text: 'Switch organization', icon: 'arrow-random', - onClick: toggleOrgSwitcher, + onClick: onOpenOrgSwitcher, }, ]; }