+ // tabIndex={0} is needed for keyboard accessibility in the plot area
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
+
{loadingState === LoadingState.Loading ? : null}
@@ -262,20 +265,21 @@ const getStyles = (theme: GrafanaTheme2) => {
'.show-on-hover': {
opacity: '0',
+ visibility: 'hidden',
},
'&:focus-visible, &:hover': {
// only show menu icon on hover or focused panel
'.show-on-hover': {
opacity: '1',
+ visibility: 'visible',
},
},
- '&:focus-visible': {
- outline: `1px solid ${theme.colors.action.focus}`,
- },
+ '&:focus-visible': getFocusStyles(theme),
- '&:focus-within': {
+ // The not:(:focus) clause is so that this rule is only applied when decendants are focused (important otherwise the hover header is visible when panel is clicked).
+ '&:focus-within:not(:focus)': {
'.show-on-hover': {
visibility: 'visible',
opacity: '1',
diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx
index 2f80058db33..595bda416e0 100644
--- a/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx
+++ b/packages/grafana-ui/src/components/PanelChrome/PanelMenu.tsx
@@ -14,7 +14,6 @@ interface PanelMenuProps {
title?: string;
placement?: TooltipPlacement;
offset?: [number, number];
- onVisibleChange?: (state: boolean) => void;
onOpenMenu?: () => void;
}
@@ -25,7 +24,6 @@ export function PanelMenu({
offset,
dragClassCancel,
menuButtonClass,
- onVisibleChange,
onOpenMenu,
}: PanelMenuProps) {
const testId = title ? selectors.components.Panels.Panel.menu(title) : `panel-menu-button`;
@@ -35,9 +33,8 @@ export function PanelMenu({
if (show && onOpenMenu) {
onOpenMenu();
}
- return onVisibleChange;
},
- [onOpenMenu, onVisibleChange]
+ [onOpenMenu]
);
return (
diff --git a/packages/grafana-ui/src/components/VizLayout/VizLayout.tsx b/packages/grafana-ui/src/components/VizLayout/VizLayout.tsx
index d1ce27ef82e..b18a687db41 100644
--- a/packages/grafana-ui/src/components/VizLayout/VizLayout.tsx
+++ b/packages/grafana-ui/src/components/VizLayout/VizLayout.tsx
@@ -42,9 +42,7 @@ export const VizLayout: VizLayoutComponentType = ({ width, height, legend, child
if (!legend) {
return (
<>
- {/* tabIndex={0} is needed for keyboard accessibility in the plot area */}
- {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
-
+
{children(width, height)}
>
@@ -97,11 +95,7 @@ export const VizLayout: VizLayoutComponentType = ({ width, height, legend, child
return (
- {/* tabIndex={0} is needed for keyboard accessibility in the plot area */}
- {/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
-
- {size && children(size.width, size.height)}
-
+
{size && children(size.width, size.height)}
{legend}
diff --git a/packages/grafana-ui/src/components/uPlot/plugins/KeyboardPlugin.tsx b/packages/grafana-ui/src/components/uPlot/plugins/KeyboardPlugin.tsx
index 0f8a1a1d893..f23cded4172 100644
--- a/packages/grafana-ui/src/components/uPlot/plugins/KeyboardPlugin.tsx
+++ b/packages/grafana-ui/src/components/uPlot/plugins/KeyboardPlugin.tsx
@@ -13,12 +13,12 @@ const SHIFT_MULTIPLIER = 2 as const;
const KNOWN_KEYS = new Set(['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown', 'Shift', ' ']);
const initHook = (u: uPlot) => {
- let vizLayoutViz: HTMLElement | null = u.root.closest('[tabindex]');
+ let parentWithFocus: HTMLElement | null = u.root.closest('[tabindex]');
let pressedKeys = new Set
();
let dragStartX: number | null = null;
let keysLastHandledAt: number | null = null;
- if (!vizLayoutViz) {
+ if (!parentWithFocus) {
return;
}
@@ -133,7 +133,7 @@ const initHook = (u: uPlot) => {
const onFocus = () => {
// We only want to initialize the cursor if the user is using keyboard controls
- if (!vizLayoutViz?.matches(':focus-visible')) {
+ if (!parentWithFocus?.matches(':focus-visible')) {
return;
}
@@ -150,18 +150,17 @@ const initHook = (u: uPlot) => {
u.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false);
};
- vizLayoutViz.addEventListener('keydown', onKeyDown);
- vizLayoutViz.addEventListener('keyup', onKeyUp);
- vizLayoutViz.addEventListener('focus', onFocus);
- vizLayoutViz.addEventListener('blur', onBlur);
+ parentWithFocus.addEventListener('keydown', onKeyDown);
+ parentWithFocus.addEventListener('keyup', onKeyUp);
+ parentWithFocus.addEventListener('focus', onFocus);
+ parentWithFocus.addEventListener('blur', onBlur);
const onDestroy = () => {
- vizLayoutViz?.removeEventListener('keydown', onKeyDown);
- vizLayoutViz?.removeEventListener('keyup', onKeyUp);
- vizLayoutViz?.removeEventListener('focus', onFocus);
- vizLayoutViz?.removeEventListener('blur', onBlur);
-
- vizLayoutViz = null;
+ parentWithFocus?.removeEventListener('keydown', onKeyDown);
+ parentWithFocus?.removeEventListener('keyup', onKeyUp);
+ parentWithFocus?.removeEventListener('focus', onFocus);
+ parentWithFocus?.removeEventListener('blur', onBlur);
+ parentWithFocus = null;
};
(u.hooks.destroy ??= []).push(onDestroy);
diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx
index 6e6e2a06713..76e4da995eb 100644
--- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx
+++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin.tsx
@@ -57,6 +57,7 @@ export const TooltipPlugin = ({
const [coords, setCoords] = useState(null);
const [isActive, setIsActive] = useState(false);
const isMounted = useMountedState();
+ let parentWithFocus: HTMLElement | null = null;
const pluginId = `TooltipPlugin`;
@@ -92,12 +93,16 @@ export const TooltipPlugin = ({
config.addHook('init', (u) => {
plotInstance.current = u;
- u.root.parentElement?.addEventListener('focus', plotEnter);
u.over.addEventListener('mouseenter', plotEnter);
-
- u.root.parentElement?.addEventListener('blur', plotLeave);
u.over.addEventListener('mouseleave', plotLeave);
+ parentWithFocus = u.root.closest('[tabindex]');
+
+ if (parentWithFocus) {
+ parentWithFocus.addEventListener('focus', plotEnter);
+ parentWithFocus.addEventListener('blur', plotLeave);
+ }
+
if (sync && sync() === DashboardCursorSync.Crosshair) {
u.root.classList.add('shared-crosshair');
}
@@ -162,11 +167,15 @@ export const TooltipPlugin = ({
return () => {
setCoords(null);
+
if (plotInstance.current) {
plotInstance.current.over.removeEventListener('mouseleave', plotLeave);
plotInstance.current.over.removeEventListener('mouseenter', plotEnter);
- plotInstance.current.root.parentElement?.removeEventListener('focus', plotEnter);
- plotInstance.current.root.parentElement?.removeEventListener('blur', plotLeave);
+
+ if (parentWithFocus) {
+ parentWithFocus.removeEventListener('focus', plotEnter);
+ parentWithFocus.removeEventListener('blur', plotLeave);
+ }
}
};
}, [config, setCoords, setIsActive, setFocusedPointIdx, setFocusedPointIdxs]);
diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
index 6a77ad4eee4..295483aefa7 100644
--- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
+++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
@@ -93,6 +93,7 @@ export class DashboardGrid extends PureComponent {
}
this.props.dashboard.sortPanelsByGridPos();
+ this.forceUpdate();
};
triggerForceUpdate = () => {