Skip to content

Commit 1bd2795

Browse files
os-zhuangclaude
andauthored
feat(spec,lint): the ui vocabularies admit what the renderers implement (objectui#2945) (#4070)
* feat(spec,lint): the ui vocabularies admit what the renderers implement (objectui#2945) Additions-only follow-up to the vocabulary audit. Nothing narrows, so no already-stored metadata changes meaning. - ChartTypeSchema admits `combo`. The taxonomy could not name the one family the rest of chart.zod.ts is written for: ChartSeriesSchema.type exists to override a series' type ("combo charts", per its own doc comment) and ChartSeriesSchema.yAxis binds left/right, which only means something for mixed marks. objectui draws it distinctly and carried `combo` in a local fork whose comment claimed to mirror this list. - WidgetActionTypeSchema is ActionType. They disagreed by `form`, and backwards: a widget/header action dispatches through the same ActionRunner that implements `form` (DashboardRenderer routes everything but a raw `url` into it, deliberately — #3528), so the narrower enum rejected at validation what the dispatcher executes. - ListChartConfigSchema.chartType is ChartTypeSchema.extract([...]) — same five members, a de-duplication rather than a widening. - @objectstack/lint's chart-family set derives from the taxonomy. Its omissions failed in the worst direction: an unlisted family read as "not a chart", so a widget with no chartConfig measure mapping PASSED validation. Pinning the old hand-written list back reproduces exactly that for `combo` — zero findings — which is the new test's teeth. ActionType deliberately does NOT gain `navigation`: executeNavigation is a strictly weaker executeUrl (no ${param} interpolation, no apiBase promotion, no openIn) differing only by `replace`, and its one live producer is the SDUI element:button `action` prop that ElementButtonPropsSchema does not model at all. Adding the name would put a second spelling of "navigate" into a closed vocabulary without closing the real gap. spec 6944 tests / 267 files and lint 544 / 37 green; tsc clean on both. Refs objectstack-ai/objectui#2945, objectstack-ai/objectui#2901, #3528 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(showcase,docs): the gallery demonstrates `combo`, and the reference docs regenerate Two gates fired on the taxonomy addition, both correctly. - app-showcase's coverage test requires a gallery widget for every distinctly-renderable ChartType, so admitting `combo` without demonstrating it failed. The Chart Gallery now shows one: task count as bars on the left axis, avg progress as a line on the right — the configuration series[].type / series[].yAxis exist for. Rows below it shift down by 4 to keep the 12-column layout non-overlapping. - content/docs/references/ui/{chart,dashboard,report}.mdx regenerated for the new chart-type member and the two derived enums. check:docs, check:api-surface and check:authorable-surface pass; the showcase coverage suite is 13/13. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): regenerate the objectstack-data reference index `view.zod.ts` now imports `chart.zod.ts` (for the extracted list-chart subset), so the taxonomy joins that skill's transitive reference set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): regenerate the react-blocks contract for the `combo` chart type Fifth generated artefact tracking the spec surface: the react-blocks contract and its reference table inline the ChartType union. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(showcase): translate the new gallery widget title at birth check-i18n-coverage freezes each example's untranslated-declared-string count and fails on growth; the `combo` widget's title took app-showcase from 456 to 457. Translated in both locales the example claims to support, which is the remedy the bundle's own `globalActions` comment prescribes. The gallery's other 20 widget titles predate that ratchet and stay part of the frozen baseline — this does not ratchet the number up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 785d2ea commit 1bd2795

16 files changed

Lines changed: 283 additions & 42 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
"@objectstack/spec": minor
3+
"@objectstack/lint": patch
4+
---
5+
6+
feat(spec,lint): the `ui` vocabularies admit what the renderers implement, and derive instead of restating (objectui#2945)
7+
8+
Additions-only follow-up to the vocabulary audit
9+
(objectstack-ai/objectui#2901, #2945). Nothing here narrows a vocabulary, so no
10+
already-stored metadata changes meaning — three of the four `ui/` enums that had
11+
drifted from what is actually implemented, plus the fork that drift had made
12+
invisible.
13+
14+
**`ChartTypeSchema` admits `combo`.** The taxonomy could not name the one chart
15+
family the rest of `chart.zod.ts` is written for: `ChartSeriesSchema.type`
16+
exists to override a series' type — its doc comment literally says *"combo
17+
charts"* — and `ChartSeriesSchema.yAxis` binds a series to the left or right
18+
axis, which is only meaningful for mixed marks. objectui's renderer draws it
19+
distinctly (mixed bar/line/area on dual axes, per-series type) and had to carry
20+
`combo` in a local fork of this list, whose own comment claimed to mirror it.
21+
22+
**`WidgetActionTypeSchema` is `ActionType`.** The two disagreed by one member,
23+
`form`, and the disagreement was backwards: a dashboard header or widget action
24+
button dispatches through the same `ActionRunner` that implements `form`
25+
objectui's `DashboardRenderer` deliberately routes everything except a raw `url`
26+
into it, so a `flow` header action works (#3528). The narrower enum therefore
27+
rejected at validation exactly what the shared dispatcher then executes.
28+
Derived, so the next type the runner implements needs one edit, not two.
29+
30+
**`ListChartConfigSchema.chartType` is `ChartTypeSchema.extract([...])`.** Same
31+
five members as before — a de-duplication, not a widening. A member renamed in
32+
the taxonomy now fails at build time instead of leaving a second list quietly
33+
disagreeing.
34+
35+
**`@objectstack/lint`'s chart-family set is derived from the taxonomy.**
36+
`validate-widget-bindings` decides which widgets need a `chartConfig` measure
37+
mapping from a hand-written list of families, and its omissions fail in the
38+
worst direction: an unlisted family reads as *"not a chart"*, so a widget
39+
missing its mapping **passes** validation. `combo` was exactly that case —
40+
verified by pinning the old list back, where a `combo` widget with no
41+
`chartConfig` produced zero findings. The set is now the taxonomy minus an
42+
explicit `MEASURE_EXEMPT_CHART_TYPES` (single-value and tabular families), so a
43+
family added to the spec is covered without editing the rule.
44+
45+
Guards: `packages/spec/src/ui/vocabulary-derivation.test.ts` asserts both
46+
derivations still hold (a restated list fails silently — it keeps validating,
47+
just not what the other list says), and the lint suite now walks every
48+
multi-series family in the taxonomy rather than a list of its own.
49+
50+
A third ratchet already existed and did its job: `app-showcase`'s coverage test
51+
requires a gallery widget for every distinctly-renderable `ChartType`, and it
52+
failed the moment `combo` was admitted. The Chart Gallery dashboard now
53+
demonstrates it — a task count as bars on the left axis, an average as a line on
54+
the right, which is the configuration `series[].type` / `series[].yAxis` exist
55+
for.
56+
57+
`ActionType` deliberately does **not** gain `navigation`, which the audit
58+
suggested. `ActionRunner.executeNavigation` is a strictly weaker
59+
`executeUrl` — no `${param.X}` interpolation, no `apiBase` promotion, no
60+
`openIn` — differing only by a `replace` option, and its one live producer is
61+
the SDUI `element:button` `action` prop, which `ElementButtonPropsSchema` does
62+
not model at all. Promoting the name would add a second spelling of *navigate*
63+
to a closed authorable vocabulary (members cannot be removed later) without
64+
closing the gap that actually exists. Tracked separately.
65+
66+
Verified: `@objectstack/spec` **6944 tests / 267 files**, `@objectstack/lint`
67+
**544 tests / 37 files**, both green; `tsc --noEmit` clean on both.

content/docs/references/ui/chart.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,13 @@ Inline aggregation for an object-bound chart
9797

9898
| Property | Type | Required | Description |
9999
| :--- | :--- | :--- | :--- |
100-
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
100+
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
101101
| **title** | `string` | optional | Chart title |
102102
| **subtitle** | `string` | optional | Chart subtitle |
103103
| **description** | `string` | optional | Accessibility description — announced to screen readers as the chart’s label |
104104
| **xAxis** | `{ field: string; title?: string; format?: string; min?: number; … }` | optional | X-Axis configuration |
105105
| **yAxis** | `{ field: string; title?: string; format?: string; min?: number; … }[]` | optional | Y-Axis configuration (support dual axis) |
106-
| **series** | `{ name: string; label?: string; type?: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; color?: string; … }[]` | optional | Defined series configuration |
106+
| **series** | `{ name: string; label?: string; type?: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; color?: string; … }[]` | optional | Defined series configuration |
107107
| **colors** | `string[] \| Record<string, string>` | optional | Color palette (string[]) or value→color map (`{ value: color }`) |
108108
| **height** | `number` | optional | Fixed plot height in pixels (overrides the container default) |
109109
| **showLegend** | `boolean` || Display legend |
@@ -164,7 +164,7 @@ Type: `string`
164164
| :--- | :--- | :--- | :--- |
165165
| **name** | `string` || Field name or series identifier |
166166
| **label** | `string` | optional | Series display label |
167-
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` | optional | Override chart type for this series |
167+
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` | optional | Override chart type for this series |
168168
| **color** | `string` | optional | Series color (hex/rgb/token) |
169169
| **stack** | `string` | optional | Stack identifier to group series |
170170
| **yAxis** | `Enum<'left' \| 'right'>` || Bind to specific Y-Axis |
@@ -190,6 +190,7 @@ Type: `string`
190190
* `scatter`
191191
* `treemap`
192192
* `sankey`
193+
* `combo`
193194
* `gauge`
194195
* `solid-gauge`
195196
* `metric`

content/docs/references/ui/dashboard.mdx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ const result = Dashboard.parse(data);
3232
| **name** | `string` || Dashboard unique name |
3333
| **label** | `string` || Dashboard label |
3434
| **description** | `string` | optional | Dashboard description |
35-
| **header** | `{ showTitle: boolean; showDescription: boolean; actions?: { label: string; actionUrl: string; actionType?: Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api'>; icon?: string }[] }` | optional | Dashboard header configuration |
36-
| **widgets** | `{ id: string; title?: string; description?: string; type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; … }[]` || Widgets to display |
35+
| **header** | `{ showTitle: boolean; showDescription: boolean; actions?: { label: string; actionUrl: string; actionType?: Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api' \| 'form'>; icon?: string }[] }` | optional | Dashboard header configuration |
36+
| **widgets** | `{ id: string; title?: string; description?: string; type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; … }[]` || Widgets to display |
3737
| **columns** | `integer` | optional | Number of grid columns (default 12) |
3838
| **gap** | `integer` | optional | Grid gap in Tailwind spacing units |
3939
| **refreshInterval** | `number` | optional | Auto-refresh interval in seconds |
@@ -63,7 +63,7 @@ Dashboard header configuration
6363
| :--- | :--- | :--- | :--- |
6464
| **showTitle** | `boolean` || Show dashboard title in header |
6565
| **showDescription** | `boolean` || Show dashboard description in header |
66-
| **actions** | `{ label: string; actionUrl: string; actionType?: Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api'>; icon?: string }[]` | optional | Header action buttons |
66+
| **actions** | `{ label: string; actionUrl: string; actionType?: Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api' \| 'form'>; icon?: string }[]` | optional | Header action buttons |
6767

6868

6969
---
@@ -78,7 +78,7 @@ Dashboard header action
7878
| :--- | :--- | :--- | :--- |
7979
| **label** | `string` || Action button label |
8080
| **actionUrl** | `string` || URL or target for the action |
81-
| **actionType** | `Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api'>` | optional | Type of action |
81+
| **actionType** | `Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api' \| 'form'>` | optional | Type of action |
8282
| **icon** | `string` | optional | Icon identifier for the action button |
8383

8484

@@ -93,13 +93,13 @@ Dashboard header action
9393
| **id** | `string` || Unique widget identifier (snake_case) |
9494
| **title** | `string` | optional | Widget title |
9595
| **description** | `string` | optional | Widget description text below the header |
96-
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || Visualization type |
97-
| **chartConfig** | `{ type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; title?: string; subtitle?: string; description?: string; … }` | optional | Chart visualization configuration |
96+
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || Visualization type |
97+
| **chartConfig** | `{ type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; title?: string; subtitle?: string; description?: string; … }` | optional | Chart visualization configuration |
9898
| **colorVariant** | `Enum<'default' \| 'blue' \| 'teal' \| 'orange' \| 'purple' \| 'success' \| 'warning' \| 'danger'>` | optional | Widget color variant for theming |
9999
| **requiresObject** | `string` | optional | Hide the widget unless the named object is registered |
100100
| **requiresService** | `string` | optional | Hide the widget unless the named kernel service is registered |
101101
| **actionUrl** | `string` | optional | URL or target for the widget action button |
102-
| **actionType** | `Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api'>` | optional | Type of action for the widget action button |
102+
| **actionType** | `Enum<'script' \| 'url' \| 'modal' \| 'flow' \| 'api' \| 'form'>` | optional | Type of action for the widget action button |
103103
| **actionIcon** | `string` | optional | Icon identifier for the widget action button |
104104
| **filter** | `any` | optional | Presentation-scope filter (runtimeFilter) |
105105
| **compareTo** | `'previousPeriod' \| 'previousYear' \| { offset: string }` | optional | Period-over-period comparison window |
@@ -179,6 +179,7 @@ Widget action type
179179
* `modal`
180180
* `flow`
181181
* `api`
182+
* `form`
182183

183184

184185
---

content/docs/references/ui/report.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const result = JoinedReportBlock.parse(data);
3333
| **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
3434
| **description** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) |
3535
| **type** | `Enum<'tabular' \| 'summary' \| 'matrix'>` || |
36-
| **chart** | `{ type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; title?: string; subtitle?: string; description?: string; … }` | optional | |
36+
| **chart** | `{ type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; title?: string; subtitle?: string; description?: string; … }` | optional | |
3737
| **dataset** | `string` | optional | Dataset name to bind (ADR-0021) |
3838
| **rows** | `string[]` | optional | Dimension names down (dataset-bound) |
3939
| **columns** | `string[]` | optional | Dimension names across (matrix, dataset-bound) |
@@ -61,7 +61,7 @@ const result = JoinedReportBlock.parse(data);
6161
| **runtimeFilter** | `any` | optional | Render-time scope filter |
6262
| **order** | `{ by: string; direction: Enum<'asc' \| 'desc'> }[]` | optional | Result ordering, most significant key first |
6363
| **drilldown** | `boolean` || Click-through to underlying records |
64-
| **chart** | `{ type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; title?: string; subtitle?: string; description?: string; … }` | optional | Embedded chart configuration |
64+
| **chart** | `{ type: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; title?: string; subtitle?: string; description?: string; … }` | optional | Embedded chart configuration |
6565
| **blocks** | `{ name: string; label?: string; description?: string; type: Enum<'tabular' \| 'summary' \| 'matrix'>; … }[]` | optional | Sub-reports for type=joined |
6666
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this report. |
6767
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
@@ -81,13 +81,13 @@ const result = JoinedReportBlock.parse(data);
8181

8282
| Property | Type | Required | Description |
8383
| :--- | :--- | :--- | :--- |
84-
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
84+
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
8585
| **title** | `string` | optional | Chart title |
8686
| **subtitle** | `string` | optional | Chart subtitle |
8787
| **description** | `string` | optional | Accessibility description — announced to screen readers as the chart’s label |
8888
| **xAxis** | `string` || Dataset dimension name for the X-axis (bound-dataset dimension, not a raw field) |
8989
| **yAxis** | `string` || Dataset measure name for the Y-axis (bound-dataset measure, not a raw field) |
90-
| **series** | `{ name: string; label?: string; type?: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; color?: string; … }[]` | optional | Defined series configuration |
90+
| **series** | `{ name: string; label?: string; type?: Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'combo' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>; color?: string; … }[]` | optional | Defined series configuration |
9191
| **colors** | `string[] \| Record<string, string>` | optional | Color palette (string[]) or value→color map (`{ value: color }`) |
9292
| **height** | `number` | optional | Fixed plot height in pixels (overrides the container default) |
9393
| **showLegend** | `boolean` || Display legend |

examples/app-showcase/src/system/translations/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ export const ShowcaseTranslationBundle = {
149149
successMessage: 'Portfolio snapshot taken.',
150150
},
151151
},
152+
// Translated at birth for the same reason as `globalActions` above: this
153+
// example is ratcheted at its current untranslated count, so a declared
154+
// label that skips zh-CN widens the debt and fails check-i18n-coverage.
155+
// The gallery's other widget titles predate that ratchet and remain part of
156+
// the frozen baseline. objectui#2945.
157+
dashboards: {
158+
showcase_chart_gallery: {
159+
widgets: {
160+
combo_count_vs_progress: { title: 'Task Count vs Avg Progress' },
161+
},
162+
},
163+
},
152164
},
153165
'zh-CN': {
154166
objects: {
@@ -308,5 +320,12 @@ export const ShowcaseTranslationBundle = {
308320
successMessage: '已生成业务概览。',
309321
},
310322
},
323+
dashboards: {
324+
showcase_chart_gallery: {
325+
widgets: {
326+
combo_count_vs_progress: { title: '任务数与平均进度' },
327+
},
328+
},
329+
},
311330
},
312331
};

0 commit comments

Comments
 (0)