11/**
2- * Line chart with `tooltip.shared: true` — clicking a marker should
3- * report the series the user clicked, not always series 0.
2+ * Line chart with `tooltip.shared: true` — click events must report the
3+ * series the user actually clicked, not always series 0.
44 *
5- * Background: a previous fix for #3439 (zoomed-bar shared tooltip)
6- * skipped Y in closestInMultiArray whenever allSeriesHasEqualX was true.
7- * That made every line-chart series tie at distance 0, so series 0
8- * always won → markerClick always reported series 0 regardless of which
9- * line the user clicked. The fix narrows the Y-skip to bar charts only;
10- * line/area charts now use full Euclidean distance to identify the
11- * actually-hovered series.
5+ * Two distinct bugs converge here:
6+ * 1. The fix for #3439 (zoomed-bar shared tooltip) made
7+ * closestInMultiArray skip Y whenever allSeriesHasEqualX was true.
8+ * That tied every line series at distance X = 0, so series 0 always
9+ * won. The Y-skip is now narrowed to bar charts only.
10+ * 2. Even before #3439, closestInMultiArray measured distance to each
11+ * series's *markers* rather than its *line segments*. Clicks landing
12+ * between two markers picked whichever series's marker was closest
13+ * to that empty space — often the wrong line. For line/area charts
14+ * we now project the cursor onto each segment, which makes clicks
15+ * on the line itself report the line under the cursor.
1216 *
13- * Uses real `page.mouse.click` because synthetic dispatchEvent on a
14- * marker would bypass the foreignObject overlay and silently mask the
15- * bug. Marker click coordinates are taken from the marker's bounding
16- * rect, so the click lands exactly on the series's marker.
17+ * Real `page.mouse.click` is used throughout — synthetic dispatchEvent
18+ * on a marker bypasses the foreignObject hover overlay and would
19+ * silently mask the bug.
1720 */
1821
1922import { test , expect } from '../fixtures/base.js'
2023
21- test ( 'Shared tooltip — markerClick reports the clicked series' , async ( {
22- page,
23- loadChart,
24- } ) => {
24+ async function loadDemo ( page , loadChart ) {
2525 await loadChart ( 'line' , 'shared-tooltip-click' )
2626 await page . evaluate ( ( ) => {
2727 window . __events = [ ]
@@ -42,23 +42,75 @@ test('Shared tooltip — markerClick reports the clicked series', async ({
4242 ( ) => window . chart . w . globals . animationEnded === true ,
4343 { timeout : 5_000 } ,
4444 )
45+ }
4546
46- // Use point 4 (May): 49, 70, 33, 55, 57 — values are spread enough that
47- // each series's marker is unambiguously hit by clicking its own center.
48- for ( let s = 0 ; s < 5 ; s ++ ) {
49- const { x, y } = await page . evaluate ( ( si ) => {
50- const m = document . querySelector (
51- `.apexcharts-marker[index="${ si } "][rel="4"]` ,
52- )
53- const r = m . getBoundingClientRect ( )
54- return { x : r . left + r . width / 2 , y : r . top + r . height / 2 }
55- } , s )
56- await page . evaluate ( ( ) => ( window . __events = [ ] ) )
57- await page . mouse . click ( x , y )
58- await page . waitForTimeout ( 80 )
59- const events = await page . evaluate ( ( ) => window . __events )
60- expect ( events [ 0 ] , `no markerClick fired for series ${ s } ` ) . toBeTruthy ( )
61- expect ( events [ 0 ] . seriesIndex ) . toBe ( s )
62- expect ( events [ 0 ] . dataPointIndex ) . toBe ( 4 )
63- }
47+ async function clickAt ( page , x , y ) {
48+ await page . evaluate ( ( ) => ( window . __events = [ ] ) )
49+ await page . mouse . click ( x , y )
50+ await page . waitForTimeout ( 80 )
51+ return page . evaluate ( ( ) => window . __events )
52+ }
53+
54+ test . describe ( 'Shared tooltip — click reports correct series' , ( ) => {
55+ test ( 'clicking each series marker reports that series index' , async ( {
56+ page,
57+ loadChart,
58+ } ) => {
59+ await loadDemo ( page , loadChart )
60+
61+ // Point 4 (May): 49, 70, 33, 55, 57 — values are spread enough that
62+ // each series's marker is unambiguously hit by clicking its own center.
63+ for ( let s = 0 ; s < 5 ; s ++ ) {
64+ const { x, y } = await page . evaluate ( ( si ) => {
65+ const m = document . querySelector (
66+ `.apexcharts-marker[index="${ si } "][rel="4"]` ,
67+ )
68+ const r = m . getBoundingClientRect ( )
69+ return { x : r . left + r . width / 2 , y : r . top + r . height / 2 }
70+ } , s )
71+ const events = await clickAt ( page , x , y )
72+ expect ( events [ 0 ] , `no markerClick fired for series ${ s } ` ) . toBeTruthy ( )
73+ expect ( events [ 0 ] . seriesIndex ) . toBe ( s )
74+ expect ( events [ 0 ] . dataPointIndex ) . toBe ( 4 )
75+ }
76+ } )
77+
78+ test ( 'clicking on a line between markers reports the clicked line' , async ( {
79+ page,
80+ loadChart,
81+ } ) => {
82+ await loadDemo ( page , loadChart )
83+ // Hide markers so the click coordinates fall on the line itself —
84+ // mirrors the original user report where clicks landed on the colored
85+ // line segment, not on a data point.
86+ await page . evaluate ( ( ) =>
87+ window . chart . updateOptions ( { markers : { size : 0 } } ) ,
88+ )
89+ await page . waitForFunction (
90+ ( ) => window . chart . w . globals . animationEnded === true ,
91+ { timeout : 5_000 } ,
92+ )
93+
94+ // Click midway between data points 3 and 4 of each series. Series 3
95+ // is the historically tricky one: its midpoint sits very close to
96+ // series 0's marker, so a marker-distance algorithm would
97+ // misidentify it as series 0.
98+ for ( let s = 0 ; s < 5 ; s ++ ) {
99+ const pos = await page . evaluate ( ( si ) => {
100+ const gl = window . chart . w . globals
101+ const svg = document . querySelector ( '.apexcharts-svg' )
102+ const rect = svg . getBoundingClientRect ( )
103+ const x =
104+ gl . translateX +
105+ ( gl . seriesXvalues [ si ] [ 3 ] + gl . seriesXvalues [ si ] [ 4 ] ) / 2
106+ const y =
107+ gl . translateY +
108+ ( gl . seriesYvalues [ si ] [ 3 ] + gl . seriesYvalues [ si ] [ 4 ] ) / 2
109+ return { x : rect . left + x , y : rect . top + y }
110+ } , s )
111+ const events = await clickAt ( page , pos . x , pos . y )
112+ expect ( events [ 0 ] , `no markerClick fired for line ${ s } ` ) . toBeTruthy ( )
113+ expect ( events [ 0 ] . seriesIndex ) . toBe ( s )
114+ }
115+ } )
64116} )
0 commit comments