Navigation: Prevent chevron overlaying navbar menus (#47988)

* Attach nav item menus to a portal that's a sibling of the chevron to prevent incorrect stacking

* Make sure hover menus size correctly

* refactor into separate component

* Fix focus behaviour

* fix test again...
pull/48045/head
Ashley Harrison 3 years ago committed by GitHub
parent 28665a869b
commit 11bc5e1a06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      public/app/core/components/NavBar/Next/NavBarItemMenu.tsx
  2. 5
      public/app/core/components/NavBar/Next/NavBarItemMenuTrigger.tsx
  3. 1
      public/app/core/components/NavBar/Next/NavBarMenuItem.tsx
  4. 26
      public/app/core/components/NavBar/Next/NavBarMenuPortalContainer.tsx
  5. 3
      public/app/core/components/NavBar/Next/NavBarNext.tsx
  6. 2
      public/app/core/components/NavBar/Next/NavBarToggle.tsx
  7. 7
      public/app/features/variables/inspect/VariablesUnknownTable.test.tsx

@ -78,14 +78,11 @@ function getStyles(theme: GrafanaTheme2, reverseDirection?: boolean) {
menu: css`
background-color: ${theme.colors.background.primary};
border: 1px solid ${theme.components.panel.borderColor};
bottom: ${reverseDirection ? 0 : 'auto'};
box-shadow: ${theme.shadows.z3};
display: flex;
flex-direction: column;
left: 100%;
list-style: none;
min-width: 140px;
top: ${reverseDirection ? 'auto' : 0};
transition: ${theme.transitions.create('opacity')};
z-index: ${theme.zIndex.sidemenu};
`,

@ -1,6 +1,6 @@
import React, { ReactElement, useEffect, useState } from 'react';
import { css, cx } from '@emotion/css';
import { getPortalContainer, Icon, IconName, Link, useTheme2 } from '@grafana/ui';
import { Icon, IconName, Link, useTheme2 } from '@grafana/ui';
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
import { MenuTriggerProps } from '@react-types/menu';
import { useMenuTriggerState } from '@react-stately/menu';
@ -14,6 +14,7 @@ import { FocusScope } from '@react-aria/focus';
import { NavBarItemMenuContext, useNavBarContext } from '../context';
import { NavFeatureHighlight } from '../NavFeatureHighlight';
import { reportExperimentView } from '@grafana/runtime';
import { getNavMenuPortalContainer } from './NavBarMenuPortalContainer';
export interface NavBarItemMenuTriggerProps extends MenuTriggerProps {
children: ReactElement;
@ -185,7 +186,7 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
<div className={cx(styles.element, 'dropdown')} {...focusWithinProps}>
{element}
{state.isOpen && (
<OverlayContainer portalContainer={getPortalContainer()}>
<OverlayContainer portalContainer={getNavMenuPortalContainer()}>
<NavBarItemMenuContext.Provider
value={{
menuProps,

@ -99,6 +99,7 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive'], hasIcon: b
overflowWrap: 'anywhere',
padding: !hasIcon ? `${theme.spacing(0.5, 2)}` : '5px 12px 5px 10px',
textAlign: 'left',
width: '100%',
'&:hover, &:focus-visible': {
backgroundColor: theme.colors.action.hover,
color: theme.colors.text.primary,

@ -0,0 +1,26 @@
import React from 'react';
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { useTheme2 } from '@grafana/ui';
const NAV_MENU_PORTAL_CONTAINER_ID = 'navbar-menu-portal-container';
export const getNavMenuPortalContainer = () => document.getElementById(NAV_MENU_PORTAL_CONTAINER_ID) ?? document.body;
export const NavBarMenuPortalContainer = () => {
const theme = useTheme2();
const styles = getStyles(theme);
return <div className={styles.menuPortalContainer} id={NAV_MENU_PORTAL_CONTAINER_ID} />;
};
NavBarMenuPortalContainer.displayName = 'NavBarMenuPortalContainer';
const getStyles = (theme: GrafanaTheme2) => ({
menuPortalContainer: css({
left: 0,
position: 'fixed',
right: 0,
top: 0,
zIndex: theme.zIndex.sidemenu,
}),
});

@ -16,6 +16,7 @@ import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
import { FocusScope } from '@react-aria/focus';
import { NavBarContext } from '../context';
import { NavBarToggle } from './NavBarToggle';
import { NavBarMenuPortalContainer } from './NavBarMenuPortalContainer';
const onOpenSearch = () => {
locationService.partial({ search: 'open' });
@ -85,6 +86,8 @@ export const NavBarNext = React.memo(() => {
onClick={() => setMenuOpen(!menuOpen)}
/>
<NavBarMenuPortalContainer />
<ul className={styles.itemList}>
<NavBarItemWithoutMenu
isActive={isMatchOrChildMatch(homeItem, activeItem)}

@ -33,7 +33,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
border: `1px solid ${theme.colors.border.weak}`,
borderRadius: '50%',
marginRight: 0,
zIndex: theme.zIndex.sidemenu,
zIndex: theme.zIndex.sidemenu + 1,
[theme.breakpoints.down('md')]: {
display: 'none',

@ -115,10 +115,9 @@ describe('VariablesUnknownTable', () => {
await userEvent.click(screen.getByRole('heading', { name: /renamed or missing variables/i }));
// make sure we report the interaction for slow expansion
await waitFor(() => expect(reportInteractionSpy).toHaveBeenCalledTimes(2));
expect(reportInteractionSpy.mock.calls[0][0]).toEqual('Unknown variables section expanded');
expect(reportInteractionSpy.mock.calls[1][0]).toEqual('Slow unknown variables expansion');
expect(reportInteractionSpy.mock.calls[1][1]).toEqual({ elapsed: 1000 });
await waitFor(() =>
expect(reportInteractionSpy).toHaveBeenCalledWith('Slow unknown variables expansion', { elapsed: 1000 })
);
});
});
});

Loading…
Cancel
Save