mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 01:23:32 -06:00
MegaMenu: Fix broken hamburger toggle (#52770)
* MegaMenu: Fix broken hamburger toggle * oops * MegaMenu: move NavBarToggle to FocusScope
This commit is contained in:
parent
7cf2b68e0a
commit
fc9577b76d
@ -20,7 +20,7 @@ export function AppChrome({ children }: Props) {
|
||||
const state = chrome.useState();
|
||||
|
||||
if (state.chromeless || !config.featureToggles.topnav) {
|
||||
return <main className="main-view">{children} </main>;
|
||||
return <main className="main-view">{children}</main>;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -37,7 +37,7 @@ export function AppChrome({ children }: Props) {
|
||||
/>
|
||||
</div>
|
||||
<div className={cx(styles.content, state.searchBarHidden && styles.contentNoSearchBar)}>{children}</div>
|
||||
{state.megaMenuOpen && <MegaMenu searchBarHidden={state.searchBarHidden} onClose={chrome.toggleMegaMenu} />}
|
||||
<MegaMenu searchBarHidden={state.searchBarHidden} onClose={() => chrome.setMegaMenu(false)} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
@ -62,6 +62,10 @@ export class AppChromeService {
|
||||
this.update({ megaMenuOpen: !this.state.getValue().megaMenuOpen });
|
||||
};
|
||||
|
||||
setMegaMenu = (megaMenuOpen: boolean) => {
|
||||
this.update({ megaMenuOpen });
|
||||
};
|
||||
|
||||
toggleSearchBar = () => {
|
||||
const searchBarHidden = !this.state.getValue().searchBarHidden;
|
||||
store.set(this.searchBarStorageKey, searchBarHidden);
|
||||
|
@ -2,9 +2,11 @@ import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||
|
||||
import { NavModelItem, NavSection } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { GrafanaContext } from 'app/core/context/GrafanaContext';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import TestProvider from '../../../../test/helpers/TestProvider';
|
||||
@ -31,15 +33,20 @@ const setup = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const context = getGrafanaContextMock();
|
||||
const store = configureStore({ navBarTree });
|
||||
|
||||
context.chrome.toggleMegaMenu();
|
||||
|
||||
return render(
|
||||
<Provider store={store}>
|
||||
<TestProvider>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<MegaMenu onClose={() => {}} />
|
||||
</Router>
|
||||
</TestProvider>
|
||||
<GrafanaContext.Provider value={context}>
|
||||
<TestProvider>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<MegaMenu onClose={() => {}} />
|
||||
</Router>
|
||||
</TestProvider>
|
||||
</GrafanaContext.Provider>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
@ -2,12 +2,13 @@ import { css } from '@emotion/css';
|
||||
import { useDialog } from '@react-aria/dialog';
|
||||
import { FocusScope } from '@react-aria/focus';
|
||||
import { OverlayContainer, useOverlay } from '@react-aria/overlays';
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import CSSTransition from 'react-transition-group/CSSTransition';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { CustomScrollbar, Icon, IconButton, useTheme2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
|
||||
import { TOP_BAR_LEVEL_HEIGHT } from '../AppChrome/types';
|
||||
import { NavItem } from '../NavBar/NavBarMenu';
|
||||
@ -27,29 +28,47 @@ export function NavBarMenu({ activeItem, navItems, searchBarHidden, onClose }: P
|
||||
const styles = getStyles(theme, searchBarHidden);
|
||||
const animationSpeed = theme.transitions.duration.shortest;
|
||||
const animStyles = getAnimStyles(theme, animationSpeed);
|
||||
const { chrome } = useGrafana();
|
||||
const state = chrome.useState();
|
||||
const ref = useRef(null);
|
||||
const backdropRef = useRef(null);
|
||||
const { dialogProps } = useDialog({}, ref);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const onMenuClose = () => setIsOpen(false);
|
||||
|
||||
const { overlayProps, underlayProps } = useOverlay(
|
||||
{
|
||||
isDismissable: true,
|
||||
isOpen: true,
|
||||
onClose,
|
||||
onClose: onMenuClose,
|
||||
},
|
||||
ref
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.megaMenuOpen) {
|
||||
setIsOpen(true);
|
||||
}
|
||||
}, [state.megaMenuOpen]);
|
||||
|
||||
return (
|
||||
<OverlayContainer>
|
||||
<FocusScope contain autoFocus>
|
||||
<CSSTransition appear={true} in={true} classNames={animStyles.overlay} timeout={animationSpeed}>
|
||||
<div data-testid="navbarmenu" ref={ref} {...overlayProps} {...dialogProps} className={styles.container}>
|
||||
<CSSTransition
|
||||
in={isOpen}
|
||||
unmountOnExit={true}
|
||||
classNames={animStyles.overlay}
|
||||
timeout={animationSpeed}
|
||||
onExited={onClose}
|
||||
>
|
||||
<div data-testid="navbarmenu" ref={ref} {...overlayProps} {...dialogProps} className={styles.container}>
|
||||
<FocusScope contain autoFocus>
|
||||
<div className={styles.mobileHeader}>
|
||||
<Icon name="bars" size="xl" />
|
||||
<IconButton
|
||||
aria-label="Close navigation menu"
|
||||
name="times"
|
||||
onClick={onClose}
|
||||
onClick={onMenuClose}
|
||||
size="xl"
|
||||
variant="secondary"
|
||||
/>
|
||||
@ -59,24 +78,24 @@ export function NavBarMenu({ activeItem, navItems, searchBarHidden, onClose }: P
|
||||
isExpanded={true}
|
||||
onClick={() => {
|
||||
reportInteraction('grafana_navigation_collapsed');
|
||||
onClose();
|
||||
onMenuClose();
|
||||
}}
|
||||
/>
|
||||
<nav className={styles.content}>
|
||||
<CustomScrollbar hideHorizontalTrack>
|
||||
<ul className={styles.itemList}>
|
||||
{navItems.map((link) => (
|
||||
<NavItem link={link} onClose={onClose} activeItem={activeItem} key={link.text} />
|
||||
<NavItem link={link} onClose={onMenuClose} activeItem={activeItem} key={link.text} />
|
||||
))}
|
||||
</ul>
|
||||
</CustomScrollbar>
|
||||
</nav>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
<CSSTransition appear={true} in={true} classNames={animStyles.backdrop} timeout={animationSpeed}>
|
||||
<div className={styles.backdrop} {...underlayProps} />
|
||||
</CSSTransition>
|
||||
</FocusScope>
|
||||
</FocusScope>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
<CSSTransition in={isOpen} unmountOnExit={true} classNames={animStyles.backdrop} timeout={animationSpeed}>
|
||||
<div ref={backdropRef} className={styles.backdrop} {...underlayProps} />
|
||||
</CSSTransition>
|
||||
</OverlayContainer>
|
||||
);
|
||||
}
|
||||
@ -109,6 +128,7 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden?: boolean) => {
|
||||
zIndex: theme.zIndex.navbarFixed - 1,
|
||||
position: 'fixed',
|
||||
top: topPosition,
|
||||
backgroundColor: theme.colors.background.primary,
|
||||
boxSizing: 'content-box',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
borderRight: `1px solid ${theme.colors.border.weak}`,
|
||||
@ -154,7 +174,7 @@ const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
||||
|
||||
const overlayTransition = {
|
||||
...commonTransition,
|
||||
transitionProperty: 'background-color, box-shadow, width',
|
||||
transitionProperty: 'box-shadow, width',
|
||||
// this is needed to prevent a horizontal scrollbar during the animation on firefox
|
||||
'.scrollbar-view': {
|
||||
overflow: 'hidden !important',
|
||||
@ -167,7 +187,6 @@ const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
||||
};
|
||||
|
||||
const overlayOpen = {
|
||||
backgroundColor: theme.colors.background.primary,
|
||||
boxShadow: theme.shadows.z3,
|
||||
width: '100%',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
@ -178,10 +197,6 @@ const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
||||
const overlayClosed = {
|
||||
boxShadow: 'none',
|
||||
width: 0,
|
||||
[theme.breakpoints.up('md')]: {
|
||||
backgroundColor: theme.colors.background.primary,
|
||||
width: theme.spacing(7),
|
||||
},
|
||||
};
|
||||
|
||||
const backdropOpen = {
|
||||
@ -194,16 +209,16 @@ const getAnimStyles = (theme: GrafanaTheme2, animationDuration: number) => {
|
||||
|
||||
return {
|
||||
backdrop: {
|
||||
appear: css(backdropClosed),
|
||||
appearActive: css(backdropTransition, backdropOpen),
|
||||
appearDone: css(backdropOpen),
|
||||
enter: css(backdropClosed),
|
||||
enterActive: css(backdropTransition, backdropOpen),
|
||||
enterDone: css(backdropOpen),
|
||||
exit: css(backdropOpen),
|
||||
exitActive: css(backdropTransition, backdropClosed),
|
||||
},
|
||||
overlay: {
|
||||
appear: css(overlayClosed),
|
||||
appearActive: css(overlayTransition, overlayOpen),
|
||||
appearDone: css(overlayOpen),
|
||||
enter: css(overlayClosed),
|
||||
enterActive: css(overlayTransition, overlayOpen),
|
||||
enterDone: css(overlayOpen),
|
||||
exit: css(overlayOpen),
|
||||
exitActive: css(overlayTransition, overlayClosed),
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user