Skip to content

feat(charts): name the service on release marker hover - #2895

Merged
teeohhem merged 1 commit into
mainfrom
tom/release-marker-hover
Aug 14, 2026
Merged

feat(charts): name the service on release marker hover#2895
teeohhem merged 1 commit into
mainfrom
tom/release-marker-hover

Conversation

@teeohhem

@teeohhem teeohhem commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Colour was the only thing tying a release marker to a service, and that lookup breaks down exactly when it matters. The legend caps at 4 entries (MAX_LEGEND_ITEMS), so on a chart with more series than that a marker can be tinted to match a line whose legend entry is hidden behind "+N more" — there is nothing on screen to resolve the colour against. Collapsed clusters were worse still: "6 releases" named nobody at all.

Hovering a marker now lists every release in its cluster with the service that shipped it, its version, and the time. Verified on a 6-service chart: all six named, no colour lookup needed.

Implementation notes for reviewers

Most of the work here is getting a hover target to actually receive the pointer inside a Recharts chart:

  • Z-index. The hit targets live in a ZIndexLayer above every other chart layer. Without that the series areas (zIndex 100) and the marker lines (400) receive the pointer first. The lines and labels are now pointer-transparent for the same reason: they are decoration, and the hit layer owns the interaction.
  • Scope. Targets are confined to the label headroom rather than the full plot height. Covering the plot made the series tooltip fire alongside this one, and the label is the natural thing to aim at anyway. Events are never stopped, so drag-to-zoom still works underneath — asserted explicitly while developing.
  • One target per cluster, spanning it, so the muted lines inside a cluster are covered by their anchor rather than intercepting it.
  • Portaled tooltip. A dashboard tile clips its overflow, so an absolutely-positioned tooltip is cut off at the tile edge; this matches the pattern the series tooltip already uses. Its anchor is measured from the hit band in the event handler, so nothing reads a ref during render.

Layout is shared between the rendered lines and the hit layer via layoutAnnotations, so the hover bands can't drift from the markers they belong to.

Stack: based on #2894 (release markers), which is based on #2893 (the source field). Review those first.

How to test on Vercel preview

Preview routes: /dashboards

Steps:

  1. Open a dashboard with a time chart over the Logs source.
  2. Append ?releaseMarkers=true to the URL.
  3. Hover a dashed vertical marker's label.
  4. Verify a tooltip appears naming the service and version for each release at that point.
  5. Drag horizontally across the plot and confirm zoom still works (a "Reset zoom" button appears).

Note: markers only render if the preview's demo data carries a version attribute. Step 5 holds regardless.

https://hyperdx-oss-git-tom-release-marker-hover-hyperdx.vercel.app/dashboards/50dd68a8a37a1420?granularity=auto&from=1786546044852&to=1786718844852&releaseMarkers=true

image

Compound Engineering
Claude Code

@changeset-bot

changeset-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5256985

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Minor
@hyperdx/api Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 14, 2026 6:42pm
hyperdx-storybook Ready Ready Preview Aug 14, 2026 6:42pm

Request Review

@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Hovering a release marker now opens a portaled tooltip that names every service and version represented by its annotation cluster.

  • Separates annotation layout from rendering so marker lines and hover targets share cluster geometry.
  • Adds a high-z-index SVG hit layer and a tooltip containing each cluster member.
  • Adds unit and end-to-end coverage for cluster membership and basic hover behavior.

Confidence Score: 4/5

The PR should not merge until active annotation hover state is cleared or reconciled when the underlying marker data or geometry changes.

The previously reported lifecycle defect remains: annotation layout can refresh or resize while hoveredAnnotation continues referencing and displaying the old cluster because only mouse-enter and mouse-leave events update that state.

Files Needing Attention: packages/app/src/HDXMultiSeriesTimeChart.tsx

Important Files Changed

Filename Overview
packages/app/src/HDXMultiSeriesTimeChart.tsx Integrates annotation hover state, tooltip rendering, and the Recharts hit layer, but does not reconcile an active hover when annotations or geometry change.
packages/app/src/components/charts/AnnotationHitLayer.tsx Adds clustered SVG hover targets and captures a viewport anchor when the pointer enters.
packages/app/src/components/charts/AnnotationTooltip.tsx Renders clustered release details through a fixed-position body portal.
packages/app/src/components/charts/chartAnnotations.tsx Exposes annotation layout and preserves cluster members for rendering and tooltip content.
packages/app/tests/e2e/features/release-markers.spec.ts Covers opening the marker tooltip and displaying its service and version.

Reviews (7): Last reviewed commit: "feat(charts): name the service on releas..." | Re-trigger Greptile

Comment thread packages/app/src/HDXMultiSeriesTimeChart.tsx
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Scope: PR #2895 — release-marker hover tooltip. 8 files, base e30ecc3b. Adds AnnotationHitLayer.tsx + AnnotationTooltip.tsx, splits chartAnnotations into layoutAnnotations (geometry + members) and getAnnotationElements (rendering), and wires a <Customized> hit layer + body-portaled tooltip into HDXMultiSeriesTimeChart.

Intent: Let a release marker name its service on hover, since colour is unresolvable once the legend overflows MAX_LEGEND_ITEMS and a collapsed "N releases" cluster names nobody.

No critical issues found. The geometry refactor is sound: layoutAnnotations correctly attaches members only to labelled anchors (muted members stay members-less and are excluded from hit targets by annotations.filter(a => a.members != null)), the rect-centring math (x = left - (width - (right-left))/2) reduces to left when the span already exceeds MIN_HIT_WIDTH_PX and centres a lone marker otherwise, and pointerEvents="none" on the lines/labels plus a non-propagation-stopping hit layer preserves drag-to-zoom. No P0/P1 issues. The items below are recommendations and nits.

🟡 P2 — recommended

  • packages/app/src/components/charts/AnnotationHitLayer.tsx:60 — the hit bands are positioned by manually applying useXAxisScale inside a ZIndexLayer, but no test asserts the resulting rects actually overlay the rendered ReferenceLine markers; the new e2e hovers the rect element directly (targets.first().hover()), so a coordinate-space or offset mismatch between the layer and the marker lines would still pass every test while leaving bands visually detached from their markers.
    • Fix: Add a test that asserts the hit band's x-range matches the marker's rendered x position, so a scale/offset regression is caught rather than masked by direct-element hovering.
  • packages/app/src/components/charts/AnnotationTooltip.tsx:1 — the two new interactive components have no unit coverage; only the pure layoutAnnotations geometry is unit-tested, leaving rect sizing, the finite-xs filter, the empty-xs early return, and the members fallback exercised solely by one happy-path e2e.
    • Fix: Add unit tests for AnnotationHitLayer band geometry (single marker, degenerate equal-x cluster, empty scale output) and for AnnotationTooltip row rendering.
🔵 P3 nitpicks (3)
  • packages/app/src/HDXMultiSeriesTimeChart.tsx:1449layoutAnnotations runs twice per render: once for the laidOutAnnotations memo and again inside getAnnotationElements (called by the annotationElements memo) with identical arguments; the geometry is recomputed, not shared as intended.
    • Fix: Compute the laid-out annotations once and pass them into a render helper, so the lines and the hit layer consume a single result.
  • packages/app/src/components/charts/AnnotationHitLayer.tsx:87hovered.point is captured from getBoundingClientRect at onMouseEnter and rendered as position: fixed; scrolling the dashboard or resizing/zooming the chart while the pointer stays on the band leaves the tooltip pinned at stale viewport coordinates.
    • Fix: Recompute the anchor on scroll/resize while hovered, or document the limitation if hover is expected to end on scroll.
  • packages/app/tests/e2e/features/release-markers.spec.ts:170 — the assertion checks getByText(RELEASE_SERVICE).last() and getByText(OLD_VERSION).last() visibility independently, so it can pass when the service name and version appear in unrelated places (legend, filter) rather than paired in the same tooltip row.
    • Fix: Scope the assertion to the tooltip container / release row so it verifies the service and version render together.

Reviewers (10): correctness, adversarial, testing, maintainability, performance, project-standards, kieran-typescript, julik-frontend-races, agent-native, learnings-researcher.

Testing gaps: No unit tests for AnnotationHitLayer/AnnotationTooltip geometry; e2e hovers the rect directly so it cannot detect band-vs-marker misalignment; the hover assertion does not verify service and version are paired within one tooltip row.

@teeohhem
teeohhem force-pushed the tom/release-marker-hover branch from d65c53c to b8b84ab Compare August 12, 2026 21:17
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 294 passed • 1 skipped • 1084s

Status Count
✅ Passed 294
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 259 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 4
  • Production lines changed: 259 (+ 95 in test files, excluded from tier calculation)
  • Branch: tom/release-marker-hover
  • Author: teeohhem

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@pulpdrew pulpdrew left a comment

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.

LGTM

Colour was the only thing tying a marker to a service, and that lookup
breaks down exactly when it matters. The legend caps at 4 entries, so on
a chart with more series than that a marker can be tinted to match a
line whose legend entry is hidden behind "+N more" - there is nothing on
screen to resolve the colour against. Collapsed clusters were worse
still: "6 releases" named nobody at all.

Hovering a marker now lists every release in its cluster with the
service that shipped it, its version, and the time.

Implementation notes:

- The hit targets live in a ZIndexLayer above every other chart layer.
  Without that the series areas (zIndex 100) and the marker lines (400)
  receive the pointer first. The lines and labels are now
  pointer-transparent for the same reason; they are decoration and the
  hit layer owns the interaction.
- Targets are confined to the label headroom rather than the full plot
  height. Covering the plot made the series tooltip fire alongside this
  one, and the label is the natural thing to aim at anyway. Events are
  never stopped, so drag-to-zoom still works underneath.
- One target per cluster, spanning it, so the muted lines inside a
  cluster are covered by their anchor rather than intercepting it.
- The tooltip is portaled and fixed-positioned, matching the series
  tooltip: a dashboard tile clips its overflow, so an absolutely
  positioned tooltip is cut off at the tile edge. Its anchor is measured
  from the hit band in the event handler, so nothing reads a ref during
  render.

Layout is shared between the rendered lines and the hit layer via
`layoutAnnotations`, so the hover bands can't drift from the markers
they belong to.
@teeohhem
teeohhem force-pushed the tom/release-marker-hover branch from b51f8ea to 5256985 Compare August 14, 2026 18:37
@teeohhem
teeohhem merged commit 72269ec into main Aug 14, 2026
28 of 49 checks passed
@teeohhem
teeohhem deleted the tom/release-marker-hover branch August 14, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants