diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts index 78c1222a1fc6..a7a40ddaa079 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts @@ -472,6 +472,7 @@ export function transformFormulaAnnotation( return { name, id: name, + z: 10, itemStyle: { color: color || colorScale(name, sliceId), }, @@ -565,6 +566,7 @@ export function transformIntervalAnnotation( id: `Interval - ${name}`, type: 'line', animation: false, + z: 10, markArea: { silent: false, itemStyle: { @@ -660,6 +662,7 @@ export function transformEventAnnotation( id: `Event - ${name}`, type: 'line', animation: false, + z: 10, markLine: { silent: false, symbol: 'none', @@ -705,6 +708,7 @@ export function transformTimeseriesAnnotation( type: 'line', id: name, name, + z: 10, data, symbolSize: showMarkers ? markerSize : 0, itemStyle: computedStyle, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/constants.ts b/superset-frontend/plugins/plugin-chart-echarts/src/constants.ts index f496b7e2af81..3044bf10d704 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/constants.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/constants.ts @@ -122,4 +122,8 @@ export const TOOLTIP_POINTER_MARGIN = 10; // from the edge of the window should the tooltip be kept export const TOOLTIP_OVERFLOW_MARGIN = 5; +// Minimum distance from the top of the chart container to keep the tooltip, +// reserving space for annotation labels rendered at insideEndTop of markLines/markAreas +export const TOOLTIP_TOP_CLEARANCE = 40; + export const DEFAULT_LOCALE = 'en'; diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts index 59aaf63b0d7c..4fe0c16be519 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/tooltip.ts @@ -24,7 +24,11 @@ import { getColumnLabel, getMetricLabel, } from '@superset-ui/core'; -import { TOOLTIP_OVERFLOW_MARGIN, TOOLTIP_POINTER_MARGIN } from '../constants'; +import { + TOOLTIP_OVERFLOW_MARGIN, + TOOLTIP_POINTER_MARGIN, + TOOLTIP_TOP_CLEARANCE, +} from '../constants'; import { Refs } from '../types'; export function getDefaultTooltip(refs: Refs) { @@ -107,18 +111,39 @@ export function getDefaultTooltip(refs: Refs) { } } - // Position tooltip above cursor, or below if no space - yPos = mouseY - TOOLTIP_POINTER_MARGIN - effectiveTooltipHeight; + // Mirror horizontal logic: position tooltip below cursor when in top half of chart, + // above cursor when in bottom half. This prevents the tooltip from covering annotation + // labels that appear at the top of the chart (markArea/markLine labels). + const chartHeight = divRect?.height || viewportHeight; + const cursorYInChart = canvasMousePos[1]; + const isInTopHalfOfChart = cursorYInChart < chartHeight / 2; - // The tooltip is overflowing past the top edge of the window - if (yPos <= 0) { - // Attempt to place the tooltip to the bottom of the mouse position + if (isInTopHalfOfChart) { yPos = mouseY + TOOLTIP_POINTER_MARGIN; - // The tooltip is overflowing past the bottom edge of the window - if (yPos + effectiveTooltipHeight >= viewportHeight) - // Place the tooltip a fixed distance from the top edge of the window - yPos = TOOLTIP_OVERFLOW_MARGIN; + if (yPos + effectiveTooltipHeight >= viewportHeight) { + yPos = mouseY - TOOLTIP_POINTER_MARGIN - effectiveTooltipHeight; + + if (yPos <= 0) { + yPos = TOOLTIP_OVERFLOW_MARGIN; + } + } + } else { + yPos = mouseY - TOOLTIP_POINTER_MARGIN - effectiveTooltipHeight; + + if (yPos <= 0) { + yPos = mouseY + TOOLTIP_POINTER_MARGIN; + + if (yPos + effectiveTooltipHeight >= viewportHeight) { + yPos = TOOLTIP_OVERFLOW_MARGIN; + } + } + } + + // Clamp tooltip away from the top of the chart to avoid covering annotation labels + // (markLine/markArea labels rendered at insideEndTop are within the first ~40px) + if (divRect) { + yPos = Math.max(yPos, divRect.y + TOOLTIP_TOP_CLEARANCE); } // Return the position (converted back to a relative position on the canvas)