Skip to content

feat: Add per-series number formats#2174

Open
pulpdrew wants to merge 4 commits intomainfrom
drew/per-series-number-format
Open

feat: Add per-series number formats#2174
pulpdrew wants to merge 4 commits intomainfrom
drew/per-series-number-format

Conversation

@pulpdrew
Copy link
Copy Markdown
Contributor

@pulpdrew pulpdrew commented Apr 29, 2026

Summary

This PR adds per-series number formats (for chart-builder-based charts). This allows the user to specify a different number format for different table columns or series.

For series without formats defined, the existing config's format is applied (if applicable). If there's no chart-wide format, then we attempt to infer a duration type format for any series aggregating the Trace Duration field. (re-using the previous logic for inferring duration number formats).

This PR includes updates the external API to support the new per-series numberFormat field.

This PR also updates various charts on the services dashboard which previously displayed ms units for non-duration series. These charts now only show ms formatting for duration aggregations.

Screenshots or video

Screen.Recording.2026-04-29.at.10.07.45.AM.mov

Services Dashboard:

Screenshot 2026-04-29 at 10 09 31 AM Screenshot 2026-04-29 at 10 09 23 AM

How to test locally or on Vercel

Most of this can be tested in the preview environment in a dashboard or in the chart explorer. The external API updates can be tested locally by running CRUD requests against a dashboard, specifying per-series number formats.

References

  • Linear Issue: Closes HDX-3609
  • Related PRs:

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 29, 2026

🦋 Changeset detected

Latest commit: b4af62f

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

This PR includes changesets to release 4 packages
Name Type
@hyperdx/common-utils Minor
@hyperdx/api Minor
@hyperdx/app 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
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

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

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Apr 30, 2026 5:59am

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

E2E Test Results

All tests passed • 161 passed • 3 skipped • 1173s

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

Tests ran across 4 shards in parallel.

View full report →

@pulpdrew pulpdrew marked this pull request as ready for review April 29, 2026 01:31
@github-actions github-actions Bot added the review/tier-4 Critical — deep review + domain expert sign-off label Apr 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Critical-path files (2):
    • packages/api/src/routers/external-api/v2/dashboards.ts
    • packages/api/src/routers/external-api/v2/utils/dashboards.ts
  • Cross-layer change: touches frontend (packages/app) + backend (packages/api) + shared utils (packages/common-utils)

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 20
  • Production lines changed: 669 (+ 805 in test files, excluded from tier calculation)
  • Branch: drew/per-series-number-format
  • Author: pulpdrew

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

PR Review

  • ⚠️ Index-based series→column mapping is fragile (source.ts:useChartNumberFormats, lines ~1997–2009): The loop assumes config.select[i] maps to meta[i]. If groupBy columns appear in meta before value columns (or query engine reorders outputs), formats will be silently misassigned to wrong columns. The comment documents this assumption, but there's no guard. Consider verifying this assumption holds for all chart types that call this hook with meta (DBTableChart, DBListBarChart, DBTimeChart).

  • ⚠️ tooltipNumberFormatsByKey is now required on MemoChart (HDXMultiSeriesTimeChart.tsx:263): It changed from optional numberFormat?: NumberFormat to required tooltipNumberFormatsByKey: Map<string, NumberFormat>. The only current call site (DBTimeChart) passes it correctly, but this is a breaking internal API change that any future direct MemoChart usage would silently fail on at runtime if TypeScript isn't checked strictly.

  • ⚠️ UseFormSetValue type mismatch (NumberFormat.tsx:534, SeriesNumberFormatDrawer.tsx:1637): NumberFormatForm expects UseFormSetValue<ChartConfigDisplaySettings> but SeriesNumberFormatDrawer passes UseFormSetValue<{ numberFormat?: NumberFormat }> — a narrower form type. Works at runtime since only numberFormat is ever set, but this is a type unsafety that TypeScript may not catch depending on how contravariance is applied.

  • Bundled bug fix in ServiceDashboardEndpointPerformanceChart.tsx (line ~1716): "Calls per Request" was dividing by Average Duration instead of Number of Requests — this fix is correct and worth calling out explicitly in the PR description.

  • ✅ No security issues found. The external API schema change correctly uses the existing NumberFormatSchema with Zod validation, and the pick() calls in the API conversion utilities explicitly allowlist numberFormat, preventing unintended field passthrough.

Comment on lines +235 to +239
numberFormat ??
(Array.isArray(select)
? getFirstSeriesNumberFormat(select, tableSource)
: undefined),
[numberFormat, select, tableSource],
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.

Only auto-detect the number format if there is no existing chart-wide table format set

@pulpdrew pulpdrew force-pushed the drew/per-series-number-format branch from fd4f3c0 to 4113c42 Compare April 29, 2026 05:11
@pulpdrew pulpdrew requested review from a team and fleon and removed request for a team April 29, 2026 05:27
fleon
fleon previously approved these changes Apr 30, 2026
Comment thread .changeset/dirty-rings-report.md
await dashboardPage.createNewDashboard();
});

test('per-series format overrides chart-wide format and falls back when reset to inherit', async () => {
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.

praise: very well written feature specs 👍

question (non-blocking): do we really need a 60s timeout? can we shorten it?

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.

Reduced to 15 seconds. That's still probably pretty conservative, but the page can load pretty slowly in CI sometimes.


// --- chartFormat resolution ---

it('uses config.numberFormat as chartFormat when set', () => {
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.

nit (non-blocking): we can probably use parameterized tests to dry some of these tests up

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 idea, updated.

fleon
fleon previously approved these changes Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants