Skip to content

feat(dashboards): background trend sparkline on number tiles#2489

Merged
kodiakhq[bot] merged 6 commits into
mainfrom
alex/HDX-1360-number-tile-background-chart
Jun 22, 2026
Merged

feat(dashboards): background trend sparkline on number tiles#2489
kodiakhq[bot] merged 6 commits into
mainfrom
alex/HDX-1360-number-tile-background-chart

Conversation

@alex-fedotyev

@alex-fedotyev alex-fedotyev commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Number tiles can now show a faint trend sparkline (line or area) behind the value, derived from a time-bucketed version of the tile's own query. This is the first in a short series building toward SLO / error-budget tiles; it covers builder number tiles. Raw SQL support and click-through drill-down follow in separate PRs.

Builds on #1360.

Summary

  • Add an optional backgroundChart to the number-tile config: a line or area sparkline drawn behind the value, derived from a time-bucketed version of the tile's own query.
  • The sparkline color inherits the tile color by default and can be overridden to any palette token.

Changes

  • common-utils: BackgroundChartSchema ({ type, color? }) on the shared chart settings, mirroring the existing color / colorRules placement so it flows through the saved-config types unchanged.
  • app: NumberTileBackgroundChart renders the sparkline (recharts area / line) behind DBNumberChart. New BackgroundChartInput control in the display-settings drawer, wired through EditTimeChartForm. Display-only fields are stripped before the sparkline's query key, so visual edits do not refetch identical data.

Why

Number tiles are the natural KPI surface, and a trend behind the value is the common "stat with sparkline" pattern. Builder tiles can auto-derive the sparkline because the query is structured; raw SQL needs an explicit query, which is a follow-up.

Test plan

  • lint + typecheck (eslint, tsc --noEmit) on the touched packages
  • unit tests: common-utils schema round-trip and app render-wiring + the sparkline points helper
  • manual: builder number tile, enable Background chart (area / line); confirm the sparkline renders in light and dark

What's not in this PR (follow-ups)

  • Reference lines (they belong on full line / stacked-bar tiles, where they have real axes).
  • Raw SQL background-query mode (author the sparkline query directly).
  • Click-through drill-down on number tiles.
  • External API (v2 REST) + MCP parity for backgroundChart.
  • Customer docs.

Screenshots

Verified on a live build in both light and dark mode: the value renders in high-contrast theme text over a faint area sparkline. Builder number tile, count over a demo dataset.

Light

Number tile with background area sparkline, light mode

Dark

Number tile with background area sparkline, dark mode

[ui-states: allow]
[viewport: allow]
[no-story: allow]

Notes on the markers above: the sparkline is decorative behind the value and only mounts in the success branch, so the tile's empty / loading / error states are unchanged; it uses ResponsiveContainer, so it is size-independent; and the two new components render only with live time-series data, so they are demonstrated in product rather than in Storybook. The sparkline color resolves through theme tokens (getColorFromCSSToken), so it reflows across light and dark.

alex-fedotyev and others added 2 commits June 18, 2026 01:00
Number tiles can render a faint line or area sparkline behind the value,
derived from a time-bucketed version of the same query, so the trend over
the selected range is visible at a glance. Useful for SLO / error-budget
tiles where the burn over time matters as much as the current number.

- common-utils: BackgroundChartSchema (line / area + optional palette color
  override) on SharedChartSettingsSchema, mirroring color / colorRules.
- app: NumberTileBackgroundChart draws the sparkline behind DBNumberChart,
  reusing convertToTimeChartConfig + formatResponseForTimeChart for the
  bucketed series. Builder number tiles only; raw SQL has no time dimension
  to bucket.
- Display Settings: a Background chart control (type + optional color),
  gated on builder number tiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds an optional horizontal reference line to the background sparkline
(value plus optional label and palette color), so a number tile can mark a
0 error budget, an SLA, or a target. Rendered via a recharts ReferenceLine
with a hidden YAxis and ifOverflow="extendDomain" so it stays visible when
the value sits outside the data range.

- common-utils: backgroundChart.referenceLine { value, label?, color? }.
- app: NumberTileBackgroundChart draws the line on the area / line sparkline;
  BackgroundChartInput gains value / label / color controls.

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

changeset-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 273f8ed

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

This PR includes changesets to release 4 packages
Name Type
@hyperdx/app Minor
@hyperdx/common-utils 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 Jun 18, 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 Jun 22, 2026 11:03pm
hyperdx-storybook Ready Ready Preview, Comment Jun 22, 2026 11:03pm

Request Review

@greptile-apps

greptile-apps Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds an optional background trend sparkline (line or area) to number tiles, rendered via a new NumberTileBackgroundChart component using recharts behind the tile value. The sparkline derives its data from a time-bucketed version of the tile's own query, and a new BackgroundChartInput control in Display Settings lets users configure the shape and optional color override.

  • BackgroundChartSchema is added to SharedChartSettingsSchema in common-utils so the new field flows through the existing saved-config types cleanly, with proper Zod validation for the type enum and palette token.
  • NumberTileBackgroundChart correctly strips display-only fields (backgroundChart, color, colorRules, numberFormat) before computing the query key so visual-only edits don't trigger refetches, and applies Number.isFinite checks on both timestamp and value before pushing points to recharts.
  • The sparkline is guarded by isBuilderChartConfig and wrapped in an ErrorBoundary, keeping it from affecting the tile's loading/error/empty states.

Confidence Score: 4/5

Safe to merge once the groupBy stripping issue in the sparkline query is resolved.

The implementation is well-structured and the previously flagged refetch and NaN guard issues were fixed before this review. One remaining defect: timeConfig in NumberTileBackgroundChartInner retains any groupBy from the stored tile config, so when a tile was originally a grouped Line chart and was later switched to Number display, the sparkline fires a multi-series query and renders only the first group's trend — a different signal than the single aggregate value the tile shows. The rest of the change — schema additions, display-settings wiring, error boundaries, and test coverage — is solid.

packages/app/src/components/NumberTileBackgroundChart.tsx — the timeConfig memo should destructure and discard groupBy alongside the other display-only fields it already strips.

Important Files Changed

Filename Overview
packages/app/src/components/NumberTileBackgroundChart.tsx New sparkline component; display-only fields are correctly stripped from the query key, but groupBy is not stripped, so a tile with a residual group-by field renders the first group's trend instead of the aggregate trend that the number tile itself shows.
packages/common-utils/src/types.ts Adds BackgroundChartSchema (line/area enum + optional palette token) to SharedChartSettingsSchema; schema and type are clean and well-documented.
packages/app/src/components/BackgroundChartInput.tsx New UI control for selecting sparkline type and color; handles disabled state for SQL tiles correctly with accessible labels.
packages/app/src/components/DBNumberChart.tsx Mounts NumberTileBackgroundChart behind the value in a relative-positioned container when config.backgroundChart is set; z-index layering and pointer-events are correct.
packages/app/src/components/ChartDisplaySettingsDrawer.tsx Wires backgroundChart into the display settings form; shows the control for all number tiles and disables it for raw SQL configs, consistent with the existing pattern.
packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx Adds backgroundChart to the watched fields and propagates it through onDisplaySettingsApply; straightforward plumbing change.
packages/app/src/components/tests/DBNumberChart.test.tsx Adds mount/no-mount wiring tests for the sparkline; correctly mocks NumberTileBackgroundChart to isolate DBNumberChart behavior.
packages/app/src/components/tests/NumberTileBackgroundChart.test.tsx Unit-tests sparklinePointsFromGraphResults including non-finite timestamp and absent-value cases; coverage is thorough for the helper function.
packages/common-utils/src/tests/types.test.ts Adds schema round-trip tests for BackgroundChartSchema; positive and negative cases are comprehensive.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant DB as DBNumberChart
    participant BG as NumberTileBackgroundChart
    participant TQ as TanStack Query
    participant API as ClickHouse API

    DB->>TQ: useQueriedChartConfig(numberConfig)
    TQ->>API: single-value query (groupBy stripped)
    API-->>TQ: "{ data: [{value: 42}] }"
    TQ-->>DB: "formattedValue = 42"

    DB->>BG: mount if config.backgroundChart set
    BG->>BG: strip backgroundChart/color/colorRules/numberFormat
    BG->>TQ: useQueriedChartConfig(timeConfig)
    TQ->>API: time-bucketed query (groupBy NOT stripped)
    API-->>TQ: "[{ts:100,y:5}, {ts:200,y:8}, ...]"
    TQ-->>BG: graphResults to SparklinePoint[]

    BG->>BG: sparklinePointsFromGraphResults()
    BG->>BG: render AreaChart or LineChart behind value
    DB->>DB: render SafeAutoSizeNumber on top
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"}}}%%
sequenceDiagram
    participant DB as DBNumberChart
    participant BG as NumberTileBackgroundChart
    participant TQ as TanStack Query
    participant API as ClickHouse API

    DB->>TQ: useQueriedChartConfig(numberConfig)
    TQ->>API: single-value query (groupBy stripped)
    API-->>TQ: "{ data: [{value: 42}] }"
    TQ-->>DB: "formattedValue = 42"

    DB->>BG: mount if config.backgroundChart set
    BG->>BG: strip backgroundChart/color/colorRules/numberFormat
    BG->>TQ: useQueriedChartConfig(timeConfig)
    TQ->>API: time-bucketed query (groupBy NOT stripped)
    API-->>TQ: "[{ts:100,y:5}, {ts:200,y:8}, ...]"
    TQ-->>BG: graphResults to SparklinePoint[]

    BG->>BG: sparklinePointsFromGraphResults()
    BG->>BG: render AreaChart or LineChart behind value
    DB->>DB: render SafeAutoSizeNumber on top
Loading

Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

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

Comment thread packages/app/src/components/NumberTileBackgroundChart.tsx
Comment thread packages/app/src/components/NumberTileBackgroundChart.tsx Outdated
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 218 passed • 3 skipped • 1477s

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

Tests ran across 4 shards in parallel.

View full report →

…isual edits

Addresses code review on NumberTileBackgroundChart:
- Strip display-only fields (backgroundChart, color, colorRules, numberFormat)
  before building the sparkline's time-series query, so they no longer enter
  the TanStack query key and refetch identical data on a purely visual edit
  (sparkline type, reference line, tile color, number format).
- Guard the bucket timestamp with Number.isFinite as well, mirroring the value
  check, so a degenerate timestamp cannot reach recharts.

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

github-actions Bot commented Jun 19, 2026

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: 376 production lines changed (Tier 2 max: < 250)
  • Cross-layer change: touches frontend (packages/app) + shared utils (packages/common-utils)

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

Stats
  • Production files changed: 6
  • Production lines changed: 376 (+ 142 in test files, excluded from tier calculation)
  • Branch: alex/HDX-1360-number-tile-background-chart
  • Author: alex-fedotyev

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

@alex-fedotyev alex-fedotyev self-assigned this Jun 19, 2026
Per review, a reference line on the axis-less background sparkline reads as
confusing: there is no visible scale to anchor it, and it blurs against the
value and the conditional color rules. Reference lines belong on full line /
stacked-bar tiles, where they have real axes, and will ship in that slice
instead. This keeps the number-tile feature to just the background sparkline.

Removes backgroundChart.referenceLine from the schema, the recharts
ReferenceLine + hidden YAxis from the renderer, and the reference-line
controls from the editor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alex-fedotyev alex-fedotyev changed the title feat(dashboards): background trend sparkline + reference line on number tiles feat(dashboards): background trend sparkline on number tiles Jun 19, 2026
</>
)}

{showBackgroundChart && (

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.

ux: maybe if it's a sql chart we can just show a disabled state with a message instead of disappearing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. Done in 4fa65c9. On a SQL number tile the control now stays visible but disabled, with a hint ("Available on query-builder number tiles"), instead of disappearing. Keeps it discoverable, and the slot is already in place for when the SQL background query lands.

* dimension to bucket) and wraps the renderer in an error boundary so a
* sparkline failure never blanks the tile's value.
*/
export default function NumberTileBackgroundChart({

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.

any reason we couldn't reuse/extend the usual time chart instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good question. The shared part is actually already reused: this runs the same data pipeline as the time chart (convertToTimeChartConfig, useQueriedChartConfig, formatResponseForTimeChart, useTimeChartSettings, shouldFillNullsWithZero), so the only new code is the ~40-line render shell.

I kept that shell separate because MemoChart is close to the opposite of a sparkline and has no knobs to slim it down: it always renders CartesianGrid + XAxis + YAxis + Tooltip plus the full mouse-interaction, click-to-drilldown, and hover/series-selection machinery, with showLegend as the only off switch. A background sparkline wants none of that (no axes/grid/tooltip/legend, non-interactive, low-opacity, sitting behind the value), so reusing it would mean bolting a minimal mode onto the component that renders every line/area/stacked-bar tile, which is more code and more blast radius than the small dedicated renderer.

It also matches what we already do for small specialized charts: DBRowTable's pattern-trend column, DBHistogramChart, and DBPieChart render their own recharts directly and share the data layer rather than going through MemoChart.

If you'd rather consolidate, I think the cleaner form is a small shared <Sparkline> primitive (which DBRowTable's trend could adopt too) rather than a MemoChart mode, and I'd be glad to do that as a follow-up once the SQL and drill-down PRs show how much shared surface there will be. Happy to take that route now if you would prefer.

…led on SQL tiles

On a raw SQL number tile the Background chart control was hidden entirely.
Show it disabled with a hint ("Available on query-builder number tiles")
instead, so the option stays discoverable and the slot is already in place
for when SQL background queries land. Builder number tiles are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment on lines +88 to +101
const timeConfig = useMemo<ChartConfigWithDateRange>(() => {
const {
backgroundChart: _backgroundChart,
color: _color,
colorRules: _colorRules,
numberFormat: _numberFormat,
...rest
} = config;
return {
...rest,
displayType: DisplayType.Line,
granularity: config.granularity ?? 'auto',
};
}, [config]);

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.

P1 Residual groupBy causes sparkline/value mismatch

convertToNumberChartConfig strips groupBy before sending the number tile's own query, collapsing the result to one aggregate value. But timeConfig spreads ...rest which retains any groupBy present in the stored config (common when a tile started as a Line chart with a group-by and was later switched to Number display). The sparkline query would then return multiple series, and only lineData[0]?.dataKey is used — so the trend shown represents just the first group, not the aggregate that the number tile is displaying. Adding groupBy: _groupBy to the same destructure/strip block that already handles backgroundChart, color, colorRules, and numberFormat would keep the sparkline and value in sync.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@kodiakhq kodiakhq Bot merged commit f40cf68 into main Jun 22, 2026
21 checks passed
@kodiakhq kodiakhq Bot deleted the alex/HDX-1360-number-tile-background-chart branch June 22, 2026 23:10
kodiakhq Bot pushed a commit that referenced this pull request Jun 24, 2026
… apply (#2507)

Fix a number tile losing its auto-detected format when Display Settings is applied.

On a builder number tile that shows p95 of a trace `Duration` column, the value auto-detects a duration format (for example `367.7ms`). Opening **Display Settings** and clicking **Apply** without touching the format flipped it to plain Number, so the value then rendered as a raw nanosecond integer. This was a latent bug; #2489 surfaced it by adding the background-chart control, which is a new reason to open that drawer on a number tile.

## Summary

Two things were wrong, both fixed here:

- The drawer's auto-detected fallback (`defaultNumberFormat`) read the form's `select` field. `select` is only synced from `series` on submit and on display-type / source resets, so after the user edits the aggregation in the builder it goes stale, `getFirstSeriesNumberFormat` resolves `undefined`, and the drawer falls through to the Number default. It now reads the live `series` (the field the builder actually edits), so the drawer reflects the datasource format.
- **Apply** unconditionally wrote `numberFormat` into the config, freezing whatever the drawer happened to show (an inferred value the user never chose). It now persists `numberFormat` only on an explicit override: either the tile already had a saved format, or the user changed the format control in this session. Otherwise it stays unset and render-time auto-detection keeps driving the format.

This matches the contract render already uses (`useSingleSeriesNumberFormat`): an explicit `numberFormat` wins, otherwise the datasource-derived format applies.

## Changes

- `EditTimeChartForm.tsx`: source `autoDetectedNumberFormat` from the live `series` watch instead of the stale `select`, and persist the drawer's `numberFormat` only when it is defined.
- `ChartDisplaySettingsDrawer.tsx`: on Apply, emit `numberFormat` only when it is an explicit override (an existing saved format, or a field the user changed this session), otherwise emit `undefined`.

## Test plan

- [x] `make ci-lint` (eslint + tsc + stylelint, app package)
- [x] `make ci-unit` (app package, 2081 passed)
- [x] New drawer tests in `ChartDisplaySettingsDrawer.test.tsx`:
  - Apply without touching the format emits `numberFormat: undefined` (does not clobber the auto-detected format).
  - Changing the output format and applying persists the chosen format.
  - Editing only another setting (color) preserves an existing explicit format.

### UI verification

This is a persistence-logic fix with no styling or layout change. The visible effect (the tile value staying duration-formatted, and the drawer showing Duration rather than Number) depends on a trace source with a `Duration` column and seeded data, which the fresh local dev stack does not have. The behavior is covered by the new drawer tests, which drive the real Apply pipeline and assert exactly what gets persisted; the render path that formats the value is unchanged. [ui-check: allow]

### What's not in this PR (follow-up)

Switching the output format to Duration or Time still keeps the previous factor (for example Seconds), so a nanosecond value can be misread as seconds. A follow-up will seed the factor from the source's duration precision when the output switches to a time-based format. It stacks on this change.
kodiakhq Bot pushed a commit that referenced this pull request Jun 24, 2026
…splayed value (#2501)

Follow-up to #2489. The background trend sparkline that #2489 added to number tiles can plot the wrong data when a tile carries a leftover `groupBy`.

## Summary

The big number on a number tile is a single aggregate: `convertToNumberChartConfig` strips `groupBy` from its query. The background sparkline builds its own query from the tile config but kept `groupBy`, so a tile that still carried one queried multiple series and the renderer plotted only the first. The faint trend behind the value then belonged to a single group while the value itself aggregated every group.

A residual `groupBy` is reachable in normal use: switching a grouped Line chart to the Number display type only hides the group-by input, it does not clear the value, so a saved Number tile can still carry it.

The fix extracts the sparkline's query derivation into a small pure helper, `buildSparklineTimeConfig`, and drops `groupBy` there, mirroring the value query's strip. `granularity` is still kept (defaulting to `auto`) so the trend stays bucketed. There is no visual or layout change: the sparkline renders exactly as before, it just plots the same single series as the value it sits behind.

## Changes

- Extract `buildSparklineTimeConfig(config)` from the inline `useMemo` in `NumberTileBackgroundChart` and strip `groupBy` alongside the existing display-only fields.
- Add a render test that asserts the issued query has no `groupBy`, plus unit tests for the helper.
- Patch changeset.

## Test plan

- [x] `nx run @hyperdx/app:ci:lint` (lint + tsc)
- [x] `nx run @hyperdx/app:ci:unit` (2069 passed, 0 failures)
- [x] New render test mounts `NumberTileBackgroundChart` with a tile that has a residual `groupBy` and asserts the config passed to `useQueriedChartConfig` drops it, exercising the real `buildSparklineTimeConfig` then `convertToTimeChartConfig` path rather than the helper in isolation.

[ui-check: allow] Data-correctness fix: it changes which series the sparkline queries, not how anything is drawn. There is no visual, layout, color, or state surface to capture, and the new render test proves the issued query is correct.
[viewport: allow] No layout change.
kodiakhq Bot pushed a commit that referenced this pull request Jun 25, 2026
…hboards API (#2509)

Add `backgroundChart` (the number-tile background trend sparkline) to the external dashboards API, so a tile authored in the editor round-trips through `/api/v2/dashboards`.

Builds on #2489, which added the sparkline to the dashboard editor but left the v2 REST surface unaware of the field. Follows the same pattern as the number-tile `color` parity in #2428.

Part of #1360.

## Summary

Builder number tiles can now carry an optional `backgroundChart` over the v2 REST API. The field mirrors the internal `BackgroundChartSchema` (imported from `common-utils`, not re-declared, so the surfaces cannot drift): a required `type` (`line` or `area`) and an optional palette-token `color` override. Raw SQL number tiles do not expose it, matching the editor and the save path.

## What

- Add `backgroundChart: BackgroundChartSchema.optional()` to `externalDashboardNumberChartConfigSchema` (`packages/api/src/utils/zod.ts`).
- Carry `backgroundChart` through both conversion directions for the builder number arm in `packages/api/src/routers/external-api/v2/utils/dashboards.ts`.
- Add a `BackgroundChart` OpenAPI component and a `backgroundChart` property on `NumberBuilderChartConfig`; regenerate `openapi.json`.
- Tests: positive round-trips (line; area + color; PUT), rejection rules (type outside `line`/`area`, missing `type`, non-palette and legacy-numeric color), the raw-SQL strip case, and the no-sparkline backward-compat case.

## Why builder-only

The sparkline derives from a time-bucketed version of the tile's structured query, so it only makes sense for builder number tiles. The editor reflects this: the control renders for number tiles but is disabled when `configType === 'sql'` (`ChartDisplaySettingsDrawer`), and `convertFormStateToSavedChartConfig` persists `backgroundChart` only on the builder branch (the raw SQL and promql picks omit it, exactly like `colorRules`). The external schema mirrors that: `backgroundChart` lives on the builder number schema only. A raw SQL number tile sent with the field has it stripped on save, the same way `colorRules` is.

No legacy-token normalization is needed on the way out: `backgroundChart` shipped after the palette hue rename, so its optional `color` can only hold a hue-named token (unlike the static `color`, which still maps legacy `chart-1..chart-10` tokens from older documents).

## Backward compatibility

`backgroundChart` is optional everywhere. Existing dashboards and payloads without it are unchanged; a number tile with no sparkline round-trips with the field absent.

## Test plan

- [x] `make ci-lint` (eslint + tsc + openapi spectral)
- [x] integration: `external-api/__tests__/dashboards.test.ts` (round-trip + rejection + strip + backward-compat)
- [x] `openapi.json` regenerated via `yarn workspace @hyperdx/api docgen`

### What's NOT in this PR (follow-up)

- MCP authoring parity for `backgroundChart` (follows in a separate PR, mirroring the number-tile color MCP work).
- Customer docs for the sparkline option (tracked separately).
- Raw SQL background-query mode and the reference line are out of scope here (separate work on the editor side).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants