[HDX-1360] feat(app): number tile static color picker#2265
[HDX-1360] feat(app): number tile static color picker#2265alex-fedotyev wants to merge 6 commits into
Conversation
PR-1 of the issue #1360 series. The existing ColorSwatchInput (orphan in OSS since #440) used a free-form ColorInput plus a hand-picked 9-value hex array. Refactor it to a palette-only picker driven by the unified 13-token palette landed in #1627: ten categorical tokens (chart-1..chart-10) and three semantic tokens (chart-success, chart-warning, chart-error). Storing tokens (not hex) lets user color choices reflow across themes and color modes without losing contrast or breaking the WCAG ratios baked into the palette. The component remains internal in this PR. PRs 2b through 6b in the issue #1360 series wire it into ChartSeriesEditor, ChartDisplaySettingsDrawer, and the number-tile thresholds / reference-line editors. PR 7 mirrors the schema fields on the external dashboards API. PR 8 documents the feature. Highlights: - New ChartPaletteToken type + CHART_PALETTE_TOKENS, CATEGORICAL_PALETTE_TOKENS, SEMANTIC_PALETTE_TOKENS arrays, isChartPaletteToken type guard in utils.ts. - New getColorFromCSSToken(token) reads the matching --color-chart-* CSS variable, with SSR-safe fallbacks. - New resolveChartColor(token, fallbackIndex, level) for PR 2b to plug into setLineColors / formatResponseForPieChart. - ColorSwatchInput popover split into categorical / semantic sections; trigger uses --color-outline-focus for focus-visible; swatch buttons use aria-pressed; legacy non-token values are guarded to treat as unset. - Storybook stories matrix: six isolated stories (picker mechanics) and five in-context mocks (InSeriesRow, InLineChart, InStackedBar, InNumberTile, InReferenceLineEditor). The in-context stories are storybook-only mocks; they do not import the real editor components. - 13 RTL tests cover the rejection rules and keyboard nav.
🦋 Changeset detectedLatest commit: 1bf59d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 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.
|
resolveChartColor and ColorSwatchInputProps had no consumers on this branch and tripped the knip CI check. Drop resolveChartColor entirely (the next PR in the issue #1360 series adds it back alongside its first consumer in ChartUtils.setLineColors), and make ColorSwatchInputProps an internal type alias (consumers infer the prop shape from typeof ColorSwatchInput).
E2E Test Results✅ All tests passed • 192 passed • 3 skipped • 1252s
Tests ran across 4 shards in parallel. |
Wire the palette picker into a real product surface. The number tile's Display Settings drawer now exposes a "Color" section that stores a palette token on the tile config; the renderer resolves the token at paint time so the choice reflows correctly across light, dark, and IDE themes. Re-scope of PR #2265: - Move the palette tokens (CHART_PALETTE_TOKENS, ChartPaletteToken, CATEGORICAL_PALETTE_TOKENS, SEMANTIC_PALETTE_TOKENS, isChartPaletteToken) from packages/app/src/utils.ts to packages/common-utils/src/types.ts so the Zod schema and the app share a single source of truth. The theme-aware CSS resolver (getColorFromCSSToken) stays in app because it depends on getComputedStyle(document.documentElement). app/utils.ts re-exports the moved symbols so existing imports keep working unchanged. - Add ChartPaletteTokenSchema = z.enum(CHART_PALETTE_TOKENS) and color: ChartPaletteTokenSchema.optional() on SharedChartSettingsSchema. Mirrors the existing numberFormat: NumberFormatSchema.optional() pattern at the same level (also a number-only field stored at shared level and gated in the UI). - Add the Color section to ChartDisplaySettingsDrawer, gated on displayType === DisplayType.Number, wired via <Controller> around <ColorSwatchInput>. - Round-trip the color through EditTimeChartForm's displaySettings memo and handleUpdateDisplaySettings. - Apply config.color in DBNumberChart via Mantine <Text c={getColorFromCSSToken(token)}>. Unknown tokens (legacy values, schema drift) fall back to the default text color via isChartPaletteToken guard. - Trim the 5 in-context Storybook stories (InSeriesRow, InLineChart, InStackedBar, InNumberTile, InReferenceLineEditor, TokensReference). The picker now has a real product surface, so the mocks are redundant. The 6 isolated stories (Default, Selected, Disabled, WithCustomLabel, AllTokensSelected, KeyboardNav) stay because they exercise picker mechanics that the in-product surface only triggers one row at a time. Adds 3 RTL tests on DBNumberChart: positive (color resolved through getColorFromCSSToken), unset (no resolution), unknown token (renderer falls back gracefully). Schema parity for the external dashboards API (packages/api/src/utils/zod.ts + convertToExternal/InternalTileChartConfig + OpenAPI regen) and customer docs on docs/use/dashboards.md ship in follow-up PRs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…watch-input # Conflicts: # packages/app/src/components/DBNumberChart.tsx
🟡 Tier 3 — StandardIntroduces new logic, modifies core functionality, or touches areas with non-trivial risk. Why this tier:
Review process: Full human review — logic, architecture, edge cases. Stats
|
|
<!-- deep-review --> Deep Review✅ No critical issues found. The diff is tightly scoped, the palette-token store is additive-optional, the renderer is defensively guarded against legacy values, and 🟡 P2 -- recommended
🔵 P3 nitpicks (4)
Reviewers (5): ce-correctness-reviewer, ce-testing-reviewer, ce-maintainability-reviewer, ce-kieran-typescript-reviewer, ce-api-contract-reviewer. Testing gaps:
|
- Add getColorFromCSSToken unit tests covering the browser happy path,
empty CSS var fallback, getComputedStyle throw fallback, and SSR
fallback for all categorical indices (chart-1 through chart-10).
Tests note that jsdom prevents simulating window===undefined so the
throw branch covers the same paletteTokenSSRFallback code path.
- Add isChartPaletteToken tests for valid tokens, hex strings,
undefined/null/numbers, empty string, case-sensitive non-matches,
out-of-range indices, and lookalike non-tokens.
- Add ChartDisplaySettingsDrawer integration tests: assert the color
picker is present for displayType=Number and absent for Table; assert
onChange receives the selected token (categorical and semantic) when
Apply is clicked.
- Strengthen DBNumberChart color test: update mock to return a valid
hex (#00ff00) so Mantine applies it as an inline color style, then
assert toHaveStyle({color: 'rgb(0, 255, 0)'}) to lock in that the
resolved token reaches the <Text> DOM element.
|
P2 test coverage from deep review: added direct unit tests for |
Deep Review🔴 P0/P1 -- must fix
🟡 P2 -- recommended
🔵 P3 nitpicks (6)
Reviewers (7): correctness, testing, maintainability, project-standards, kieran-typescript, api-contract, adversarial. Testing gaps:
|
Summary
First PR in the issue #1360 series. Adds a "Color" section to the number tile's Display Settings drawer that stores a palette token (categorical
chart-1throughchart-10, or semanticchart-success/chart-warning/chart-error). The renderer resolves the token at paint time, so the choice reflows correctly across light, dark, and IDE themes without losing contrast.Re-scoped from the original component-only plan: the picker is now wired into a real product surface (number tile -> Display Settings drawer -> renderer), so reviewers can see it work end-to-end on the Vercel preview instead of through Storybook mocks.
What ships in this PR:
CHART_PALETTE_TOKENS,ChartPaletteToken,CATEGORICAL_PALETTE_TOKENS,SEMANTIC_PALETTE_TOKENS,isChartPaletteToken) move frompackages/app/src/utils.tstopackages/common-utils/src/types.tsso the Zod schema and the app share one source of truth. The theme-aware CSS resolvergetColorFromCSSTokenstays in app (it depends ongetComputedStyle(document.documentElement)).app/utils.tsre-exports the moved symbols so existing imports keep working.ChartPaletteTokenSchema = z.enum(CHART_PALETTE_TOKENS);SharedChartSettingsSchemagainscolor: ChartPaletteTokenSchema.optional(). Mirrors the existingnumberFormat: NumberFormatSchema.optional()pattern at the same level (also a Number-only field stored at shared level and gated in the UI).ColorSwatchInput(palette-only picker, 13 tokens, theme-aware, keyboard-accessible) is unchanged from the previous revision.ChartDisplaySettingsDrawergains a Color section, gated ondisplayType === DisplayType.Number, wired via<Controller>around<ColorSwatchInput>.EditTimeChartFormround-trips the field throughdisplaySettingsandhandleUpdateDisplaySettings.DBNumberChartappliesconfig.colorvia Mantine<Text c={getColorFromCSSToken(token)}>. Unknown tokens (legacy values, schema drift) fall back to the default text color via theisChartPaletteTokenguard.Default,Selected,Disabled,WithCustomLabel,AllTokensSelected,KeyboardNav); dropped the 5 in-context mocks since the picker now has a real product surface.Follow-ups
Filed before merging so the parity work isn't lost:
coloron the number-tile schema: [HDX-1360] External API parity for number-tile color #2359.Per-series color on line / bar / pie, threshold-driven color on number, reference lines, table per-column color, and the background-chart sparkline all ship in their own follow-up PRs.
Test plan
yarn workspace @hyperdx/app jestclean (1647 / 1656 pass, 9 pre-existing skips; 3 new tests onDBNumberChartcover positive, unset, and unknown-token paths).yarn workspace @hyperdx/common-utils jestclean (957 / 957).yarn workspace @hyperdx/app lintclean.yarn workspace @hyperdx/app tsc --noEmitclean.yarn workspace @hyperdx/common-utils lintclean.yarn workspace @hyperdx/app knip: no new findings; pre-existing flags unchanged.Resolves part of #1360 (AC3, partial AC6).