Skip to content

feat(chart): dim non-hovered series rows in tooltip#447

Open
lkipke wants to merge 2 commits intocloudflare:mainfrom
lkipke:levi/add-dimming-to-chart-tooltip
Open

feat(chart): dim non-hovered series rows in tooltip#447
lkipke wants to merge 2 commits intocloudflare:mainfrom
lkipke:levi/add-dimming-to-chart-tooltip

Conversation

@lkipke
Copy link
Copy Markdown
Contributor

@lkipke lkipke commented Apr 24, 2026

When hovering a series on a bar or line chart, the tooltip now dims rows for other series to 50% opacity, matching the emphasis dimming that ECharts already applies to the chart elements themselves.

Screenshot 2026-04-24 at 1 43 00 PM Screenshot 2026-04-24 at 1 43 07 PM Screenshot 2026-04-24 at 1 43 16 PM
  • Reviews
    • bonk has reviewed the change
    • automated review not possible because:
  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows: Verified dimming works for both line and bar charts (see screenshots)
    • Additional testing not necessary because:

When hovering a series on a bar or line chart, the tooltip now dims
rows for other series to 50% opacity, matching the emphasis dimming
that ECharts already applies to the chart elements themselves.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 24, 2026

npm i https://pkg.pr.new/@cloudflare/kumo@447

commit: d49b1cd

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 24, 2026

Docs Preview

View docs preview

Commit: d49b1cd

Copy link
Copy Markdown
Collaborator

@mattrothenberg mattrothenberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, didn't mean to approve. Still reviewing

@mattrothenberg
Copy link
Copy Markdown
Collaborator

/bonk My other AI Agent wants to know: Why drive tooltip-row dimming from element-level mouseover/mouseout and a seriesName ref, when axis-trigger tooltips aren't element-activated. Does this silently no-op on line charts with showSymbol: false, flicker between adjacent bars of the same series, and read a ref from inside the formatter that ECharts has no way to keep in sync with tooltip re-renders? Would updateAxisPointer with series-index matching not be the correct signal here?

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented Apr 24, 2026

@mattrothenberg Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@mattrothenberg
Copy link
Copy Markdown
Collaborator

/bonk pls review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR adds tooltip row dimming when hovering chart series. The visual goal is good, but the implementation has a critical synchronization issue that will cause the dimming to not work reliably or at all.

Critical Issues

1. Stale Closure in Tooltip Formatter (Primary Issue)

The dangerousHtmlFormatter is created inside useMemo and captures hoveredSeriesRef.current in its closure. When mouseover/mouseout update the ref, the formatter function does not get re-created because refs don't trigger re-renders and the useMemo dependencies don't include the hovered state.

Result: The formatter always sees the initial null value, so dimming never actually works.

2. Event Mismatch with Axis-Triggered Tooltips

The tooltip uses trigger: 'axis', meaning tooltips appear on axis hover, not element hover. However, mouseover/mouseout events fire when hovering specific graphical elements.

Problems:

  • Line charts with showSymbol: false: No symbols to hover, so mouseover events may not fire for the tooltip to dim rows
  • Bar charts: Rapidly moving between adjacent bars of the same series can cause flicker (mouseout clears, then mouseover sets)
  • The tooltip renders before/without element mouseover, leaving rows undimmed during axis hover

3. Incomplete Incomplete-Series Config

The incomplete-series config adds triggerLineEvent: true but this doesn't work for bar series (bar charts don't have lines). The hover tracking won't work consistently across chart types.

Suggested Approach

Consider using updateAxisPointer event instead, which fires consistently for axis-triggered tooltips. Match the axis pointer's series index to dim the correct tooltip row:

handlers.updateAxisPointer = (params: any) => {
  // params.dataIndex gives the hovered data index
  // Compare with series data to determine which series is 'active'
  hoveredSeriesRef.current = findSeriesAtDataIndex(params.dataIndex);
};

Or use React state with a throttled update to ensure the formatter re-renders when hover changes.

