mirror of
https://github.com/grafana/grafana.git
synced 2025-01-04 13:17:16 -06:00
DockedMegaMenu: Keep undock button (#78461)
* dock undock smoothly * handle keyboard focus * use ref instead of state * run i18n:extract * undo this change * make dock/undock first button to focus * only focus when going to docked, add comment * minor tweaks
This commit is contained in:
parent
b5d1c8874b
commit
4247696402
@ -93,7 +93,7 @@ export function AppChrome({ children }: Props) {
|
||||
</main>
|
||||
{!state.chromeless && (
|
||||
<>
|
||||
{config.featureToggles.dockedMegaMenu ? (
|
||||
{config.featureToggles.dockedMegaMenu && state.megaMenu !== 'docked' ? (
|
||||
<AppChromeMenu />
|
||||
) : (
|
||||
<MegaMenu searchBarHidden={searchBarHidden} onClose={() => chrome.setMegaMenu('closed')} />
|
||||
|
@ -2,7 +2,7 @@ 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 } from 'react';
|
||||
import CSSTransition from 'react-transition-group/CSSTransition';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
@ -20,11 +20,18 @@ export function AppChromeMenu({}: Props) {
|
||||
const theme = useTheme2();
|
||||
const { chrome } = useGrafana();
|
||||
const state = chrome.useState();
|
||||
const prevMegaMenuState = useRef(state.megaMenu);
|
||||
const searchBarHidden = state.searchBarHidden || state.kioskMode === KioskMode.TV;
|
||||
|
||||
useEffect(() => {
|
||||
prevMegaMenuState.current = state.megaMenu;
|
||||
}, [state.megaMenu]);
|
||||
|
||||
const ref = useRef(null);
|
||||
const backdropRef = useRef(null);
|
||||
const animationSpeed = theme.transitions.duration.shortest;
|
||||
// we don't want to show the opening animation when transitioning between docked + open
|
||||
const animationSpeed =
|
||||
prevMegaMenuState.current === 'docked' && state.megaMenu === 'open' ? 0 : theme.transitions.duration.shortest;
|
||||
const animationStyles = useStyles2(getAnimStyles, animationSpeed);
|
||||
|
||||
const isOpen = state.megaMenu === 'open';
|
||||
@ -57,7 +64,7 @@ export function AppChromeMenu({}: Props) {
|
||||
classNames={animationStyles.overlay}
|
||||
timeout={{ enter: animationSpeed, exit: 0 }}
|
||||
>
|
||||
<FocusScope contain autoFocus>
|
||||
<FocusScope contain autoFocus restoreFocus>
|
||||
<MegaMenu className={styles.menu} onClose={onClose} ref={ref} {...overlayProps} {...dialogProps} />
|
||||
</FocusScope>
|
||||
</CSSTransition>
|
||||
|
@ -35,7 +35,12 @@ export const MegaMenu = React.memo(
|
||||
const activeItem = getActiveItem(navItems, location.pathname);
|
||||
|
||||
const handleDockedMenu = () => {
|
||||
chrome.setMegaMenu(state.megaMenu === 'docked' ? 'closed' : 'docked');
|
||||
chrome.setMegaMenu(state.megaMenu === 'docked' ? 'open' : 'docked');
|
||||
|
||||
// refocus on dock/undock button when changing state
|
||||
setTimeout(() => {
|
||||
document.getElementById('dock-menu-button')?.focus();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -54,21 +59,26 @@ export const MegaMenu = React.memo(
|
||||
<CustomScrollbar showScrollIndicators hideHorizontalTrack>
|
||||
<ul className={styles.itemList}>
|
||||
{navItems.map((link, index) => (
|
||||
<Stack key={link.text} direction="row" alignItems="center">
|
||||
<MegaMenuItem
|
||||
link={link}
|
||||
onClick={state.megaMenu === 'open' ? onClose : undefined}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
{index === 0 && Boolean(state.megaMenu === 'open') && (
|
||||
<Stack key={link.text} direction={index === 0 ? 'row-reverse' : 'row'} alignItems="center">
|
||||
{index === 0 && (
|
||||
<IconButton
|
||||
id="dock-menu-button"
|
||||
className={styles.dockMenuButton}
|
||||
tooltip={t('navigation.megamenu.dock', 'Dock menu')}
|
||||
tooltip={
|
||||
state.megaMenu === 'docked'
|
||||
? t('navigation.megamenu.undock', 'Undock menu')
|
||||
: t('navigation.megamenu.dock', 'Dock menu')
|
||||
}
|
||||
name="web-section-alt"
|
||||
onClick={handleDockedMenu}
|
||||
variant="secondary"
|
||||
/>
|
||||
)}
|
||||
<MegaMenuItem
|
||||
link={link}
|
||||
onClick={state.megaMenu === 'open' ? onClose : undefined}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
</Stack>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { Components } from '@grafana/e2e-selectors';
|
||||
import { Icon, IconButton, ToolbarButton, useStyles2 } from '@grafana/ui';
|
||||
import { Icon, IconButton, ToolbarButton, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { HOME_NAV_ID } from 'app/core/reducers/navModel';
|
||||
import { useSelector } from 'app/types';
|
||||
@ -39,9 +40,22 @@ export function NavToolbar({
|
||||
const { chrome } = useGrafana();
|
||||
const state = chrome.useState();
|
||||
const homeNav = useSelector((state) => state.navIndex)[HOME_NAV_ID];
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
const breadcrumbs = buildBreadcrumbs(sectionNav, pageNav, homeNav);
|
||||
|
||||
const dockMenuBreakpoint = theme.breakpoints.values.xl;
|
||||
const [isTooSmallForDockedMenu, setIsTooSmallForDockedMenu] = useState(
|
||||
!window.matchMedia(`(min-width: ${dockMenuBreakpoint}px)`).matches
|
||||
);
|
||||
|
||||
useMediaQueryChange({
|
||||
breakpoint: dockMenuBreakpoint,
|
||||
onChange: (e) => {
|
||||
setIsTooSmallForDockedMenu(!e.matches);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div data-testid={Components.NavToolbar.container} className={styles.pageToolbar}>
|
||||
<div className={styles.menuButton}>
|
||||
@ -49,7 +63,7 @@ export function NavToolbar({
|
||||
id={TOGGLE_BUTTON_ID}
|
||||
name="bars"
|
||||
tooltip={
|
||||
state.megaMenu === 'closed'
|
||||
state.megaMenu === 'closed' || (state.megaMenu === 'docked' && isTooSmallForDockedMenu)
|
||||
? t('navigation.toolbar.open-menu', 'Open menu')
|
||||
: t('navigation.toolbar.close-menu', 'Close menu')
|
||||
}
|
||||
|
@ -935,7 +935,8 @@
|
||||
},
|
||||
"megamenu": {
|
||||
"close": "Menü schließen",
|
||||
"dock": "Menü andocken"
|
||||
"dock": "Menü andocken",
|
||||
"undock": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"close-menu": "Menü schließen",
|
||||
|
@ -935,7 +935,8 @@
|
||||
},
|
||||
"megamenu": {
|
||||
"close": "Close menu",
|
||||
"dock": "Dock menu"
|
||||
"dock": "Dock menu",
|
||||
"undock": "Undock menu"
|
||||
},
|
||||
"toolbar": {
|
||||
"close-menu": "Close menu",
|
||||
|
@ -941,7 +941,8 @@
|
||||
},
|
||||
"megamenu": {
|
||||
"close": "Cerrar menú",
|
||||
"dock": "Menú base"
|
||||
"dock": "Menú base",
|
||||
"undock": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"close-menu": "Cerrar menú",
|
||||
|
@ -941,7 +941,8 @@
|
||||
},
|
||||
"megamenu": {
|
||||
"close": "Fermer le menu",
|
||||
"dock": "Ancrer le menu"
|
||||
"dock": "Ancrer le menu",
|
||||
"undock": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"close-menu": "Fermer le menu",
|
||||
|
@ -935,7 +935,8 @@
|
||||
},
|
||||
"megamenu": {
|
||||
"close": "Cľőşę męʼnū",
|
||||
"dock": "Đőčĸ męʼnū"
|
||||
"dock": "Đőčĸ męʼnū",
|
||||
"undock": "Ůʼnđőčĸ męʼnū"
|
||||
},
|
||||
"toolbar": {
|
||||
"close-menu": "Cľőşę męʼnū",
|
||||
|
@ -929,7 +929,8 @@
|
||||
},
|
||||
"megamenu": {
|
||||
"close": "关闭菜单",
|
||||
"dock": "停靠菜单"
|
||||
"dock": "停靠菜单",
|
||||
"undock": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"close-menu": "关闭菜单",
|
||||
|
Loading…
Reference in New Issue
Block a user