mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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...
This commit is contained in:
parent
28665a869b
commit
11bc5e1a06
@ -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…
Reference in New Issue
Block a user