Skip to content

Commit db02d47

Browse files
authored
feat(spec)!: ChartInteraction drops zoom and clickAction; stepSize/description/height are delivered (#3752) (#3763)
The tail of the declared-≠-delivered sweep from #3729. Five ChartConfig props reached the renderer and did nothing; each got the ADR-0078 call — honor it, or remove it. Three were honored (objectui#2888), two are removed here. Removed: ChartInteraction.zoom and ChartInteraction.clickAction. Both were redundant against something the platform already delivers, which is why neither had a consumer anywhere in the framework, the console, the showcase or the skill corpus. `zoom` had no renderer primitive behind it and `brush` already narrows a range (migration: brush: true). `clickAction` competed with two click owners that DO work — drillDown, which opens the filtered records and is what a segment click is almost always for, and the react tier's own onSegmentClick; a third, silent owner only invited authors to wire a click that never fired (migration: drillDown, or handle it in React). ChartInteraction is now { tooltips, brush }, both honored — the #1475 precedent: trim what cannot be cleanly delivered, implement the rest, leave nothing inert in between. Delivered: ChartAxis.stepSize, ChartConfig.description and ChartConfig.height (objectui#2888). description and height join <ObjectChart>'s published dataProps now that they do something; stepSize rides along inside xAxis/yAxis. Their schema descriptions say what they do rather than restating their names. Off-spec zoom/clickAction keys are stripped by Zod rather than rejected, so no stored metadata fails to parse — the break is at the TypeScript type level for anyone constructing a ChartInteraction in code. With this, every ChartConfig prop <ObjectChart> publishes is honored by the renderer: the audit that began at #3701 ends with no known declared-≠-delivered props on this surface.
1 parent 558fe4d commit db02d47

7 files changed

Lines changed: 84 additions & 25 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
**BREAKING** `ChartInteraction` drops `zoom` and `clickAction`; `stepSize` / `description` / `height` are delivered (issue #3752)
6+
7+
The tail of the declared-≠-delivered sweep from #3729. Five `ChartConfig` props
8+
reached the renderer and did nothing; each got the ADR-0078 call — honor it, or
9+
remove it. Three were honored (objectui#2885), two are removed here.
10+
11+
**Removed — `ChartInteraction.zoom` and `ChartInteraction.clickAction`.** Both
12+
were redundant against something the platform already delivers, which is why
13+
neither had a consumer anywhere in the framework, the console, the showcase, or
14+
the skill corpus:
15+
16+
- `zoom` had no renderer primitive behind it, and `brush` already narrows a
17+
range. **Migration:** `interaction: { brush: true }`.
18+
- `clickAction` competed with two click owners that *do* work — `drillDown`
19+
(opens the filtered records, which is what a segment click is almost always
20+
for) and, in the react tier, the host's own `onSegmentClick`. A third, silent
21+
owner only invited authors to wire a click that never fired.
22+
**Migration:** `drillDown`, or handle the click in React.
23+
24+
`ChartInteraction` is now `{ tooltips, brush }` — both honored. This follows the
25+
#1475 precedent: trim what cannot be cleanly delivered, implement the rest, and
26+
leave nothing declared-but-inert in between.
27+
28+
**Delivered — `ChartAxis.stepSize`, `ChartConfig.description`, `ChartConfig.height`**
29+
(objectui#2885). `description` and `height` join `<ObjectChart>`'s published
30+
`dataProps` now that they do something; `stepSize` rides along inside
31+
`xAxis`/`yAxis`. Their schema descriptions say what they actually do rather than
32+
restating their names.
33+
34+
Breaking, but shipped as `minor` per the launch-window convention (see
35+
`scripts/check-changeset-no-major.mjs`). Off-spec `zoom`/`clickAction` keys are
36+
stripped by Zod rather than rejected, so no stored metadata fails to parse — the
37+
break is at the TypeScript type level for anyone constructing a
38+
`ChartInteraction` in code.

content/docs/references/ui/chart.mdx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,16 @@ Inline aggregation for an object-bound chart
100100
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
101101
| **title** | `string` | optional | Chart title |
102102
| **subtitle** | `string` | optional | Chart subtitle |
103-
| **description** | `string` | optional | Accessibility description |
103+
| **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) |
106106
| **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 |
107107
| **colors** | `string[] \| Record<string, string>` | optional | Color palette (string[]) or value→color map (`{ value: color }`) |
108-
| **height** | `number` | optional | Fixed height in pixels |
108+
| **height** | `number` | optional | Fixed plot height in pixels (overrides the container default) |
109109
| **showLegend** | `boolean` || Display legend |
110110
| **showDataLabels** | `boolean` || Display data labels |
111111
| **annotations** | `{ type: Enum<'line' \| 'region'>; axis: Enum<'x' \| 'y'>; value: number \| string; endValue?: number \| string; … }[]` | optional | Reference lines/bands drawn over the plot: `{ type: "line" \| "region", axis: "x" \| "y", value, endValue?, color?, label?, style? }` |
112-
| **interaction** | `{ tooltips: boolean; zoom: boolean; brush: boolean; clickAction?: string }` | optional | Interaction toggles: `{ tooltips?, brush?, zoom?, clickAction? }` |
112+
| **interaction** | `{ tooltips: boolean; brush: boolean }` | optional | Interaction toggles: `{ tooltips?, brush? }` |
113113
| **aria** | `{ ariaLabel?: string; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |
114114

115115

@@ -151,9 +151,7 @@ Type: `string`
151151
| Property | Type | Required | Description |
152152
| :--- | :--- | :--- | :--- |
153153
| **tooltips** | `boolean` || Show the hover tooltip |
154-
| **zoom** | `boolean` || Pan/zoom the plot — NOT implemented by the default renderer; use `brush` |
155154
| **brush** | `boolean` || Show the range selector under the plot |
156-
| **clickAction** | `string` | optional | Action ID to trigger on click |
157155

158156

159157
---

content/docs/references/ui/report.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,16 @@ const result = JoinedReportBlock.parse(data);
8282
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
8383
| **title** | `string` | optional | Chart title |
8484
| **subtitle** | `string` | optional | Chart subtitle |
85-
| **description** | `string` | optional | Accessibility description |
85+
| **description** | `string` | optional | Accessibility description — announced to screen readers as the chart’s label |
8686
| **xAxis** | `string` || Dataset dimension name for the X-axis (bound-dataset dimension, not a raw field) |
8787
| **yAxis** | `string` || Dataset measure name for the Y-axis (bound-dataset measure, not a raw field) |
8888
| **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 |
8989
| **colors** | `string[] \| Record<string, string>` | optional | Color palette (string[]) or value→color map (`{ value: color }`) |
90-
| **height** | `number` | optional | Fixed height in pixels |
90+
| **height** | `number` | optional | Fixed plot height in pixels (overrides the container default) |
9191
| **showLegend** | `boolean` || Display legend |
9292
| **showDataLabels** | `boolean` || Display data labels |
9393
| **annotations** | `{ type: Enum<'line' \| 'region'>; axis: Enum<'x' \| 'y'>; value: number \| string; endValue?: number \| string; … }[]` | optional | Reference lines/bands drawn over the plot: `{ type: "line" \| "region", axis: "x" \| "y", value, endValue?, color?, label?, style? }` |
94-
| **interaction** | `{ tooltips: boolean; zoom: boolean; brush: boolean; clickAction?: string }` | optional | Interaction toggles: `{ tooltips?, brush?, zoom?, clickAction? }` |
94+
| **interaction** | `{ tooltips: boolean; brush: boolean }` | optional | Interaction toggles: `{ tooltips?, brush? }` |
9595
| **aria** | `{ ariaLabel?: string; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |
9696

9797

packages/spec/src/ui/chart.zod.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ export const ChartTypeSchema = lazySchema(() => z.enum([
7272
// (`ChartSeries.stack` — series sharing a group id stack, otherwise they
7373
// group). One `bar` family plus a series-level stack group expresses all three
7474
// without multiplying the taxonomy. The renderer honors it (objectui#2880);
75-
// before that it did not, which is why they once sat in the list above. (`metric`/`kpi` are kept as honest single-value
76-
// synonyms; `gauge`/`solid-gauge`/`bullet` render a value today and gain a dial
77-
// when a gauge renderer lands.)
75+
// before that it did not, which is why they once sat in the list above.
76+
//
77+
// `metric`/`kpi` are kept as honest single-value synonyms; `gauge`/
78+
// `solid-gauge`/`bullet` render a value today and gain a dial when a gauge
79+
// renderer lands.
7880

7981
export type ChartType = z.infer<typeof ChartTypeSchema>;
8082

@@ -163,18 +165,23 @@ export const ChartAnnotationSchema = lazySchema(() => z.object({
163165

164166
/**
165167
* Chart Interaction Schema
168+
*
169+
* Both toggles are honored by the renderer. Two former members were removed in
170+
* #3752 rather than left declared-but-inert (ADR-0078; the #1475 trim-vs-
171+
* implement call), because each was redundant against something the platform
172+
* already delivers:
173+
*
174+
* * `zoom` — no renderer had a zoom primitive behind it, and `brush` already
175+
* narrows a range. Migration: `brush: true`.
176+
* * `clickAction` — a chart segment click already has two owners that DO
177+
* work: `drillDown` (opens the filtered records, which is what a segment
178+
* click is almost always for) and, in the react tier, the host's own
179+
* `onSegmentClick`. A third, silent one only invited authors to wire a
180+
* click that never fired. Migration: `drillDown`, or handle it in React.
166181
*/
167182
export const ChartInteractionSchema = lazySchema(() => z.object({
168183
tooltips: z.boolean().default(true).describe('Show the hover tooltip'),
169-
/**
170-
* ⚠️ Declared, not delivered. The default Recharts renderer has no zoom
171-
* primitive, so this draws nothing today — `brush` is the shipped way to
172-
* narrow a range. Kept in the schema because an opt-in renderer can honor it;
173-
* tracked with the rest of the chart-contract work in framework#3729.
174-
*/
175-
zoom: z.boolean().default(false).describe('Pan/zoom the plot — NOT implemented by the default renderer; use `brush`'),
176184
brush: z.boolean().default(false).describe('Show the range selector under the plot'),
177-
clickAction: z.string().optional().describe('Action ID to trigger on click'),
178185
}));
179186

180187
/**
@@ -188,7 +195,7 @@ export const ChartConfigSchema = lazySchema(() => z.object({
188195
/** Titles */
189196
title: I18nLabelSchema.optional().describe('Chart title'),
190197
subtitle: I18nLabelSchema.optional().describe('Chart subtitle'),
191-
description: I18nLabelSchema.optional().describe('Accessibility description'),
198+
description: I18nLabelSchema.optional().describe('Accessibility description — announced to screen readers as the chart’s label'),
192199

193200
/** Axes Mapping */
194201
xAxis: ChartAxisSchema.optional().describe('X-Axis configuration'),
@@ -206,7 +213,7 @@ export const ChartConfigSchema = lazySchema(() => z.object({
206213
z.array(z.string()),
207214
z.record(z.string(), z.string()),
208215
]).optional().describe('Color palette (string[]) or value→color map ({ value: color })'),
209-
height: z.number().optional().describe('Fixed height in pixels'),
216+
height: z.number().optional().describe('Fixed plot height in pixels (overrides the container default)'),
210217

211218
/** Components */
212219
showLegend: z.boolean().default(true).describe('Display legend'),
@@ -218,7 +225,7 @@ export const ChartConfigSchema = lazySchema(() => z.object({
218225

219226
/** Interactions */
220227
interaction: ChartInteractionSchema.optional()
221-
.describe('Interaction toggles: { tooltips?, brush?, zoom?, clickAction? }'),
228+
.describe('Interaction toggles: { tooltips?, brush? }'),
222229

223230
/** ARIA accessibility attributes */
224231
aria: AriaPropsSchema.optional().describe('ARIA accessibility attributes'),

packages/spec/src/ui/react-blocks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export const REACT_BLOCKS: ReactBlockDef[] = [
116116
// react-page wrapper parks an author `type` beside it and the renderer
117117
// reads it back. It is published here as the spec spells it; the internal
118118
// `chartType` spelling is no longer part of the author contract.
119-
dataProps: ['type', 'title', 'subtitle', 'xAxis', 'yAxis', 'series', 'colors', 'showLegend', 'showDataLabels', 'annotations', 'interaction'],
119+
dataProps: ['type', 'title', 'subtitle', 'description', 'height', 'xAxis', 'yAxis', 'series', 'colors', 'showLegend', 'showDataLabels', 'annotations', 'interaction'],
120120
interactions: [
121121
OBJECT_NAME,
122122
{ name: 'filter', type: 'FilterArray', kind: 'controlled', description: 'ObjectQL filter scoping the data; drive from React state.' },

skills/objectstack-ui/contracts/react-blocks.contract.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,20 @@
341341
"required": false,
342342
"description": "Chart subtitle"
343343
},
344+
{
345+
"name": "description",
346+
"type": "string",
347+
"kind": "data",
348+
"required": false,
349+
"description": "Accessibility description — announced to screen readers as the chart’s label"
350+
},
351+
{
352+
"name": "height",
353+
"type": "number",
354+
"kind": "data",
355+
"required": false,
356+
"description": "Fixed plot height in pixels (overrides the container default)"
357+
},
344358
{
345359
"name": "xAxis",
346360
"type": "object",
@@ -395,7 +409,7 @@
395409
"type": "object",
396410
"kind": "data",
397411
"required": false,
398-
"description": "Interaction toggles: { tooltips?, brush?, zoom?, clickAction? }"
412+
"description": "Interaction toggles: { tooltips?, brush? }"
399413
}
400414
]
401415
},

skills/objectstack-ui/references/react-blocks.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,16 @@ Chart over an object’s aggregated data. Bind objectName + aggregate; the axes
7676
| `type` | `'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'` | data || |
7777
| `title` | `string` | data | | Chart title |
7878
| `subtitle` | `string` | data | | Chart subtitle |
79+
| `description` | `string` | data | | Accessibility description — announced to screen readers as the chart’s label |
80+
| `height` | `number` | data | | Fixed plot height in pixels (overrides the container default) |
7981
| `xAxis` | `object` | data | | X-Axis configuration |
8082
| `yAxis` | `object[]` | data | | Y-Axis configuration (support dual axis) |
8183
| `series` | `object[]` | data | | Defined series configuration |
8284
| `colors` | `string[] \| object` | data | | Color palette (string[]) or value→color map ({ value: color }) |
8385
| `showLegend` | `boolean` | data | | Display legend |
8486
| `showDataLabels` | `boolean` | data | | Display data labels |
8587
| `annotations` | `object[]` | data | | Reference lines/bands drawn over the plot: { type: "line" \| "region", axis: "x" \| "y", value, endValue?, color?, label?, style? } |
86-
| `interaction` | `object` | data | | Interaction toggles: { tooltips?, brush?, zoom?, clickAction? } |
88+
| `interaction` | `object` | data | | Interaction toggles: { tooltips?, brush? } |
8789

8890
## `<RecordDetails>``record:details`
8991

0 commit comments

Comments
 (0)