From f7f1f6f333be206e3750d9a064c7989855333989 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 6 Jan 2021 17:13:42 +0100 Subject: [PATCH] fix: align tooltip z-index to EUI tooltip z-index (#931) This commit moves the z-index value to display the tooltip below the current navigation bars but above the rest of the UI in Kibana Co-authored-by: nickofthyme --- packages/osd-charts/.storybook/style.scss | 6 ++ packages/osd-charts/api/charts.api.md | 2 + .../xy_chart/renderer/dom/_crosshair.scss | 4 -- .../xy_chart/renderer/dom/_highlighter.scss | 1 - .../dom/annotations/annotation_tooltip.tsx | 5 +- .../renderer/dom/annotations/annotations.tsx | 17 ++++- .../xy_chart/renderer/dom/crosshair.tsx | 6 ++ .../xy_chart/renderer/dom/highlighter.tsx | 13 ++-- .../__snapshots__/chart.test.tsx.snap | 2 +- packages/osd-charts/src/components/chart.tsx | 9 +++ .../src/components/portal/_portal.scss | 1 - .../src/components/portal/tooltip_portal.tsx | 18 ++++- .../osd-charts/src/components/portal/utils.ts | 72 ++++++++++++++++++- .../src/components/tooltip/tooltip.tsx | 6 ++ .../osd-charts/src/state/actions/index.ts | 4 +- .../osd-charts/src/state/actions/z_index.ts | 32 +++++++++ packages/osd-charts/src/state/chart_state.ts | 18 +++-- 17 files changed, 193 insertions(+), 23 deletions(-) create mode 100644 packages/osd-charts/src/state/actions/z_index.ts diff --git a/packages/osd-charts/.storybook/style.scss b/packages/osd-charts/.storybook/style.scss index 9c9f697192ea..7e0bf21bca58 100644 --- a/packages/osd-charts/.storybook/style.scss +++ b/packages/osd-charts/.storybook/style.scss @@ -4,6 +4,11 @@ html { background-color: white; } +#root { + z-index: 200; + position: relative; +} + .story-chart { box-sizing: border-box; background: white; @@ -23,6 +28,7 @@ html { width: 100%; height: 400px; position: relative; + z-index: 500; box-sizing: border-box; background-color: blanchedalmond; diff --git a/packages/osd-charts/api/charts.api.md b/packages/osd-charts/api/charts.api.md index e1d5681d0fd3..5c1859c6e8ab 100644 --- a/packages/osd-charts/api/charts.api.md +++ b/packages/osd-charts/api/charts.api.md @@ -332,6 +332,8 @@ export interface BubbleSeriesStyle { export class Chart extends React.Component { constructor(props: ChartProps); // (undocumented) + componentDidMount(): void; + // (undocumented) componentWillUnmount(): void; // (undocumented) static defaultProps: ChartProps; diff --git a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_crosshair.scss b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_crosshair.scss index b5750a68d0d5..a60dcaeb80d4 100644 --- a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_crosshair.scss +++ b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_crosshair.scss @@ -4,7 +4,3 @@ position: absolute; pointer-events: none; } - -.echCrosshair__line { - z-index: $euiZLevel8; -} diff --git a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_highlighter.scss b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_highlighter.scss index cb4b3f63a30f..775cc040ac85 100644 --- a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_highlighter.scss +++ b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/_highlighter.scss @@ -1,6 +1,5 @@ .echHighlighter { position: absolute; - z-index: 1000; pointer-events: none; top: 0; bottom: 0; diff --git a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/annotations/annotation_tooltip.tsx b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/annotations/annotation_tooltip.tsx index 87b1b5abe945..7ea0173af311 100644 --- a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/annotations/annotation_tooltip.tsx +++ b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/annotations/annotation_tooltip.tsx @@ -27,11 +27,12 @@ interface AnnotationTooltipProps { state: AnnotationTooltipState | null; chartRef: RefObject; chartId: string; + zIndex: number; onScroll?: () => void; } /** @internal */ -export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll }: AnnotationTooltipProps) => { +export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll, zIndex }: AnnotationTooltipProps) => { const renderTooltip = useCallback(() => { if (!state || !state.isVisible) { return null; @@ -77,6 +78,8 @@ export const AnnotationTooltip = ({ state, chartRef, chartId, onScroll }: Annota ; annotationSpecs: AnnotationSpec[]; chartId: string; + zIndex: number; } interface AnnotationsOwnProps { @@ -93,6 +94,7 @@ const AnnotationsComponent = ({ annotationDimensions, getChartContainerRef, chartId, + zIndex, onPointerMove, }: AnnotationsProps) => { const renderAnnotationMarkers = useCallback((): JSX.Element[] => { @@ -125,7 +127,13 @@ const AnnotationsComponent = ({ return ( <> {renderAnnotationMarkers()} - + ); }; @@ -136,6 +144,7 @@ const mapDispatchToProps = (dispatch: Dispatch): AnnotationsDispatchProps => bindActionCreators({ onPointerMove: onPointerMoveAction }, dispatch); const mapStateToProps = (state: GlobalChartState): AnnotationsStateProps => { + const { zIndex, chartId } = state; if (getInternalIsInitializedSelector(state) !== InitStatus.Initialized) { return { isChartEmpty: true, @@ -143,7 +152,8 @@ const mapStateToProps = (state: GlobalChartState): AnnotationsStateProps => { annotationDimensions: new Map(), annotationSpecs: [], tooltipState: null, - chartId: '', + chartId, + zIndex, }; } return { @@ -152,7 +162,8 @@ const mapStateToProps = (state: GlobalChartState): AnnotationsStateProps => { annotationDimensions: computeAnnotationDimensionsSelector(state), annotationSpecs: getAnnotationSpecsSelector(state), tooltipState: getAnnotationTooltipStateSelector(state), - chartId: state.chartId, + chartId, + zIndex, }; }; diff --git a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/crosshair.tsx b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/crosshair.tsx index b7894f621fb6..c0e908ed5a14 100644 --- a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/crosshair.tsx +++ b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/crosshair.tsx @@ -42,6 +42,7 @@ interface CrosshairProps { cursorLinePosition?: Dimensions; tooltipType: TooltipType; fromExternalEvent?: boolean; + zIndex: number; } function canRenderBand(type: TooltipType, visible: boolean, fromExternalEvent?: boolean) { @@ -84,6 +85,7 @@ class CrosshairComponent extends React.Component { cursorLinePosition, tooltipType, chartRotation, + zIndex, } = this.props; if (!cursorLinePosition || !canRenderHelpLine(tooltipType, line.visible)) { @@ -97,6 +99,7 @@ class CrosshairComponent extends React.Component { borderTopWidth: line.strokeWidth, borderTopColor: line.stroke, borderTopStyle: line.dash ? 'dashed' : 'solid', + zIndex, }; } else { style = { @@ -104,6 +107,7 @@ class CrosshairComponent extends React.Component { borderLeftWidth: line.strokeWidth, borderLeftColor: line.stroke, borderLeftStyle: line.dash ? 'dashed' : 'solid', + zIndex, }; } return
; @@ -125,6 +129,7 @@ const mapStateToProps = (state: GlobalChartState): CrosshairProps => { theme: LIGHT_THEME, chartRotation: 0, tooltipType: TooltipType.None, + zIndex: 0, }; } const settings = getSettingsSpecSelector(state); @@ -138,6 +143,7 @@ const mapStateToProps = (state: GlobalChartState): CrosshairProps => { cursorLinePosition: getCursorLinePositionSelector(state), tooltipType, fromExternalEvent: cursorBandPosition?.fromExternalEvent, + zIndex: state.zIndex, }; }; diff --git a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx index d3bdb54f1b39..86f17a58cf42 100644 --- a/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx +++ b/packages/osd-charts/src/chart_types/xy_chart/renderer/dom/highlighter.tsx @@ -36,6 +36,7 @@ import { computeChartTransform } from '../../state/utils/utils'; interface HighlighterProps { initialized: boolean; chartId: string; + zIndex: number; highlightedGeometries: IndexedGeometry[]; chartTransform: Transform; chartDimensions: Dimensions; @@ -51,12 +52,12 @@ class HighlighterComponent extends React.Component { static displayName = 'Highlighter'; render() { - const { highlightedGeometries, chartDimensions, chartRotation, chartId } = this.props; + const { highlightedGeometries, chartDimensions, chartRotation, chartId, zIndex } = this.props; const clipWidth = [90, -90].includes(chartRotation) ? chartDimensions.height : chartDimensions.width; const clipHeight = [90, -90].includes(chartRotation) ? chartDimensions.width : chartDimensions.height; const clipPathId = `echHighlighterClipPath__${chartId}`; return ( - + @@ -104,10 +105,12 @@ class HighlighterComponent extends React.Component { } const mapStateToProps = (state: GlobalChartState): HighlighterProps => { + const { chartId, zIndex } = state; if (getInternalIsInitializedSelector(state) !== InitStatus.Initialized) { return { initialized: false, - chartId: state.chartId, + chartId, + zIndex, highlightedGeometries: [], chartTransform: { x: 0, @@ -118,9 +121,11 @@ const mapStateToProps = (state: GlobalChartState): HighlighterProps => { chartRotation: 0, }; } + return { initialized: true, - chartId: state.chartId, + chartId, + zIndex, highlightedGeometries: getHighlightedGeomsSelector(state), chartTransform: computeChartTransformSelector(state), chartDimensions: computeChartDimensionsSelector(state).chartDimensions, diff --git a/packages/osd-charts/src/components/__snapshots__/chart.test.tsx.snap b/packages/osd-charts/src/components/__snapshots__/chart.test.tsx.snap index 1e984a129f19..75b1cc181a2f 100644 --- a/packages/osd-charts/src/components/__snapshots__/chart.test.tsx.snap +++ b/packages/osd-charts/src/components/__snapshots__/chart.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Chart should render the legend name test 1`] = `"
"`; +exports[`Chart should render the legend name test 1`] = `"
"`; diff --git a/packages/osd-charts/src/components/chart.tsx b/packages/osd-charts/src/components/chart.tsx index 578eaa23ba43..80343df2d101 100644 --- a/packages/osd-charts/src/components/chart.tsx +++ b/packages/osd-charts/src/components/chart.tsx @@ -27,6 +27,7 @@ import { isHorizontalAxis } from '../chart_types/xy_chart/utils/axis_type_utils' import { PointerEvent } from '../specs'; import { SpecsParser } from '../specs/specs_parser'; import { onExternalPointerEvent } from '../state/actions/events'; +import { onComputedZIndex } from '../state/actions/z_index'; import { chartStoreReducer, GlobalChartState } from '../state/chart_state'; import { getInternalIsInitializedSelector, InitStatus } from '../state/selectors/get_internal_is_intialized'; import { getSettingsSpecSelector } from '../state/selectors/get_settings_specs'; @@ -38,6 +39,7 @@ import { ChartResizer } from './chart_resizer'; import { ChartStatus } from './chart_status'; import { ErrorBoundary } from './error_boundary'; import { Legend } from './legend/legend'; +import { getElementZIndex } from './portal/utils'; interface ChartProps { /** @@ -108,6 +110,13 @@ export class Chart extends React.Component { }); } + componentDidMount() { + if (this.chartContainerRef.current) { + const zIndex = getElementZIndex(this.chartContainerRef.current, document.body); + this.chartStore.dispatch(onComputedZIndex(zIndex)); + } + } + componentWillUnmount() { this.unsubscribeToStore(); } diff --git a/packages/osd-charts/src/components/portal/_portal.scss b/packages/osd-charts/src/components/portal/_portal.scss index aa799d92768f..924e80a136f7 100644 --- a/packages/osd-charts/src/components/portal/_portal.scss +++ b/packages/osd-charts/src/components/portal/_portal.scss @@ -1,6 +1,5 @@ [id^='echTooltipPortal'] { pointer-events: none; - z-index: 10000000; } [id^='echAnchor'] { diff --git a/packages/osd-charts/src/components/portal/tooltip_portal.tsx b/packages/osd-charts/src/components/portal/tooltip_portal.tsx index 91d1be30361f..8d7dc7da0740 100644 --- a/packages/osd-charts/src/components/portal/tooltip_portal.tsx +++ b/packages/osd-charts/src/components/portal/tooltip_portal.tsx @@ -29,6 +29,7 @@ import { DEFAULT_POPPER_SETTINGS, getOrCreateNode, isHTMLElement } from './utils * @todo make this type conditional to use PortalAnchorProps or PortalAnchorRefProps */ type PortalTooltipProps = { + zIndex: number; /** * String used to designate unique portal */ @@ -55,7 +56,15 @@ type PortalTooltipProps = { chartId: string; }; -const TooltipPortalComponent = ({ anchor, scope, settings, children, visible, chartId }: PortalTooltipProps) => { +const TooltipPortalComponent = ({ + anchor, + scope, + settings, + children, + visible, + chartId, + zIndex, +}: PortalTooltipProps) => { /** * Anchor element used to position tooltip */ @@ -69,7 +78,12 @@ const TooltipPortalComponent = ({ anchor, scope, settings, children, visible, ch * This must not be removed from DOM throughout life of this component. * Otherwise the portal will loose reference to the correct node. */ - const portalNodeElement = getOrCreateNode(`echTooltipPortal${scope}__${chartId}`, 'echTooltipPortal__invisible'); + const portalNodeElement = getOrCreateNode( + `echTooltipPortal${scope}__${chartId}`, + 'echTooltipPortal__invisible', + undefined, + zIndex, + ); const portalNode = useRef(portalNodeElement); diff --git a/packages/osd-charts/src/components/portal/utils.ts b/packages/osd-charts/src/components/portal/utils.ts index 1149b8c1be08..fa191de8ad6f 100644 --- a/packages/osd-charts/src/components/portal/utils.ts +++ b/packages/osd-charts/src/components/portal/utils.ts @@ -33,7 +33,12 @@ export const DEFAULT_POPPER_SETTINGS: Required down until the stacking context + * split is found, or if there is no split then a specific z-index is unimportant + */ + + // build the array of the element + its offset parents + const nodesToInspect: HTMLElement[] = []; + while (true) { + nodesToInspect.push(element); + + // AFAICT this is a valid cast - the libdefs appear wrong + element = element.offsetParent as HTMLElement; + + // stop if there is no parent + if (element == null) { + break; + } + + // stop if the parent contains the related element + // as this is the z-index ancestor + if (element.contains(cousin)) { + break; + } + } + + // reverse the nodes to walk from top -> element + for (let i = nodesToInspect.length - 1; i >= 0; i--) { + const node = nodesToInspect[i]; + // get this node's z-index css value + const zIndex = window.document.defaultView!.getComputedStyle(node).getPropertyValue('z-index'); + + // if the z-index is not a number (e.g. "auto") return null, else the value + const parsedZIndex = parseInt(zIndex, 10); + if (!isNaN(parsedZIndex)) { + return parsedZIndex; + } + } + + return 0; +} diff --git a/packages/osd-charts/src/components/tooltip/tooltip.tsx b/packages/osd-charts/src/components/tooltip/tooltip.tsx index 4042ce835bcd..1418efc28ca9 100644 --- a/packages/osd-charts/src/components/tooltip/tooltip.tsx +++ b/packages/osd-charts/src/components/tooltip/tooltip.tsx @@ -45,6 +45,7 @@ interface TooltipDispatchProps { } interface TooltipStateProps { + zIndex: number; visible: boolean; position: TooltipAnchorPosition | null; info?: TooltipInfo; @@ -63,6 +64,7 @@ type TooltipProps = TooltipDispatchProps & TooltipStateProps & TooltipOwnProps; const TooltipComponent = ({ info, + zIndex, headerFormatter, position, getChartContainerRef, @@ -207,6 +209,8 @@ const TooltipComponent = ({ return ( { const settings = getTooltipSettings(settingsSpec, isExternal); return { visible, + zIndex: state.zIndex, info: getInternalTooltipInfoSelector(state), position: getInternalTooltipAnchorPositionSelector(state), headerFormatter: getTooltipHeaderFormatterSelector(state), diff --git a/packages/osd-charts/src/state/actions/index.ts b/packages/osd-charts/src/state/actions/index.ts index 4cd570db798f..8ddc4b8ffa5b 100644 --- a/packages/osd-charts/src/state/actions/index.ts +++ b/packages/osd-charts/src/state/actions/index.ts @@ -25,6 +25,7 @@ import { KeyActions } from './key'; import { LegendActions } from './legend'; import { MouseActions } from './mouse'; import { SpecActions } from './specs'; +import { ZIndexActions } from './z_index'; /** @internal */ export type StateActions = @@ -35,4 +36,5 @@ export type StateActions = | EventsActions | MouseActions | KeyActions - | ColorsActions; + | ColorsActions + | ZIndexActions; diff --git a/packages/osd-charts/src/state/actions/z_index.ts b/packages/osd-charts/src/state/actions/z_index.ts new file mode 100644 index 000000000000..8ff0098c6479 --- /dev/null +++ b/packages/osd-charts/src/state/actions/z_index.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +export const Z_INDEX_EVENT = 'Z_INDEX_EVENT'; + +interface ZIndexEvent { + type: typeof Z_INDEX_EVENT; + zIndex: number; +} + +/** @internal */ +export function onComputedZIndex(zIndex: number): ZIndexEvent { + return { type: Z_INDEX_EVENT, zIndex }; +} + +/** @internal */ +export type ZIndexActions = ZIndexEvent; diff --git a/packages/osd-charts/src/state/chart_state.ts b/packages/osd-charts/src/state/chart_state.ts index 67869ca6db5d..3bc9e6239be7 100644 --- a/packages/osd-charts/src/state/chart_state.ts +++ b/packages/osd-charts/src/state/chart_state.ts @@ -27,8 +27,7 @@ import { XYAxisChartState } from '../chart_types/xy_chart/state/chart_state'; import { LegendItem, LegendItemExtraValues } from '../commons/legend'; import { SeriesKey, SeriesIdentifier } from '../commons/series_id'; import { TooltipInfo, TooltipAnchorPosition } from '../components/tooltip/types'; -import { Spec, PointerEvent } from '../specs'; -import { DEFAULT_SETTINGS_SPEC } from '../specs'; +import { Spec, PointerEvent, DEFAULT_SETTINGS_SPEC } from '../specs'; import { Color } from '../utils/commons'; import { Dimensions } from '../utils/dimensions'; import { Logger } from '../utils/logger'; @@ -36,9 +35,10 @@ import { Point } from '../utils/point'; import { StateActions } from './actions'; import { CHART_RENDERED } from './actions/chart'; import { UPDATE_PARENT_DIMENSION } from './actions/chart_settings'; -import { SET_PERSISTED_COLOR, SET_TEMPORARY_COLOR, CLEAR_TEMPORARY_COLORS } from './actions/colors'; +import { CLEAR_TEMPORARY_COLORS, SET_PERSISTED_COLOR, SET_TEMPORARY_COLOR } from './actions/colors'; import { EXTERNAL_POINTER_EVENT } from './actions/events'; -import { SPEC_PARSED, SPEC_UNMOUNTED, UPSERT_SPEC, REMOVE_SPEC } from './actions/specs'; +import { REMOVE_SPEC, SPEC_PARSED, SPEC_UNMOUNTED, UPSERT_SPEC } from './actions/specs'; +import { Z_INDEX_EVENT } from './actions/z_index'; import { interactionsReducer } from './reducers/interactions'; import { getInternalIsInitializedSelector, InitStatus } from './selectors/get_internal_is_intialized'; import { getLegendItemsSelector } from './selectors/get_legend_items'; @@ -201,6 +201,10 @@ export interface GlobalChartState { * a unique ID for each chart used by re-reselect to memoize selector per chart */ chartId: string; + /** + * The Z-Index of the chart component + */ + zIndex: number; /** * true when all all the specs are parsed ad stored into the specs object */ @@ -247,6 +251,7 @@ export interface GlobalChartState { /** @internal */ export const getInitialState = (chartId: string): GlobalChartState => ({ chartId, + zIndex: 0, specsInitialized: false, specParsing: false, chartRendered: false, @@ -282,6 +287,11 @@ export const chartStoreReducer = (chartId: string) => { const initialState = getInitialState(chartId); return (state = initialState, action: StateActions): GlobalChartState => { switch (action.type) { + case Z_INDEX_EVENT: + return { + ...state, + zIndex: action.zIndex, + }; case SPEC_PARSED: const chartType = findMainChartType(state.specs);