From ca53f5c8dadddfae4055403d9f8993c0167870c4 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Mon, 13 Sep 2021 16:49:27 +0100 Subject: [PATCH] Navigation: Implement active state for items in the Sidemenu (#39030) * Navigation: Implement active state for items in the Sidemenu * Navigation: Improve logic for when link is active and extract isSearchActive into a util function * Navigation: Implement custom rule for dashboards under /d/ and fix minor bugs * Navigation: only show first matching active state + strip query params from link urls --- .../sidemenu/BottomSection.test.tsx | 2 + .../components/sidemenu/BottomSection.tsx | 4 +- .../app/core/components/sidemenu/SideMenu.tsx | 9 ++ .../core/components/sidemenu/SideMenuItem.tsx | 48 ++++--- .../components/sidemenu/TopSection.test.tsx | 13 +- .../core/components/sidemenu/TopSection.tsx | 7 +- .../core/components/sidemenu/utils.test.ts | 121 +++++++++++++++++- public/app/core/components/sidemenu/utils.ts | 36 ++++++ 8 files changed, 218 insertions(+), 22 deletions(-) diff --git a/public/app/core/components/sidemenu/BottomSection.test.tsx b/public/app/core/components/sidemenu/BottomSection.test.tsx index 8cd6273c5b2..7cb4ac70e8c 100644 --- a/public/app/core/components/sidemenu/BottomSection.test.tsx +++ b/public/app/core/components/sidemenu/BottomSection.test.tsx @@ -9,6 +9,8 @@ import BottomSection from './BottomSection'; jest.mock('./utils', () => ({ getForcedLoginUrl: () => '/mockForcedLoginUrl', + isLinkActive: () => false, + isSearchActive: () => false, })); jest.mock('../../app_events', () => ({ publish: jest.fn(), diff --git a/public/app/core/components/sidemenu/BottomSection.tsx b/public/app/core/components/sidemenu/BottomSection.tsx index 4379c31652f..f8fa6bbd879 100644 --- a/public/app/core/components/sidemenu/BottomSection.tsx +++ b/public/app/core/components/sidemenu/BottomSection.tsx @@ -12,7 +12,7 @@ import { OrgSwitcher } from '../OrgSwitcher'; import { getFooterLinks } from '../Footer/Footer'; import { HelpModal } from '../help/HelpModal'; import SideMenuItem from './SideMenuItem'; -import { getForcedLoginUrl } from './utils'; +import { getForcedLoginUrl, isLinkActive, isSearchActive } from './utils'; export default function BottomSection() { const theme = useTheme2(); @@ -21,6 +21,7 @@ export default function BottomSection() { const bottomNav = navTree.filter((item) => item.hideFromMenu); const isSignedIn = contextSrv.isSignedIn; const location = useLocation(); + const activeItemId = bottomNav.find((item) => isLinkActive(location.pathname, item))?.id; const forcedLoginUrl = getForcedLoginUrl(location.pathname + location.search); const user = contextSrv.user; const [showSwitcherModal, setShowSwitcherModal] = useState(false); @@ -76,6 +77,7 @@ export default function BottomSection() { return ( ({ display: none; min-height: ${theme.components.sidemenu.width}px; + &:focus-visible, &:hover { background-color: ${theme.colors.action.hover}; } + &:focus-visible { + box-shadow: none; + color: ${theme.colors.text.primary}; + outline: 2px solid ${theme.colors.primary.main}; + outline-offset: -2px; + transition: none; + } + img { width: ${theme.spacing(3.5)}; } diff --git a/public/app/core/components/sidemenu/SideMenuItem.tsx b/public/app/core/components/sidemenu/SideMenuItem.tsx index 19eb22651d8..c4d9fd28a6a 100644 --- a/public/app/core/components/sidemenu/SideMenuItem.tsx +++ b/public/app/core/components/sidemenu/SideMenuItem.tsx @@ -5,6 +5,7 @@ import { Link, styleMixins, useTheme2 } from '@grafana/ui'; import SideMenuDropDown from './SideMenuDropDown'; export interface Props { + isActive?: boolean; children: ReactNode; label: string; menuItems?: NavModelItem[]; @@ -16,6 +17,7 @@ export interface Props { } const SideMenuItem = ({ + isActive = false, children, label, menuItems = [], @@ -26,7 +28,7 @@ const SideMenuItem = ({ url, }: Props) => { const theme = useTheme2(); - const styles = getStyles(theme); + const styles = getStyles(theme, isActive); let element = (