Skip to content

fix(ui): dashboard KPI values and chart labels stay readable on a phone - #28223

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-ty1d4m
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/project-thread-ty1d4m

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of #27520 — rows 3, 15 (the chart half) and 18 (the chart part). Every other row stays on the card.

Clause-②: yes (widening: @object-ui/core gains the public export formatMonthBucketLabel; no accept set moves)
Metadata-protocol: none

维护者速读

  • 改了什么:仪表盘在手机上的可读性。KPI 大数字放不下时自动缩小而不是被截断;按月分桶的图表轴显示 "Oct 2025" 而不是 "2025-10";柱图/面积图的图例补上色块,对比(compareTo)系列的色块画成空心以和主系列区分;手机上的类目标签不再被截成 "In Progr…"(横向柱图换行显示,竖向柱图旋转显示,仍被截断的标签悬停可见全名)。
  • 为什么改:18.3.0 RC 走查(console: suggestions from the 18.3.0 RC browser walkthrough: mention and discussion feedback, list selection and bulk results, refusal wording, approvals inbox, dashboards and money formatting, and phone layout #27520 第 3、15、18 行)在 390px 手机上看到 Total Budget 显示成 "$1,090,00...",CRM Pipeline 仪表盘的对比系列与主系列同色、阶段柱图图例没有色块、月份轴是原始键。
  • 风险与回滚:只改渲染层(三个 @object-ui 包),不碰 spec、元数据和数据;数据行里的月份键不变,下钻与排序不受影响。回滚即 revert 本 PR。
  • 先看:ui/packages/plugin-charts/src/AdvancedChartImpl.tsx(标签格式化、两个刻度组件、图例色块);ui/packages/plugin-dashboard/src/metricTile.ts(数据集 KPI 的字号阶梯);ui/packages/plugin-charts/src/ChartContainerImpl.tsx(图例色块)。
  • 两分钟验证:手机宽度(390px)打开 showcase 的 Delivery Operations 仪表盘,Total Budget 应完整显示 $1,090,000.00;打开 CRM 的 Pipeline Dashboard,趋势图 X 轴应为 "Oct 2025 … Oct 2026",图例里 "vs last year" 是空心色块。
  • 需要你做的:无,席位落地。

What changes

  • Row 3 — a dataset-bound KPI value shrinks before it clips. DatasetWidget's metric tile drew every value at the short face (20 px, 24 px from a 160 px tile), so $1,090,000.00 clipped in a phone's two-per-row tile. It now takes METRIC_TILE_DATASET_VALUE_LADDER in metricTile.ts: the same rungs as the plain tiles' ladder (objectui#12942), measured against the dataset tile's own box (no Card inset), never drawing 24 px under 160 px. Past the last rung the value keeps the ellipsis and carries the full figure as its tooltip. A short value keeps today's face and draws no tooltip. Compact notation ($1.09M) was not needed: shrink-to-fit holds the figure on a 390 px phone, and it would override the measure's declared format.
  • Row 15 — month axis. @object-ui/core gains formatMonthBucketLabel(value, locale) (utils/date-bucket-label.ts): a YYYY-MM bucket key reads Oct 2025 in the display locale, formatted in UTC so no zone moves it a month; anything else answers undefined and keeps its text. The chart's category label reads it. The row's key is untouched, so drill-through and ordering are unchanged. The line/area edge inset (objectui#13390) is now sized from that face (Mmm 0000) instead of 0000-00.
  • Row 15 — legend swatches. A bar or area mark fills from a gradient, so recharts handed the legend url(#…), which no swatch can draw: every bar chart's legend had no swatch. AdvancedChartImpl now hands ChartLegendContent a per-series LegendSwatch (the colour the mark is drawn in). A compareTo overlay keeps its primary's hue (it mirrors it on purpose) and its swatch is drawn hollow, the way its mark is muted, so the two read apart. A lone bar series painted per category (semantic option colours) draws its label with no swatch, since no one colour names it.
  • Row 18 — category labels on a phone. The phone cut to eight characters lived in the shared category formatter, so it hit every axis. Now:
    • the horizontal-bar category axis draws each label whole, wrapped into the axis width (sized from the widest label, 60–140 px) to at most two lines, through WrappedCategoryTick; a label still too long ends in … with the full text as an SVG title;
    • on the vertical x axis the cut moved into xAxisTickFormatter, so the fit and rotation decisions measure the full label: a phone's few buckets rotate and draw In Progress whole (the rotated cap of 12 characters holds it). An unrotated label on a phone, and a rotated one past 12 characters, are still cut, and TitledCategoryTick (recharts' own Text) carries the full label as its tooltip.

The widget descriptions on the CRM Pipeline dashboard that leak authoring notes ("Pie/donut/funnel ignore compareTo", "(compareTo)") are example content in examples/app-crm/src/dashboards/pipeline.dashboard.ts; not edited here, per the order.

Tests

  • New: ui/packages/core/src/utils/__tests__/date-bucket-label.test.ts, ui/packages/plugin-charts/src/AdvancedChartImpl.dashboardReadability-27520.test.tsx, ui/packages/plugin-dashboard/src/__tests__/metricTileDatasetValueScale-27520.test.tsx.
  • Adapted, because they pinned the behaviour this changes:
    • AdvancedChartImpl.pointAxisEdgeLabels-13390 and AdvancedChartImpl.missingCategoryKey read the month labels as Aug 2026 / Jan 2026; the edge-inset geometry assertions are unchanged and pass.
    • AdvancedChartImpl.labelFitBand-13241 asserted the full name was absent from the chart's whole textContent; the tick's tooltip now carries it, so the assertion reads the drawn labels and pins the tooltip.
    • metricTileLayout-12623 pins the short face, and its fixture 1964500 is now past the short rung, so it uses 196450; the long value is pinned in the new 27520 file.
  • Ablations, each through scripts/ablation-replace.mjs with an on-disk restore to the HEAD blob: removing the month label, the dataset ladder, the legend swatch colour or the wrap each turns its new test red (1 failed each). The first month ablation was a no-op (an empty replacement is refused) and was redone with --delete.

Acceptance notes

Verification

Browser, Chromium at 390×844 and 1440×900, Console from this branch's ui/ src (Vite) against examples/app-showcase and examples/app-crm booted --fresh --seed-admin; the before column is the same stacks with the Console at the merge base 9f13ee81.

Measured before / after
Where Before (9f13ee8) After (this branch)
Showcase Delivery Operations, Total Budget, phone 20 px, scrollWidth 143 in a 123 px box: clipped 14 px, 100 px: whole
Same, desktop 24 px, whole 24 px, whole (unchanged)
Showcase Tasks by Status x axis, phone In Progr…, In Revie… In Progress, In Review (rotated)
Showcase Task Throughput x axis 2026-09, 2026-10 Sep 2026, Oct 2026
Showcase Tasks by Status legend swatch background transparent no swatch (per-category bars), label only
CRM Pipeline Trend x axis 2025-10 … 2026-10 Oct 2025 … Oct 2026
CRM Pipeline Trend legend both swatches solid rgb(36, 99, 235) primary solid, vs last year hollow (1 px border, same hue)
CRM Opportunities by Stage legend both swatches transparent (url(#…)) primary solid, vs previous period hollow
CRM Opportunities by Stage x axis, phone Prospect…, Qualific…, Closed W… Prospecting, Qualificati… (title: Qualification), Closed Won
Local runs

At head 23380e90 (this branch merged with origin/main at 35f20551):

  • Under scripts/pm/os-verify-lock.sh: pnpm install --frozen-lockfile, the @object-ui/plugin-dashboard... + @object-ui/plugin-charts... closure build, type-check of @object-ui/core, @object-ui/plugin-charts and @object-ui/plugin-dashboard, then pnpm --dir ui exec vitest run --maxWorkers=2 packages/core/ packages/plugin-charts/ packages/plugin-dashboard/: VERDICT command-exit 0; Test Files 523 passed (523), Tests 7418 passed, 42 skipped (7460).
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 35 commands, each run with </dev/null, all exit 0; --ran reconciliation: 35 derived, 35 run, 0 NOT-MEASURED. pnpm --dir ui exec vitest run scripts/ was SIGKILLed on its first run (the box was out of memory with the browser stacks still up); rerun alone after stopping them: exit 0, Test Files 169 passed, 2 skipped (171), Tests 4901 passed, 2 skipped.
  • Also run: pnpm --dir ui check:new-line-citations exit 0 (outside the derivation), node scripts/check-sdui-manifest.mjs exit 0.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head 23380e9063c4f58ab60b5fc9393dde11a04cefea · 2026-10-30T17:50Z

Contract review lane (Fable): declaration face (Clause-②: yes, no schema or governed path) at TIER_DEFAULT per record-recognisers.mjs --template --paths; reading the card, the PR diff and the head's check-runs only. The record follows on this head once its check-runs complete.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-tesla · session_01L9qibzKuj2HUUN2wUhZ9Go

Test UI (1/16) failed at 23380e90 on packages/app-shell/src/views/studio-design/AutomationsPillar.problemsAgree-27799.test.tsx:211, with expected 'flows[0].nodes[1].config.fields.id' to contain 'node \'write\''. The failure is not from this PR. It is the main-red tracked in #28219: #28192 (3efccfd5) gave the lint finding a key path, and that test still expects the old prose. This diff touches no studio-design or lint file. The fix is #28227, which is still open. I am not porting it here, because that would put another lane's test fix inside this PR. I am also not re-running the job, since main stays red until #28227 lands. Once it does, I will merge origin/main into this branch and push, and CI will run again.


Generated by Claude Code

The axis already read `2025-10` as `Oct 2025`; the tooltip header beside
it still showed the raw key. ChartTooltipContent now runs its header
through formatMonthBucketLabel in the display locale, and any other label
reads as written.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzKuj2HUUN2wUhZ9Go
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-tesla · session_01L9qibzKuj2HUUN2wUhZ9Go

The head moved to 63b2a696:

  • 644c6e3e merges origin/main at 16e21226. test(ui): the Automations Problems double serves the draft door's node path (#28192) #28227 cleared main-red Test UI red on main: AutomationsPillar.problemsAgree-27799 still expects the node config finding's where prose, and #28192 now gives it a key path #28219, so the Test UI (1/16) failure above no longer applies.
  • 63b2a696 folds in the contract review's should-fix. The chart tooltip header now reads a month bucket the way the axis does: 2025-10 shows as Oct 2025. It goes through formatMonthBucketLabel in ChartTooltipContent, and any other label reads as written. This replaces the body's acceptance note that said the header still showed the raw key. An ISO day still shows its raw key in the header, as before.
  • New cases are in AdvancedChartImpl.dashboardReadability-27520.test.tsx: the header reads Oct 2025, and as a control, 2025-Q4 and In Progress read as written. With the mapping removed, the new case fails (1 failed); with it restored, it passes.
  • Local runs at 63b2a696:
    • pnpm --filter @object-ui/plugin-charts type-check: exit 0.
    • pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-charts/ packages/plugin-dashboard/: Test Files 302 passed (302), Tests 3247 passed, 15 skipped.
    • check:nul-bytes, check:test-source-alias, check-sdui-manifest, check:new-line-citations and check-empty-changeset: all exit 0.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head 63b2a69654e11bfaac0d3f31264d749fc1e6213e · 2026-10-30T19:39Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 63b2a69654e11bfaac0d3f31264d749fc1e6213e
Local-runs: none
Scope: declaration face (Clause-②: yes (widening: @object-ui/core gains the public export formatMonthBucketLabel; no accept set moves)), the whole diff outside the prose faces: ui/packages/core/src/index.ts and src/utils/date-bucket-label.ts, ui/packages/plugin-charts/src/AdvancedChartImpl.tsx and ChartContainerImpl.tsx, ui/packages/plugin-dashboard/src/DatasetWidget.tsx and metricTile.ts, the three new and four adapted tests, .changeset/27520-dashboard-readability.md; plus the two package barrels read to check the declaration (ui/packages/plugin-charts/src/index.tsx, ui/packages/plugin-dashboard/src/index.tsx).

Inputs: card #27520 body (rows 3, 15 and 18) and its comments (triage 6495077487, seat 4's split 6506509231, ui seat 1 claim 6526230292, os-dev-report 6529089724, claim amendment 6529126343); PR #28223 body, 14-file list and the diff against main (+746/-62); the check-runs on 23380e90 (read 2026-10-30T20:05Z): check-runs on 63b2a69: 55 total, 44 success (Auto Label, Build & E2E UI, Build UI Deps, Check Changeset, Check PR Size, Dogfood Regression Gate, Governed Surface Queue Guard, Lint & Repo Gates, Lint UI, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Live E2E UI, PR-body guards (claim · duplicate · Part-of · single-writer path), Test Core, Test UI, Test UI (all 16), Type Check UI, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, TypeScript Type Check, filter); 11 skipped path-filtered (18.x upgrade harness (opt-in), Build Core, Build Docs, Dogfood Regression Gate (matrix shard/3), Dogfood Verify CLI, Packed-tarball smoke (opt-in), Plan Test Core shards, Schedule gate · skip when a green push run covers the commit, Temporal Conformance (live PG + MySQL), Test Core (matrix shard/8), UI Published Artifacts); 0 failure, 0 in progress Author user.login is objectstack-fleet[bot], so no login note is owed. The reviewer is not the drafter (session_01L9qibzKuj2HUUN2wUhZ9Go).

Delta since the 23380e9 review (claim 6529668734; re-claimed on this head): two commits. 644c6e3 merges origin/main 39da1bd (which carries the #28227 main-red fix): the PR's own hunks against the new merge base are identical to the reviewed ones (same file names, no content difference beyond hunk offsets) and main changed no file the PR touches. 63b2a69 is the should-fix below: ChartTooltipContent in ChartContainerImpl.tsx now passes a string label through formatMonthBucketLabel(value, displayLocale) and falls back to the label itself, with displayLocale (already read in that component) added to the memo deps; a labelFormatter given by the caller still wins, as before. Two new tests pin 2025-10 reading Oct 2025 and the control that 2025-Q4 / In Progress read as written; the changeset's month-axis bullet now names the tooltip header. Still Metadata-protocol: none and TIER_DEFAULT: no spec member moves.

① Derived judgments

  • The declared widening is exact: @object-ui/core's barrel gains export * from './utils/date-bucket-label.js', i.e. the one function formatMonthBucketLabel(value, locale): a YYYY-MM key (month 01–12 only) answers the locale's short-month-and-year face formatted in UTC from a UTC date (setUTCFullYear, so years 0–99 are not mapped to 1900+), anything else or an Intl refusal answers undefined and the caller keeps the key. No other public surface moves: LegendSwatch (ChartContainerImpl.tsx) and METRIC_TILE_DATASET_VALUE_LADDER (metricTile.ts) are module exports that neither package barrel re-exports, read on this head.
  • Row 15: the chart's category label formatter reads the month face and the row key stays the bucket's identity, so drill-through and ordering are untouched; the point-axis edge inset is now sized from Mmm 0000. Legend swatches: AdvancedChartImpl hands ChartLegendContent a per-series swatch (the series colour instead of the gradient's url(#…)), hollow for a comparison series, none for a lone per-category bar series; seriesColorAt is the same colour rule the marks already used, lifted out of the render loop.
  • Row 3: DatasetWidget's heading tile takes METRIC_TILE_DATASET_VALUE_LADDER (same rungs, re-measured without the Card inset, bounded so no rung draws 24 px under 160 px) through metricTileValueClass(value, ladder); past the ladder the value keeps the ellipsis and carries the full figure as title. A tile without a heading is byte-for-byte unchanged.
  • Row 18: the eight-character phone cut left the shared formatter; the horizontal-bar category axis wraps whole labels into a width sized from the widest label (60–140 px) to two lines through WrappedCategoryTick, and the vertical x axis cuts only after the fit/rotation decisions, with TitledCategoryTick carrying the full label as an SVG title element when cut.
  • Adapted tests are listed with their reasons (the month face in two pins, the tooltip in the label-fit pin, a fixture moved past the short rung in the layout pin); the three new files pin each row, and the four ablations are the dev's report, read not re-run.
  • Order: rows 3, 15 (chart half) and 18 (chart part) only; examples/** untouched; the money half of row 15 reported to the seat (amendment 6529126343) rather than fixed here.

② Semver level

  • @object-ui/core patch: an additive barrel export, the fix(ui): list each failed validation rule as its own line in the form banner and toast #28151 precedent (Clause-②: yes (widening …; no accept set moves)), with no accept set moving. @object-ui/plugin-charts and @object-ui/plugin-dashboard patch: rendering changes with no public export moved. Metadata-protocol: none holds — no spec or metadata file in the diff.
  • The three carriers agree: PR body, claim amendment 6529126343 and the changeset.

③ Boundary flags

  • Part of #27520: the card stays open for the other rows and for row 15's money half (seat-filed as its own card).
  • Behaviour visible to every dashboard: month axes read Oct 2025, bar/area legends gain swatches, phone category labels are no longer cut to eight characters; each is disclosed in the changeset, and none changes data, keys or authored schema.
  • Governed: none. Tier: +746/-62 over 14 files, Tier S. Author objectstack-fleet[bot] ⇒ no login note.

Should-fix, resolved by 63b2a69 on this head: the chart tooltip header now reads a month bucket key as its month, the same face as the axis.

Implemented-by: claude/project-thread-ty1d4m
Reviewed-by: session_01L9qibw58ehQ2gRHP2h2Puz

VERDICT: PASS

@objectstack-fleet

objectstack-fleet Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN

席位核对

ACCEPT · seat domain:ui#1 · head 63b2a696 · 2026-10-30T20:19Z

席位意见:仪表盘可读性三行。KPI 卡片的主数值在手机上不再被截成 $1,090,00...,而是保持度量自身的格式($1,090,000.00)并缩小字号以放进卡片(未改用紧凑写法,否则会覆盖度量格式)。对比系列有了自己的颜色,图例带色块,月份轴(以及提示框标题)显示 Oct 2025 而不是 2025-10。横向条形图的类目标签换行显示,放不下时截断并带完整提示。新增 @object-ui/core 公开导出 formatMonthBucketLabel,已按扩面申报。本 PR 是 Part of #27520,第 15 行的金额币种一半另立 #28225。

  1. Shape: Part of #27520, draft, base main, 14 files, +775/−63. ui/packages/core/src/utils/date-bucket-label.ts plus export, plugin-charts AdvancedChartImpl.tsx / ChartContainerImpl.tsx (series swatch, legend swatches, wrapped category ticks, month face on axis and tooltip header), plugin-dashboard DatasetWidget.tsx / metricTile.ts (value-scale ladder). New tests, plus four adapted pins. The seat read the adapted pins, and each keeps its meaning: the label-fit assertion moved from textContent to the drawn labels plus the tick tooltip, the month pins read the new face, and the tile value was shortened below the new ladder. examples/** was not edited.
  2. Declaration: Clause-②: yes (widening: @object-ui/core gains formatMonthBucketLabel; no accept set moves) (claim amended 2026-10-30T17:31Z), Metadata-protocol: none.
  3. Contract review: PASS at this head, record 6532420116 (TIER_DEFAULT). The tooltip-header should-fix is folded in at 63b2a696, with a test and a control.
  4. Gates / check-runs: on 63b2a696, 44 success, 11 skipped, 0 failure (one run re-queued by the label change). Dev's local run: 35/35 derived gates exit 0, core + plugin-charts + plugin-dashboard 7418 tests, ablations red on all four behaviours. Browser before/after at 390 and 1440 px is in the PR body.
  5. Follow-ups: Dashboard KPI tiles bound to a dataset show money values with no currency #28225 (dataset KPI tiles show money without currency, row 15's money half; serial after this PR). The CRM widget descriptions that leak authoring notes are example content in examples/app-crm/src/dashboards/pipeline.dashboard.ts, noted, not edited.
  6. Landing: check-governed-merges --pr 28223: NOT governed, 838 changed lines ≤ 3000. The seat readies the PR and arms auto-merge on the maintainer's 「按 pm-dispatch 审过的pr你可以自主落地」 (seat thread, 2026-10-30T14:19Z).

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 20:25
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 9e4f1cf Oct 30, 2026
59 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ty1d4m branch October 30, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant