Skip to content

refactor(dashboards): extract shared Sparkline primitive from number tile#2520

Merged
kodiakhq[bot] merged 3 commits into
mainfrom
alex/HDX-1360-shared-sparkline
Jul 9, 2026
Merged

refactor(dashboards): extract shared Sparkline primitive from number tile#2520
kodiakhq[bot] merged 3 commits into
mainfrom
alex/HDX-1360-shared-sparkline

Conversation

@alex-fedotyev

Copy link
Copy Markdown
Contributor

Builds on the number-tile background sparkline (#2489, #2501) and sets up the table-cell trend follow-up tracked in HDX-4604.

Summary

Extracts the chrome-less recharts core out of NumberTileBackgroundChart into a reusable <Sparkline> component (line / area / bar) and renders the number tile's background trend through it. This is a pure consolidation: the number tile issues the same query and draws the same faint line / area behind the value, so there is no user-visible change.

The table-cell trend feature (HDX-4604) needs the identical render shell in every numeric cell. A shared primitive is the alternative to standing up a second chart component next to this one, which is also the reuse question Mike raised on #2489 ("any reason we couldn't reuse/extend the usual time chart instead?").

What

  • New packages/app/src/components/Sparkline.tsx: <Sparkline points type={'line' | 'area' | 'bar'} color height />. Chrome-less (no axes, grid, legend, or tooltip; dots and animation off), sized to fill its parent via ResponsiveContainer (height defaults to 100%). Renders nothing for fewer than two points. The line and area branches are lifted from NumberTileBackgroundChart unchanged; the bar branch follows the existing PatternTrendChart in DBRowTable and is here for the table-cell consumer that lands next.
  • NumberTileBackgroundChart.tsx now renders <Sparkline points={points} type={backgroundChart.type} color={color} /> inside its unchanged aria-hidden wrapper. Its data path (buildSparklineTimeConfig, convertToTimeChartConfig, useQueriedChartConfig, formatResponseForTimeChart) and the single-series sparklinePointsFromGraphResults helper are unchanged.

Why the helper stays put

sparklinePointsFromGraphResults is single-series and specific to the number tile's data path. The table render reconstructs per-group trends differently, so the helper would not be shared; it stays with the number tile. Only the genuinely shared pieces (the component and the SparklinePoint type) live in Sparkline.tsx. This keeps the existing NumberTileBackgroundChart tests in place and avoids a dead re-export.

No behavior change

The render is a verbatim lift, so the recharts output is identical. The diff on NumberTileBackgroundChart.tsx shows the same <Area> / <Line> props (stroke, strokeOpacity 0.5, strokeWidth 2, fillOpacity 0.15, dataKey, margin) moving into <Sparkline>, wrapped in the same ResponsiveContainer inside the same wrapper. The number tile passes the same resolved color and type it computed before, and keeps its own "fewer than two points" guard so the background layer is still absent when there is no trend.

Test plan

  • nx run @hyperdx/app:ci:lint (eslint + tsc) clean
  • nx run @hyperdx/app:ci:unit: 2091 pass, 0 fail. Includes the existing number-tile render-wiring test and the DBNumberChart consumer test, both unchanged and green.
  • New Sparkline.test.tsx: mounts the component and asserts the line / area / bar recharts layers render in the given color, and that it renders nothing below two points.
  • Dev stack builds and serves the branch (app returns 200), confirming the refactor compiles and runs in the Next.js build, not only under jest.

Visual note: this is a verbatim render extraction with no visual delta, so the proof of "renders identically" is the render-block diff plus the unchanged consumer test rather than a before/after screenshot. [ui-check: allow]

What's not in this PR (follow-up, HDX-4604)

  • The table-tile "show trend sparklines" display toggle and its schema field.
  • The per-cell scoped query and grouped cell render in DBTableChart, which consumes <Sparkline> (lands after feat(dashboards): per-column color on table tiles #2517 so the per-column color drives the sparkline color).
  • External API v2 / MCP parity and customer docs for the toggle.

No changeset: internal refactor with no user-facing behavior change. [no-changeset: allow]

…tile

Lift the chrome-less recharts core out of NumberTileBackgroundChart into
a reusable <Sparkline> (line / area / bar) and render the number tile's
background trend through it. No user-visible change: the number tile
issues the same query and renders the same faint line / area behind the
value.

The upcoming table-cell trend feature needs the identical render shell
per cell, so a shared primitive is the alternative to a second parallel
chart component. It also answers the reuse question raised on #2489.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: de72b24

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel

vercel Bot commented Jun 25, 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, Comment Jul 9, 2026 1:49pm
hyperdx-storybook Ready Ready Preview, Comment Jul 9, 2026 1:49pm

Request Review

@github-actions github-actions Bot added the review/tier-2 Low risk — AI review + quick human skim label Jun 25, 2026
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

🔵 Tier 2 — Low Risk

Small, isolated change with no API route or data model modifications.

Why this tier:

  • Standard feature/fix — introduces new logic or modifies core functionality

Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns.
SLA: Resolve within 4 business hours.

Stats
  • Production files changed: 2
  • Production lines changed: 142 (+ 88 in test files, excluded from tier calculation)
  • Branch: alex/HDX-1360-shared-sparkline
  • Author: alex-fedotyev

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

@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extracts the recharts render core from NumberTileBackgroundChart into a new reusable <Sparkline> primitive (line / area / bar) without any user-visible behavior change. The refactor is straightforward: the three chart branches are lifted verbatim, wrapped in a ResponsiveContainer, and the number tile delegates to the new component while keeping its own query path and < 2 points guard unchanged.

  • Sparkline.tsx: New chrome-less component with its own < 2 points guard, height prop defaulting to '100%', and all animation/dot/tooltip chrome disabled — a clean primitive ready for the table-cell follow-up.
  • NumberTileBackgroundChart.tsx: Data path, error boundary, and aria-hidden wrapper are untouched; only the inline recharts JSX is replaced with <Sparkline>.
  • Sparkline.test.tsx: Six tests cover each variant, the under-two-points guard, default height, and forwarding an explicit numeric height.

Confidence Score: 5/5

Pure refactor with no user-visible change; the recharts render is a verbatim lift, the data path is untouched, and the error boundary and aria-hidden wrapper are preserved.

The same recharts props, constants, and guards move from one file to another. Tests cover all three chart variants and both height-forwarding scenarios.

No files require special attention.

Important Files Changed

Filename Overview
packages/app/src/components/Sparkline.tsx New chrome-less recharts primitive with line/area/bar branches; clean, well-commented, no issues found.
packages/app/src/components/NumberTileBackgroundChart.tsx Replaces inline recharts JSX with Sparkline delegation; data path, query logic, and error-boundary wrapper are untouched.
packages/app/src/components/tests/Sparkline.test.tsx Six tests covering all three variants, the under-two-points guard, default height, and explicit numeric height forwarding; all correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    NTBChart["NumberTileBackgroundChart"]
    NTBChartInner["NumberTileBackgroundChartInner"]
    Sparkline["Sparkline"]
    Line["LineChart"]
    Area["AreaChart"]
    Bar["BarChart"]
    NTBChart --> NTBChartInner
    NTBChartInner --> Sparkline
    Sparkline --> Line
    Sparkline --> Area
    Sparkline --> Bar
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    NTBChart["NumberTileBackgroundChart"]
    NTBChartInner["NumberTileBackgroundChartInner"]
    Sparkline["Sparkline"]
    Line["LineChart"]
    Area["AreaChart"]
    Bar["BarChart"]
    NTBChart --> NTBChartInner
    NTBChartInner --> Sparkline
    Sparkline --> Line
    Sparkline --> Area
    Sparkline --> Bar
Loading

Reviews (3): Last reviewed commit: "Merge branch 'main' into alex/HDX-1360-s..." | Re-trigger Greptile

Comment thread packages/app/src/components/Sparkline.tsx
Comment thread packages/app/src/components/__tests__/Sparkline.test.tsx Outdated
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 228 passed • 3 skipped • 1506s

Status Count
✅ Passed 228
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

The height test only checked that the chart still rendered; capture the
height passed to ResponsiveContainer and assert it (the 100% default and
an explicit numeric height), so the prop's contract is actually covered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found.

This is a behavior-preserving render extraction. The line/area recharts props (stroke, strokeOpacity 0.5, strokeWidth 2, fillOpacity 0.15, dot={false}, isAnimationActive={false}, margin) move into Sparkline unchanged, and the sole caller passes backgroundChart.type, which BackgroundChartSchema constrains to 'line' | 'area' — so no render path changes. No P0/P1 or P2 findings survived re-grading; all remaining items are low-impact nits.

🔵 P3 nitpicks (6)
  • packages/app/src/components/Sparkline.tsx:50 — the type === 'bar' branch (and the 'bar' member of the variant union) is unreachable from the only typed caller, so it ships as test-only code with no production consumer.
    • Fix: Defer the bar branch to the PR that actually renders bar sparklines, or confirm that consumer is imminent so the primitive lands with a real second caller.
    • maintainability, testing, correctness, kieran-typescript, project-standards
  • packages/app/src/components/__tests__/Sparkline.test.tsx:64 — the bar test asserts only that .recharts-bar renders and the color appears; the bar-specific maxBarSize={24} is unverified.
    • Fix: Assert the rendered bar width honors maxBarSize so the bar variant is pinned rather than smoke-tested.
    • testing
  • packages/app/src/components/__tests__/Sparkline.test.tsx:49innerHTML.toContain(COLOR) verifies the color string appears somewhere, not that it is applied to the correct stroke/fill attribute, so a stroke↔fill swap or dropped opacity would still pass.
    • Fix: Assert stroke on .recharts-curve for line/area and fill on the bar path directly.
    • testing
  • packages/app/src/components/Sparkline.tsx:73 — the variant ternary treats its final else as line, so a future union member would silently render as a line with no compiler error.
    • Fix: Branch explicitly on 'line' and add an assertNever(type) fallthrough (or a switch) so a dropped case fails typechecking.
    • kieran-typescript
  • packages/app/src/components/Sparkline.tsx:11SparklinePoint is exported but the accepted variant union is not, so consumers must inline string literals for the type prop.
    • Fix: Export the variant union (e.g. export type SparklineType = 'line' | 'area' | 'bar') alongside SparklinePoint.
    • kieran-typescript
  • packages/app/src/components/Sparkline.tsx:47 — the points.length < 2 guard and its bare literal 2 now live in both Sparkline and NumberTileBackgroundChartInner (NumberTileBackgroundChart.tsx:140) and could drift if the threshold changes.
    • Fix: Export a shared MIN_SPARKLINE_POINTS constant, or cross-reference the two guards in a comment.
    • maintainability, correctness

Reviewers (7): correctness, testing, maintainability, project-standards, kieran-typescript, agent-native, learnings-researcher.

Testing gaps:

  • The intentionally low-contrast styling constants (STROKE_OPACITY, STROKE_WIDTH, AREA_FILL_OPACITY) and CHART_MARGIN are unasserted, so a silent drop of any of them during a future refactor would not be caught.
  • The bar variant is unreachable from production today; its unit test is the only guard against drift until a real bar caller lands.

@kodiakhq kodiakhq Bot merged commit c22dd6b into main Jul 9, 2026
21 checks passed
@kodiakhq kodiakhq Bot deleted the alex/HDX-1360-shared-sparkline branch July 9, 2026 13:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-2 Low risk — AI review + quick human skim

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants