feat(dashboards): background trend sparkline + reference line on number tiles#2489
feat(dashboards): background trend sparkline + reference line on number tiles#2489alex-fedotyev wants to merge 2 commits into
Conversation
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 detectedLatest commit: fcb80bd The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds an optional background trend sparkline (line or area) and reference line to number tiles, derived from a time-bucketed version of the tile's own query. The feature is scoped to builder number tiles and is gated in the UI on
Confidence Score: 4/5Safe to merge; all changes are additive and behind an explicit opt-in config field. The implementation is well-structured and the error boundary ensures sparkline failures can never affect the tile's primary value display. The main issue is that packages/app/src/components/NumberTileBackgroundChart.tsx — the query key construction and the finite-check asymmetry are both in this file. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant DrawerForm as ChartDisplaySettingsDrawer
participant BGInput as BackgroundChartInput
participant DBNumber as DBNumberChart
participant BGChart as NumberTileBackgroundChart
participant Query as useQueriedChartConfig
User->>DrawerForm: Opens Display Settings
DrawerForm->>BGInput: Renders BackgroundChartInput
User->>BGInput: Selects "Area" type
BGInput->>DrawerForm: "onChange({ type: 'area' })"
DrawerForm->>DBNumber: "config.backgroundChart = { type: 'area' }"
DBNumber->>BGChart: Mount with config + backgroundChart
BGChart->>BGChart: isBuilderChartConfig check
BGChart->>BGChart: "Build timeConfig (displayType=Line)"
BGChart->>Query: useQueriedChartConfig(queriedConfig)
Query-->>BGChart: time-series data
BGChart->>BGChart: formatResponseForTimeChart → sparklinePoints
BGChart-->>DBNumber: "Renders recharts AreaChart (absolute, z=0)"
DBNumber-->>User: "Number value (z=1) over sparkline"
%%{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 User
participant DrawerForm as ChartDisplaySettingsDrawer
participant BGInput as BackgroundChartInput
participant DBNumber as DBNumberChart
participant BGChart as NumberTileBackgroundChart
participant Query as useQueriedChartConfig
User->>DrawerForm: Opens Display Settings
DrawerForm->>BGInput: Renders BackgroundChartInput
User->>BGInput: Selects "Area" type
BGInput->>DrawerForm: "onChange({ type: 'area' })"
DrawerForm->>DBNumber: "config.backgroundChart = { type: 'area' }"
DBNumber->>BGChart: Mount with config + backgroundChart
BGChart->>BGChart: isBuilderChartConfig check
BGChart->>BGChart: "Build timeConfig (displayType=Line)"
BGChart->>Query: useQueriedChartConfig(queriedConfig)
Query-->>BGChart: time-series data
BGChart->>BGChart: formatResponseForTimeChart → sparklinePoints
BGChart-->>DBNumber: "Renders recharts AreaChart (absolute, z=0)"
DBNumber-->>User: "Number value (z=1) over sparkline"
Reviews (1): Last reviewed commit: "feat(dashboards): add a reference line t..." | Re-trigger Greptile |
| const queriedConfig = useMemo( | ||
| () => convertToTimeChartConfig(timeConfig), | ||
| [timeConfig], | ||
| ); | ||
|
|
||
| const { data } = useQueriedChartConfig(queriedConfig, { | ||
| placeholderData: prev => prev, | ||
| queryKey: ['number-tile-background', queriedConfig], |
There was a problem hiding this comment.
backgroundChart bleeds into the sparkline query key, causing spurious refetches
timeConfig is built by spreading ...config, so it carries backgroundChart. convertToTimeChartConfig then spreads ...config again (line 142 of ChartUtils.tsx), so queriedConfig — and therefore the TanStack Query key — retains backgroundChart. Any purely visual change (switching type from line to area, editing the reference-line label or color) will produce a new queriedConfig object, bust the cache, and fire a fresh network request even though the underlying time-series data is identical.
The fix is to strip backgroundChart (and other display-only fields) before computing queriedConfig. The simplest approach is to destructure it out when constructing timeConfig, since the rendering code already receives backgroundChart as a separate prop and never reads it from config inside this component.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| for (const row of graphResults) { | ||
| const x = row[timestampKey]; | ||
| const y = row[valueKey]; | ||
| if (typeof x === 'number' && typeof y === 'number' && Number.isFinite(y)) { |
There was a problem hiding this comment.
The finite check on
x is weaker than the one on y. typeof x === 'number' passes for NaN and ±Infinity, both of which are valid typeof 'number' values. Applying Number.isFinite(x) keeps the guard symmetrical and prevents a degenerate timestamp (e.g. a malformed bucket edge) from being pushed into the points array and handed to recharts.
| if (typeof x === 'number' && typeof y === 'number' && Number.isFinite(y)) { | |
| if (typeof x === 'number' && Number.isFinite(x) && typeof y === 'number' && Number.isFinite(y)) { |
E2E Test Results✅ All tests passed • 202 passed • 3 skipped • 1358s
Tests ran across 4 shards in parallel. |
Number tiles can now show a faint trend sparkline behind the value, with an optional reference line to mark a threshold such as a zero error budget, an SLA, or a target. 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
backgroundChartto the number-tile config: alineorareasparkline drawn behind the value, derived from a time-bucketed version of the tile's own query.referenceLine(a value plus optional label and palette color) rendered across the sparkline, for example a zero-budget line or an SLA.Changes
common-utils:BackgroundChartSchema({ type, color?, referenceLine? }) on the shared chart settings, mirroring the existingcolor/colorRulesplacement so it flows through the saved-config types unchanged.app:NumberTileBackgroundChartrenders the sparkline (recharts area / line) and the reference line (rechartsReferenceLinewith a hiddenYAxisandifOverflow="extendDomain", so the line stays visible when its value sits outside the data range) behindDBNumberChart. NewBackgroundChartInputcontrol in the display-settings drawer, wired throughEditTimeChartForm.Why
Number tiles are the natural KPI surface, and a trend behind the value plus a threshold line is the common "stat with sparkline" pattern used to build SLO dashboards. 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
eslint,tsc --noEmit) on the touched packagescommon-utilsschema round-trip (line / area / color / reference line, positive per input + negative per rule) andapprender-wiring + the sparkline points helperWhat's not in this PR (follow-ups)
backgroundChart.Screenshots
Draft: visual verification is in progress. The sparkline rendering is confirmed on a local stack; full before/after captures (light + dark, builder tile with a reference line) will be attached before I mark this ready for review.
[ui-check: light-only]
[ui-states: allow]
[viewport: allow]
[no-story: allow]
Notes on the markers above: the sparkline color resolves through theme tokens (
getColorFromCSSToken) so it reflows in dark; 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 usesResponsiveContainer, 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.