fix(plugin-chart-echarts): tooltip overflow bug (#22218)

Co-authored-by: Ville Brofeldt <ville.brofeldt@apple.com>
This commit is contained in:
Ville Brofeldt 2022-11-24 14:11:01 +02:00 committed by GitHub
parent 3bc0865d90
commit 2e650eaebe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 105 additions and 74 deletions

View File

@ -36,7 +36,7 @@ import {
TimeSeriesDatum,
} from '../types';
import { getDateFormatter, parseMetricValue } from '../utils';
import { getDefaultPosition } from '../../utils/tooltip';
import { getDefaultTooltip } from '../../utils/tooltip';
import { Refs } from '../../types';
const defaultNumberFormatter = getNumberFormatter();
@ -234,8 +234,7 @@ export default function transformProps(
bottom: 0,
},
tooltip: {
position: getDefaultPosition(refs),
appendToBody: true,
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: 'axis',
formatter: renderTooltipFactory(formatTime, headerFormatter),

View File

@ -39,7 +39,7 @@ import { convertInteger } from '../utils/convertInteger';
import { defaultGrid, defaultYAxis } from '../defaults';
import { getPadding } from '../Timeseries/transformers';
import { OpacityEnum } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
import { Refs } from '../types';
export default function transformProps(
@ -139,6 +139,7 @@ export default function transformProps(
type: 'scatter',
data: outlierDatum.map(val => [name, val]),
tooltip: {
...getDefaultTooltip(refs),
formatter: (param: { data: [string, number] }) => {
const [outlierName, stats] = param.data;
const headline = groupbyLabels.length
@ -197,6 +198,7 @@ export default function transformProps(
type: 'boxplot',
data: transformedData,
tooltip: {
...getDefaultTooltip(refs),
formatter: (param: CallbackDataParams) => {
// @ts-ignore
const {
@ -273,7 +275,7 @@ export default function transformProps(
nameLocation: yAxisTitlePosition === 'Left' ? 'middle' : 'end',
},
tooltip: {
position: getDefaultPosition(refs),
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: 'item',
axisPointer: {

View File

@ -42,7 +42,7 @@ import {
} from '../utils/series';
import { defaultGrid } from '../defaults';
import { OpacityEnum, DEFAULT_LEGEND_FORM_DATA } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
import { Refs } from '../types';
const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT);
@ -215,7 +215,7 @@ export default function transformProps(
...defaultGrid,
},
tooltip: {
position: getDefaultPosition(refs),
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: 'item',
formatter: (params: any) =>

View File

@ -44,7 +44,7 @@ import {
FONT_SIZE_MULTIPLIERS,
} from './constants';
import { OpacityEnum } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
import { Refs } from '../types';
const setIntervalBoundsAndColors = (
@ -261,7 +261,7 @@ export default function transformProps(
color: gaugeSeriesOptions.detail?.color,
};
const tooltip = {
position: getDefaultPosition(refs),
...getDefaultTooltip(refs),
formatter: (params: CallbackDataParams) => {
const { name, value } = params;
return `${name} : ${formatValue(value as number)}`;
@ -315,7 +315,7 @@ export default function transformProps(
const echartOptions: EChartsCoreOption = {
tooltip: {
appendToBody: true,
...getDefaultTooltip(refs),
trigger: 'item',
},
series,

View File

@ -35,7 +35,7 @@ import {
} from './types';
import { DEFAULT_GRAPH_SERIES_OPTION } from './constants';
import { getChartPadding, getLegendProps, sanitizeHtml } from '../utils/series';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
import { Refs } from '../types';
type EdgeWithStyles = GraphEdgeItemOption & {
@ -192,6 +192,7 @@ export default function transformProps(
sliceId,
}: EchartsGraphFormData = { ...DEFAULT_GRAPH_FORM_DATA, ...formData };
const refs: Refs = {};
const metricLabel = getMetricLabel(metric);
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const nodes: { [name: string]: number } = {};
@ -212,7 +213,10 @@ export default function transformProps(
value: 0,
category,
select: DEFAULT_GRAPH_SERIES_OPTION.select,
tooltip: DEFAULT_GRAPH_SERIES_OPTION.tooltip,
tooltip: {
...getDefaultTooltip(refs),
...DEFAULT_GRAPH_SERIES_OPTION.tooltip,
},
});
}
const node = echartNodes[nodes[name]];
@ -296,13 +300,12 @@ export default function transformProps(
},
];
const refs: Refs = {};
const echartOptions: EChartsCoreOption = {
animationDuration: DEFAULT_GRAPH_SERIES_OPTION.animationDuration,
animationEasing: DEFAULT_GRAPH_SERIES_OPTION.animationEasing,
tooltip: {
...getDefaultTooltip(refs),
show: !inContextMenu,
position: getDefaultPosition(refs),
formatter: (params: any): string =>
edgeFormatter(
params.data.source,

View File

@ -80,7 +80,7 @@ import {
transformTimeseriesAnnotation,
} from '../Timeseries/transformers';
import { TIMESERIES_CONSTANTS, TIMEGRAIN_TO_TIMESTAMP } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
export default function transformProps(
chartProps: EchartsMixedTimeseriesProps,
@ -425,9 +425,8 @@ export default function transformProps(
},
],
tooltip: {
position: getDefaultPosition(refs),
...getDefaultTooltip(refs),
show: !inContextMenu,
appendToBody: true,
trigger: richTooltip ? 'axis' : 'item',
formatter: (params: any) => {
const xValue: number = richTooltip

View File

@ -45,7 +45,7 @@ import {
} from '../utils/series';
import { defaultGrid } from '../defaults';
import { convertInteger } from '../utils/convertInteger';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
import { Refs } from '../types';
const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT);
@ -303,8 +303,8 @@ export default function transformProps(
...defaultGrid,
},
tooltip: {
...getDefaultTooltip(refs),
show: !inContextMenu,
position: getDefaultPosition(refs),
trigger: 'item',
formatter: (params: any) =>
formatPieLabel({

View File

@ -44,7 +44,7 @@ import {
} from '../utils/series';
import { defaultGrid } from '../defaults';
import { Refs } from '../types';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
export function formatLabel({
params,
@ -232,7 +232,7 @@ export default function transformProps(
...defaultGrid,
},
tooltip: {
position: getDefaultPosition(refs),
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: 'item',
},

View File

@ -84,7 +84,7 @@ import {
TIMESERIES_CONSTANTS,
TIMEGRAIN_TO_TIMESTAMP,
} from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
export default function transformProps(
chartProps: EchartsTimeseriesChartProps,
@ -381,9 +381,8 @@ export default function transformProps(
xAxis,
yAxis,
tooltip: {
...getDefaultTooltip(refs),
show: !inContextMenu,
position: getDefaultPosition(refs),
appendToBody: true,
trigger: richTooltip ? 'axis' : 'item',
formatter: (params: any) => {
const [xIndex, yIndex] = isHorizontal ? [1, 0] : [0, 1];

View File

@ -31,6 +31,7 @@ import {
} from './types';
import { DEFAULT_FORM_DATA, DEFAULT_TREE_SERIES_OPTION } from './constants';
import { Refs } from '../types';
import { getDefaultTooltip } from '../utils/tooltip';
export function formatTooltip({
params,
@ -205,6 +206,7 @@ export default function transformProps(
animationEasing: DEFAULT_TREE_SERIES_OPTION.animationEasing,
series,
tooltip: {
...getDefaultTooltip(refs),
trigger: 'item',
triggerOn: 'mousemove',
formatter: (params: any) =>

View File

@ -47,7 +47,7 @@ import {
BORDER_COLOR,
} from './constants';
import { OpacityEnum } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { getDefaultTooltip } from '../utils/tooltip';
import { Refs } from '../types';
export function formatLabel({
@ -310,7 +310,7 @@ export default function transformProps(
const echartOptions: EChartsCoreOption = {
tooltip: {
position: getDefaultPosition(refs),
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: 'item',
formatter: (params: any) =>

View File

@ -21,56 +21,59 @@ import { CallbackDataParams } from 'echarts/types/src/util/types';
import { TOOLTIP_OVERFLOW_MARGIN, TOOLTIP_POINTER_MARGIN } from '../constants';
import { Refs } from '../types';
export function getDefaultPosition(refs: Refs) {
return (
canvasMousePos: [number, number],
params: CallbackDataParams,
tooltipDom: HTMLDivElement | null,
rect: any,
sizes: { contentSize: [number, number]; viewSize: [number, number] },
) => {
// algorithm partially based on this snippet:
// https://github.com/apache/echarts/issues/5004#issuecomment-559668309
export function getDefaultTooltip(refs: Refs) {
return {
appendToBody: true,
position: (
canvasMousePos: [number, number],
params: CallbackDataParams,
tooltipDom: HTMLDivElement | null,
rect: any,
sizes: { contentSize: [number, number]; viewSize: [number, number] },
) => {
// algorithm partially based on this snippet:
// https://github.com/apache/echarts/issues/5004#issuecomment-559668309
// The chart canvas position
const divRect = refs.divRef?.current?.getBoundingClientRect();
// The chart canvas position
const divRect = refs.divRef?.current?.getBoundingClientRect();
// The mouse coordinates relative to the whole window
// The first parameter to the position function is the mouse position relative to the canvas
const mouseX = canvasMousePos[0] + (divRect?.x || 0);
const mouseY = canvasMousePos[1] + (divRect?.y || 0);
// The mouse coordinates relative to the whole window
// The first parameter to the position function is the mouse position relative to the canvas
const mouseX = canvasMousePos[0] + (divRect?.x || 0);
const mouseY = canvasMousePos[1] + (divRect?.y || 0);
// The width and height of the tooltip dom element
const tooltipWidth = sizes.contentSize[0];
const tooltipHeight = sizes.contentSize[1];
// The width and height of the tooltip dom element
const tooltipWidth = sizes.contentSize[0];
const tooltipHeight = sizes.contentSize[1];
// Start by placing the tooltip top and right relative to the mouse position
let xPos = mouseX + TOOLTIP_POINTER_MARGIN;
let yPos = mouseY - TOOLTIP_POINTER_MARGIN - tooltipHeight;
// Start by placing the tooltip top and right relative to the mouse position
let xPos = mouseX + TOOLTIP_POINTER_MARGIN;
let yPos = mouseY - TOOLTIP_POINTER_MARGIN - tooltipHeight;
// The tooltip is overflowing past the right edge of the window
if (xPos + tooltipWidth >= document.documentElement.clientWidth) {
// Attempt to place the tooltip to the left of the mouse position
xPos = mouseX - TOOLTIP_POINTER_MARGIN - tooltipWidth;
// The tooltip is overflowing past the right edge of the window
if (xPos + tooltipWidth >= document.documentElement.clientWidth) {
// Attempt to place the tooltip to the left of the mouse position
xPos = mouseX - TOOLTIP_POINTER_MARGIN - tooltipWidth;
// The tooltip is overflowing past the left edge of the window
if (xPos <= 0)
// Place the tooltip a fixed distance from the left edge of the window
xPos = TOOLTIP_OVERFLOW_MARGIN;
}
// The tooltip is overflowing past the left edge of the window
if (xPos <= 0)
// Place the tooltip a fixed distance from the left edge of the window
xPos = TOOLTIP_OVERFLOW_MARGIN;
}
// 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
yPos = mouseY + TOOLTIP_POINTER_MARGIN;
// 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
yPos = mouseY + TOOLTIP_POINTER_MARGIN;
// The tooltip is overflowing past the bottom edge of the window
if (yPos + tooltipHeight >= document.documentElement.clientHeight)
// Place the tooltip a fixed distance from the top edge of the window
yPos = TOOLTIP_OVERFLOW_MARGIN;
}
// The tooltip is overflowing past the bottom edge of the window
if (yPos + tooltipHeight >= document.documentElement.clientHeight)
// Place the tooltip a fixed distance from the top edge of the window
yPos = TOOLTIP_OVERFLOW_MARGIN;
}
// Return the position (converted back to a relative position on the canvas)
return [xPos - (divRect?.x || 0), yPos - (divRect?.y || 0)];
// Return the position (converted back to a relative position on the canvas)
return [xPos - (divRect?.x || 0), yPos - (divRect?.y || 0)];
},
};
}

View File

@ -80,7 +80,11 @@ describe('EchartsGraph transformProps', () => {
label: { fontWeight: 'bolder' },
},
symbolSize: 50,
tooltip: { formatter: '{b}: {c}' },
tooltip: {
appendToBody: true,
formatter: '{b}: {c}',
position: expect.anything(),
},
value: 6,
},
{
@ -93,7 +97,11 @@ describe('EchartsGraph transformProps', () => {
label: { fontWeight: 'bolder' },
},
symbolSize: 50,
tooltip: { formatter: '{b}: {c}' },
tooltip: {
appendToBody: true,
formatter: '{b}: {c}',
position: expect.anything(),
},
value: 6,
},
{
@ -106,7 +114,11 @@ describe('EchartsGraph transformProps', () => {
label: { fontWeight: 'bolder' },
},
symbolSize: 10,
tooltip: { formatter: '{b}: {c}' },
tooltip: {
appendToBody: true,
formatter: '{b}: {c}',
position: expect.anything(),
},
value: 5,
},
{
@ -119,7 +131,11 @@ describe('EchartsGraph transformProps', () => {
label: { fontWeight: 'bolder' },
},
symbolSize: 10,
tooltip: { formatter: '{b}: {c}' },
tooltip: {
appendToBody: true,
formatter: '{b}: {c}',
position: expect.anything(),
},
value: 5,
},
],
@ -218,7 +234,11 @@ describe('EchartsGraph transformProps', () => {
symbolSize: 10,
category: 'category_value_1',
select: DEFAULT_GRAPH_SERIES_OPTION.select,
tooltip: DEFAULT_GRAPH_SERIES_OPTION.tooltip,
tooltip: {
appendToBody: true,
formatter: '{b}: {c}',
position: expect.anything(),
},
label: { show: true },
},
{
@ -228,7 +248,11 @@ describe('EchartsGraph transformProps', () => {
symbolSize: 10,
category: 'category_value_2',
select: DEFAULT_GRAPH_SERIES_OPTION.select,
tooltip: DEFAULT_GRAPH_SERIES_OPTION.tooltip,
tooltip: {
appendToBody: true,
formatter: '{b}: {c}',
position: expect.anything(),
},
label: { show: true },
},
],