Questions

  1. Have you verified this works on line charts with showSymbol: false?
  2. Does the dimming update when hovering between bars of different series?
  3. Would the emphasis.focus: 'series' state from ECharts be more reliable than tracking mouse events?

? `<div style="font-weight:600;margin-bottom:4px;">${echarts.format.encodeHTML(formatTimestamp(ts))}</div>`
: "";

const hovered = hoveredSeriesRef.current;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical issue: This ref read happens inside a useMemo closure. The formatter captures the initial null value and never updates because:

  1. Ref updates don't trigger re-renders
  2. The useMemo deps don't include hover state
  3. The formatter function is the same closure from initial render

Result: hovered is always null, so dimming never actually occurs.

To fix, you'd need to either:

  • Use React state + add it to the useMemo deps (with throttling to avoid perf issues)
  • Or trigger chart.setOption() manually when hover changes to force formatter re-evaluation

};
}

// Track which series is hovered so the tooltip formatter can dim
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: With trigger: 'axis' tooltips, these element-level events may not fire consistently:

  • Line charts: With showSymbol: false, no symbols exist to receive mouseover
  • Bar charts: Rapid movement between adjacent bars causes flicker (clear → set → clear)
  • Axis hover: Tooltip shows on axis hover without any element mouseover, leaving rows undimmed

Consider using updateAxisPointer event which fires consistently for axis-triggered tooltips:

showSymbol: false,
emphasis: { focus: "series" as const },
symbolSize: 14,
itemStyle: { opacity: 0 },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

triggerLineEvent: true only works for line series. For bar charts (type === 'bar'), this has no effect. The hover tracking won't work consistently across chart types.

For bar charts, you'd need a different approach - perhaps updateAxisPointer + matching the data index to the hovered bar's series.

type: "line",
// Render invisible symbols at each data point with a larger
// radius so they act as wider hover targets around the line.
showSymbol: false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With showSymbol: false, there are no symbols to receive mouseover events. Combined with triggerLineEvent: true, the line itself can receive events, but this requires the user to hover exactly on the line (1-2px wide).

Consider whether this provides enough hover area for a good user experience, or if symbolSize: 14 with invisible symbols would create better hover targets.

hovered != null && param.seriesName !== hovered;
const style = isDimmed ? ' style="opacity:0.5"' : "";

return `<div${style}>${param.marker} ${echarts.format.encodeHTML(param.seriesName)}: <strong>${formattedValue}</strong></div>`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing from <br/> to <div> wrapping changes the vertical spacing of tooltip rows. Is this intentional? It adds block-level layout which may affect spacing differently than the inline <br/> approach.

If this is just for styling, consider keeping <br/> and using CSS on the tooltip container instead, or verify the visual result matches the intended design.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented Apr 24, 2026

I've submitted a comprehensive review of PR #447. Here's a summary of the key findings:

Key Issues Identified

1. Stale Closure Bug (Critical)

The dangerousHtmlFormatter captures hoveredSeriesRef.current in its closure inside useMemo. Since ref updates don't trigger re-renders, the formatter always sees the initial null value, meaning the dimming never actually works.

2. Event Mismatch with Axis-Triggered Tooltips

The tooltip uses trigger: 'axis' but the implementation relies on mouseover/mouseout events which fire on graphical elements. This creates problems:

  • Line charts with showSymbol: false: No symbols to hover, so events don't fire
  • Bar charts: Flickering when moving between adjacent bars
  • Axis hover: Tooltip can show without any element mouseover

3. triggerLineEvent Doesn't Work for Bar Charts

This configuration only applies to line series, so hover tracking won't work consistently across chart types.

Suggested Solution

Use the updateAxisPointer event (which fires consistently for axis-triggered tooltips) instead of mouseover/mouseout, and either:

  • Use React state with throttling to ensure the formatter re-renders
  • Or manually trigger chart.setOption() when hover changes

The review has been posted with inline comments on the relevant lines.

github run

@lkipke lkipke force-pushed the levi/add-dimming-to-chart-tooltip branch from 8882aa0 to ed7246e Compare April 24, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants