From 31921bbb01f7546647d3c8ea183412dabc27518a Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Wed, 10 Jan 2024 12:09:54 -0600 Subject: [PATCH] DashboardGrid: Add LayoutItemContext to affect zIndex from panels (#80116) --- .../components/Layout/LayoutItemContext.ts | 21 ++++++++++++ packages/grafana-ui/src/components/index.ts | 2 ++ .../dashboard/dashgrid/DashboardGrid.tsx | 34 ++++++++++++++++--- 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 packages/grafana-ui/src/components/Layout/LayoutItemContext.ts diff --git a/packages/grafana-ui/src/components/Layout/LayoutItemContext.ts b/packages/grafana-ui/src/components/Layout/LayoutItemContext.ts new file mode 100644 index 00000000000..08832aab601 --- /dev/null +++ b/packages/grafana-ui/src/components/Layout/LayoutItemContext.ts @@ -0,0 +1,21 @@ +import { createContext } from 'react'; + +export interface LayoutItemContextProps { + boostZIndex(): () => void; +} + +/** + * Provides an API for downstream components (e.g. within panels) to inform the layout + * that anchored tooltips or context menus could overflow the panel bounds. The layout + * system can then boost the z-index of items with any anchored contents to prevent the overflown + * content from rendering underneath adjacent layout items (e.g. other panels) that naturally + * render later/higher in the stacking order + * + * This is used by VizTooltips and Annotations, which anchor to data points or time range within + * the viz drawing area + * + * @internal + */ +export const LayoutItemContext = createContext({ + boostZIndex: () => () => {}, +}); diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index f05bc49fd79..9325c425d40 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -264,6 +264,8 @@ export { Avatar } from './UsersIndicator/Avatar'; export { InlineFormLabel } from './FormLabel/FormLabel'; export { Divider } from './Divider/Divider'; +export { LayoutItemContext, type LayoutItemContextProps } from './Layout/LayoutItemContext'; + /** @deprecated Please use non-legacy versions of these components */ const LegacyForms = { SecretFormField, diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 532ebd73217..8fba7295ca9 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -1,10 +1,12 @@ import classNames from 'classnames'; -import React, { PureComponent, CSSProperties } from 'react'; +import React, { PureComponent, CSSProperties, useRef, useCallback, useReducer, useMemo } from 'react'; import ReactGridLayout, { ItemCallback } from 'react-grid-layout'; import AutoSizer from 'react-virtualized-auto-sizer'; import { Subscription } from 'rxjs'; +import { zIndex } from '@grafana/data/src/themes/zIndex'; import { config } from '@grafana/runtime'; +import { LayoutItemContext } from '@grafana/ui'; import appEvents from 'app/core/app_events'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants'; import { contextSrv } from 'app/core/services/context_srv'; @@ -383,6 +385,21 @@ const GrafanaGridItem = React.forwardRef(( let width = 100; let height = 100; + const boostedCount = useRef(0); + const [_, forceUpdate] = useReducer((x) => x + 1, 0); + + const boostZIndex = useCallback(() => { + boostedCount.current += 1; + forceUpdate(); + + return () => { + boostedCount.current -= 1; + forceUpdate(); + }; + }, [forceUpdate]); + + const ctxValue = useMemo(() => ({ boostZIndex }), [boostZIndex]); + const { gridWidth, gridPos, isViewing, windowHeight, windowWidth, descendingOrderIndex, ...divProps } = props; const style: CSSProperties = props.style ?? {}; @@ -413,10 +430,17 @@ const GrafanaGridItem = React.forwardRef(( // props.children[0] is our main children. RGL adds the drag handle at props.children[1] return ( -
- {/* Pass width and height to children as render props */} - {[props.children[0](width, height), props.children.slice(1)]} -
+ +
+ {/* Pass width and height to children as render props */} + {[props.children[0](width, height), props.children.slice(1)]} +
+
); });