Skip to content

fix(plugin-dashboard): metric tile captions its value with the widget description - #27972

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-qtzjxg
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-qtzjxg

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

Requested by Bill · project thread

维护者速读

  • 改了什么:仪表盘 KPI(metric)卡片数值下方的那行小字,作者写了 description 时显示 description,没写时仍显示度量名(如 "Total Amount")。
  • 为什么改:HotCRM 反馈 KPI 卡只印度量名,说明"这个数统计的是什么"的 description 只藏在标题的悬停提示里(console analytics: a formula declared returnType currency renders as a bare number, a KPI tile cannot show its description, percent fields always draw a progress bar, and report charts cannot split a series by a second dimension #27430 第 2 行)。
  • 风险与回滚:只改 plugin-dashboard 里一处取值,不新增元数据键;已写 description 的现有 KPI 卡,小字会从度量名变成描述,度量名仍在标题悬停提示里。回滚即 revert 本 PR。
  • 先看:DatasetWidget.tsx 里 const caption 一行及其渲染处;新测试 DashboardSurfaces.metricTileCaption-27430.test.tsx;两份旧测试里被改写的"描述不显示"断言。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileCaption-27430.test.tsx,预期 10 passed。
  • 需要你做的:无,席位落地。

Fixes #27430

Clause-②: no
Metadata-protocol: none

What changes

Row 2 of #27430 only. A dataset-bound metric tile (DatasetWidget with a heading, drawn by both DashboardRenderer and DashboardGridLayout) now captions its value with the widget's description when one is authored, and with the measure label otherwise. The caption keeps the measure label's slot and class (METRIC_TILE_MEASURE_LABEL_CLASS), so the tile keeps its two lines of text and still drops the caption below 160 px. The title's tooltip is unchanged: the description, then the measure label, which is where the measure label stays reachable under a description caption.

No new key is read. description is the existing widget-level key of DashboardWidgetSchema ("Widget description text below the header"), already resolved and localized by both surfaces and handed to the tile in heading. Nothing reads a caption from the options bag.

Why

HotCRM's Service and Sales dashboards declare a description on each KPI to state its base, and the tile showed only the measure label under the value. The description reached the screen only as a hover tooltip.

Tests

  • New ui/packages/plugin-dashboard/src/__tests__/DashboardSurfaces.metricTileCaption-27430.test.tsx, on both surfaces: a described metric draws the description as the caption, once, and the measure label only in the tooltip; an undescribed metric and an empty description fall back to the measure label; an inline per-locale description follows the UI language; CONTROL, a dataset-bound table draws no metric caption.
  • DashboardRenderer.metricTileDensity-12528.test.tsx and DashboardSurfaces.metricTileTwoLines-12573.test.tsx pinned "the description is drawn nowhere else". Their layout pins now render a metric with no description (caption = measure label), and the 12528 tooltip pin also asserts the description caption.
  • Ablation, at 6c8d7e20, through scripts/ablation-replace.mjs (no dist leg: the root vitest config aliases @object-ui/* to src/): the caption line set back to the measure label made 5 tests fail (both surfaces' description and per-locale pins, and the 12528 description pin) while the 8 fallback and control pins passed; restore verified blob == HEAD and git diff HEAD empty.

Acceptance notes

Row Bucket Reading on main at ed0e71db
2 KPI tile caption TAKE (this PR) The tile drew the measure label as its caption; widget.description reached the tile in heading and was used only for the title tooltip.
3 percent always a bar CARD → #27973 PercentCellRenderer draws the bar unconditionally and reads no face key. Neither the field (format has no vocabulary and only promotes text types, widget is the form override) nor the list column (type is a renderer override; number would print the stored fraction) selects a plain percentage. Needs a new display key.
4 report chart second dimension, summary subtotals CARD → #27974 (series), #27975 (subtotals) ReportChartSchema is xAxis plus yAxis, with no series dimension; the dashboard path already pivots a second dimension (buildChartSeries). A summary report asks only for the grand total (totals.groupings of the empty grouping) and the spec has no subtotal key.

Verification

Local runs at 6c8d7e2
  • pnpm --filter @object-ui/plugin-dashboard test: Test Files 198 passed (198), Tests 1908 passed, 6 skipped; lock VERDICT command-exit 0.
  • pnpm --filter @object-ui/plugin-dashboard type-check: exit 0. eslint on the six touched files: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands: 34 families, each run, all exit 0, including node scripts/check-sdui-manifest.mjs and its --self-test, pnpm check:nul-bytes, and pnpm --dir ui exec vitest run scripts/ (169 files passed, 2 skipped). --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN.
  • pnpm check:new-line-citations (ui): 0 new citations.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No spec change.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes on the touched files.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the maintainer's summary).

Generated by Claude Code

… description

A dataset-bound metric tile drew the measure label under its value and the
widget description only in the title tooltip. The description, when
authored, is now the caption in the measure label's place; the measure
label keeps riding in the title tooltip.

Claude-Session: https://claude.ai/code/session_01Utp6CXAa3A4UiNf9JKBZ7R
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

席位核对

ACCEPT · head 6c8d7e208ed59d7cd38abdf3bb65853055e87a7f · 2026-10-30T06:06Z

  • Shape: draft → main. The body moves from Part of #27430 to Fixes #27430: row 1 landed earlier (PR fix(plugin-grid, plugin-detail): a returnType currency formula reads as money in list cells and on the record page #27689, PR fix(plugin-detail): a returnType currency formula reads as money in related lists and the quick-look panel #27800), row 2 is here, and rows 3 and 4 are filed. Clause-②: no and Metadata-protocol: none match the claim. No new key is involved (widget.description is the existing widget-level key), so no contract review is owed.
  • Row 2, measured on ed0e71db: a dataset-bound metric tile captions its value with widget.description when one is authored, otherwise with the measure label. The measure label still rides in the title's tooltip. This holds on both surfaces (DashboardRenderer, DashboardGridLayout), and density is unchanged because the caption takes the label's slot.
  • The reversed pins (objectui#12528, objectui#12573), read: their "the description shows nowhere else" rested on "the spec declares no widget key to show it inline". It was a reading, not a ruling. The standing ruling C of objectui#11389 says a caption returns only as a declared widget-level key outside the options bag, never inside it. widget.description is exactly that, so the rewrite holds and the pins assert the new caption.
  • Tests: a new caption pin (10 cases, both surfaces) and the two rewritten pins. Ablation: setting the caption back to the measure label turns 5 red and leaves 8 green (fallback and controls), restored to the HEAD blob. plugin-dashboard 198/198 files.
  • Filed by the seat, all pm:queue, p3:
  • Not filed: the spec doc comment on widget.description ("card-header subtitle") now also covers the metric caption. That is wording only, left as an observation in the body.
  • Gates (check-runs on the head): 44 success, 11 skipped, 0 failure; mergeable_state clean. check-governed-merges --pr 27972: NOT governed, 244 changed lines.
  • Accepted deviations: the body's closing keyword and card references, patched by the seat. The PR line-2 link names the card thread.
  • Landing: non-governed, all checks green, mergeable_state clean ⇒ the seat lands it (pr_ready + automerge_enable).

Generated by Claude Code

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/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

1 participant