From 17ed2cb3ecacde34fa92b6c85db5973dc2e73b87 Mon Sep 17 00:00:00 2001 From: MiniPear Date: Fri, 7 Jul 2023 11:18:07 +0800 Subject: [PATCH] fix(tooltip): multi chart when mounting to body (#5283) --- .../api-chart-tooltip-multi-chart.spec.ts | 29 +++++++++++ .../plots/api/chart-tooltip-multi-chart.ts | 51 +++++++++++++++++++ __tests__/plots/api/index.ts | 1 + src/interaction/tooltip.ts | 16 +++--- 4 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 __tests__/integration/api-chart-tooltip-multi-chart.spec.ts create mode 100644 __tests__/plots/api/chart-tooltip-multi-chart.ts diff --git a/__tests__/integration/api-chart-tooltip-multi-chart.spec.ts b/__tests__/integration/api-chart-tooltip-multi-chart.spec.ts new file mode 100644 index 0000000000..b07475fe64 --- /dev/null +++ b/__tests__/integration/api-chart-tooltip-multi-chart.spec.ts @@ -0,0 +1,29 @@ +import { chartTooltipMultiChart as render } from '../plots/api/chart-tooltip-multi-chart'; +import { dispatchFirstElementEvent } from './utils/event'; +import './utils/useSnapshotMatchers'; +import { createDOMGCanvas } from './utils/createDOMGCanvas'; + +describe('chart.interaction.tooltip', () => { + const canvas1 = createDOMGCanvas(640, 480); + const canvas2 = createDOMGCanvas(640, 480); + + it('tooltip should not be shared if mount to body.', async () => { + const { finished0, finished1 } = render({ + canvas1, + canvas2, + container: document.createElement('div'), + }); + await Promise.all([finished0, finished1]); + + dispatchFirstElementEvent(canvas1, 'pointerover'); + dispatchFirstElementEvent(canvas2, 'pointerover'); + expect( + Array.from(document.body.getElementsByClassName('g2-tooltip')).length, + ).toBe(2); + }); + + afterAll(() => { + canvas1?.destroy(); + canvas2?.destroy(); + }); +}); diff --git a/__tests__/plots/api/chart-tooltip-multi-chart.ts b/__tests__/plots/api/chart-tooltip-multi-chart.ts new file mode 100644 index 0000000000..8376b9f47b --- /dev/null +++ b/__tests__/plots/api/chart-tooltip-multi-chart.ts @@ -0,0 +1,51 @@ +import { Chart } from '../../../src'; + +export function chartTooltipMultiChart(context) { + const { container, canvas1, canvas2 } = context; + + const options = { + type: 'interval', + data: [ + { genre: 'Sports', sold: 275 }, + { genre: 'Strategy', sold: 115 }, + { genre: 'Action', sold: 120 }, + { genre: 'Shooter', sold: 350 }, + { genre: 'Other', sold: 150 }, + ], + encode: { + x: 'genre', + y: 'sold', + }, + interaction: { tooltip: { mount: 'body' } }, + }; + + // View0 + const view0Container = document.createElement('div'); + container.appendChild(view0Container); + + const view0 = new Chart({ + theme: 'classic', + container: view0Container, + canvas: canvas1, + }); + + view0.options(options); + + const finished0 = view0.render(); + + // View1. + const view1Container = document.createElement('div'); + container.appendChild(view1Container); + + const view1 = new Chart({ + theme: 'classic', + container: view1Container, + canvas: canvas2, + }); + + view1.options(options); + + const finished1 = view1.render(); + + return { finished0, finished1 }; +} diff --git a/__tests__/plots/api/index.ts b/__tests__/plots/api/index.ts index d1e47b9ff0..c73caaaab9 100644 --- a/__tests__/plots/api/index.ts +++ b/__tests__/plots/api/index.ts @@ -38,3 +38,4 @@ export { chartOptionsCompositeMark } from './chart-options-composite-mark'; export { chartEmitItemTooltipHideContent } from './chart-emit-item-tooltip-hide-content'; export { chartEmitClickTooltip } from './chart-emit-click-tooltip'; export { chartChangeDataLegend } from './chart-change-data-legend'; +export { chartTooltipMultiChart } from './chart-tooltip-multi-chart'; diff --git a/src/interaction/tooltip.ts b/src/interaction/tooltip.ts index 5f78f94a4f..47e7ee7957 100644 --- a/src/interaction/tooltip.ts +++ b/src/interaction/tooltip.ts @@ -106,9 +106,12 @@ function showTooltip({ mount, bounding, }) { - // All the views share the same tooltip. const canvasContainer = root.getRootNode().defaultView.getConfig().container; - const container = single ? getContainer(root, mount) : root; + const container = getContainer(root, mount); + + // All the views share the same tooltip. + const parent = single ? canvasContainer : root; + const b = bounding || getBounding(root); const containerOffset = getContainerOffset(canvasContainer, container); const { @@ -121,7 +124,7 @@ function showTooltip({ b, containerOffset, ), - } = container; + } = parent as any; const { items, title = '' } = data; tooltipElement.update({ x, @@ -134,15 +137,16 @@ function showTooltip({ content: render(event, { items, title }), }), }); - container.tooltipElement = tooltipElement; + parent.tooltipElement = tooltipElement; } function hideTooltip({ root, single, emitter, nativeEvent = true, mount }) { if (nativeEvent) { emitter.emit('tooltip:hide', { nativeEvent }); } - const container = single ? getContainer(root, mount) : root; - const { tooltipElement } = container; + const canvasContainer = root.getRootNode().defaultView.getConfig().container; + const parent = single ? canvasContainer : root; + const { tooltipElement } = parent; if (tooltipElement) { tooltipElement.hide(); }