Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .changeset/chartconfig-trim-zoom-clickaction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
'@objectstack/spec': minor
---

**BREAKING** `ChartInteraction` drops `zoom` and `clickAction`; `stepSize` / `description` / `height` are delivered (issue #3752)

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#2885), 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:** `interaction: { brush: true }`.
- `clickAction` competed with two click owners that *do* work — `drillDown`
(opens the filtered records, which is what a segment click is almost always
for) and, in the react tier, the host's own `onSegmentClick`. A third, silent
owner only invited authors to wire a click that never fired.
**Migration:** `drillDown`, or handle the click in React.

`ChartInteraction` is now `{ tooltips, brush }` — both honored. This follows the
#1475 precedent: trim what cannot be cleanly delivered, implement the rest, and
leave nothing declared-but-inert in between.

**Delivered — `ChartAxis.stepSize`, `ChartConfig.description`, `ChartConfig.height`**
(objectui#2885). `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 actually do rather than
restating their names.

Breaking, but shipped as `minor` per the launch-window convention (see
`scripts/check-changeset-no-major.mjs`). 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.
8 changes: 3 additions & 5 deletions content/docs/references/ui/chart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,16 @@ Inline aggregation for an object-bound 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` | optional | Chart title |
| **subtitle** | `string` | optional | Chart subtitle |
| **description** | `string` | optional | Accessibility description |
| **description** | `string` | optional | Accessibility description — announced to screen readers as the chart’s label |
| **xAxis** | `{ field: string; title?: string; format?: string; min?: number; … }` | optional | X-Axis configuration |
| **yAxis** | `{ field: string; title?: string; format?: string; min?: number; … }[]` | optional | Y-Axis configuration (support dual axis) |
| **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 |
| **colors** | `string[] \| Record<string, string>` | optional | Color palette (string[]) or value→color map (`{ value: color }`) |
| **height** | `number` | optional | Fixed height in pixels |
| **height** | `number` | optional | Fixed plot height in pixels (overrides the container default) |
| **showLegend** | `boolean` | ✅ | Display legend |
| **showDataLabels** | `boolean` | ✅ | Display data labels |
| **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? }` |
| **interaction** | `{ tooltips: boolean; zoom: boolean; brush: boolean; clickAction?: string }` | optional | Interaction toggles: `{ tooltips?, brush?, zoom?, clickAction? }` |
| **interaction** | `{ tooltips: boolean; brush: boolean }` | optional | Interaction toggles: `{ tooltips?, brush? }` |
| **aria** | `{ ariaLabel?: string; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |


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


---
Expand Down
6 changes: 3 additions & 3 deletions content/docs/references/ui/report.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,16 @@ const result = JoinedReportBlock.parse(data);
| **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` | optional | Chart title |
| **subtitle** | `string` | optional | Chart subtitle |
| **description** | `string` | optional | Accessibility description |
| **description** | `string` | optional | Accessibility description — announced to screen readers as the chart’s label |
| **xAxis** | `string` | ✅ | Dataset dimension name for the X-axis (bound-dataset dimension, not a raw field) |
| **yAxis** | `string` | ✅ | Dataset measure name for the Y-axis (bound-dataset measure, not a raw field) |
| **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 |
| **colors** | `string[] \| Record<string, string>` | optional | Color palette (string[]) or value→color map (`{ value: color }`) |
| **height** | `number` | optional | Fixed height in pixels |
| **height** | `number` | optional | Fixed plot height in pixels (overrides the container default) |
| **showLegend** | `boolean` | ✅ | Display legend |
| **showDataLabels** | `boolean` | ✅ | Display data labels |
| **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? }` |
| **interaction** | `{ tooltips: boolean; zoom: boolean; brush: boolean; clickAction?: string }` | optional | Interaction toggles: `{ tooltips?, brush?, zoom?, clickAction? }` |
| **interaction** | `{ tooltips: boolean; brush: boolean }` | optional | Interaction toggles: `{ tooltips?, brush? }` |
| **aria** | `{ ariaLabel?: string; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |


Expand Down
35 changes: 21 additions & 14 deletions packages/spec/src/ui/chart.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ export const ChartTypeSchema = lazySchema(() => z.enum([
// (`ChartSeries.stack` — series sharing a group id stack, otherwise they
// group). One `bar` family plus a series-level stack group expresses all three
// without multiplying the taxonomy. The renderer honors it (objectui#2880);
// before that it did not, which is why they once sat in the list above. (`metric`/`kpi` are kept as honest single-value
// synonyms; `gauge`/`solid-gauge`/`bullet` render a value today and gain a dial
// when a gauge renderer lands.)
// before that it did not, which is why they once sat in the list above.
//
// `metric`/`kpi` are kept as honest single-value synonyms; `gauge`/
// `solid-gauge`/`bullet` render a value today and gain a dial when a gauge
// renderer lands.

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

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

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

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

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

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

/** Interactions */
interaction: ChartInteractionSchema.optional()
.describe('Interaction toggles: { tooltips?, brush?, zoom?, clickAction? }'),
.describe('Interaction toggles: { tooltips?, brush? }'),

/** ARIA accessibility attributes */
aria: AriaPropsSchema.optional().describe('ARIA accessibility attributes'),
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/src/ui/react-blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export const REACT_BLOCKS: ReactBlockDef[] = [
// react-page wrapper parks an author `type` beside it and the renderer
// reads it back. It is published here as the spec spells it; the internal
// `chartType` spelling is no longer part of the author contract.
dataProps: ['type', 'title', 'subtitle', 'xAxis', 'yAxis', 'series', 'colors', 'showLegend', 'showDataLabels', 'annotations', 'interaction'],
dataProps: ['type', 'title', 'subtitle', 'description', 'height', 'xAxis', 'yAxis', 'series', 'colors', 'showLegend', 'showDataLabels', 'annotations', 'interaction'],
interactions: [
OBJECT_NAME,
{ name: 'filter', type: 'FilterArray', kind: 'controlled', description: 'ObjectQL filter scoping the data; drive from React state.' },
Expand Down
16 changes: 15 additions & 1 deletion skills/objectstack-ui/contracts/react-blocks.contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,20 @@
"required": false,
"description": "Chart subtitle"
},
{
"name": "description",
"type": "string",
"kind": "data",
"required": false,
"description": "Accessibility description — announced to screen readers as the chart’s label"
},
{
"name": "height",
"type": "number",
"kind": "data",
"required": false,
"description": "Fixed plot height in pixels (overrides the container default)"
},
{
"name": "xAxis",
"type": "object",
Expand Down Expand Up @@ -395,7 +409,7 @@
"type": "object",
"kind": "data",
"required": false,
"description": "Interaction toggles: { tooltips?, brush?, zoom?, clickAction? }"
"description": "Interaction toggles: { tooltips?, brush? }"
}
]
},
Expand Down
4 changes: 3 additions & 1 deletion skills/objectstack-ui/references/react-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,16 @@ Chart over an object’s aggregated data. Bind objectName + aggregate; the axes
| `type` | `'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'` | data | ✓ | |
| `title` | `string` | data | | Chart title |
| `subtitle` | `string` | data | | Chart subtitle |
| `description` | `string` | data | | Accessibility description — announced to screen readers as the chart’s label |
| `height` | `number` | data | | Fixed plot height in pixels (overrides the container default) |
| `xAxis` | `object` | data | | X-Axis configuration |
| `yAxis` | `object[]` | data | | Y-Axis configuration (support dual axis) |
| `series` | `object[]` | data | | Defined series configuration |
| `colors` | `string[] \| object` | data | | Color palette (string[]) or value→color map ({ value: color }) |
| `showLegend` | `boolean` | data | | Display legend |
| `showDataLabels` | `boolean` | data | | Display data labels |
| `annotations` | `object[]` | data | | Reference lines/bands drawn over the plot: { type: "line" \| "region", axis: "x" \| "y", value, endValue?, color?, label?, style? } |
| `interaction` | `object` | data | | Interaction toggles: { tooltips?, brush?, zoom?, clickAction? } |
| `interaction` | `object` | data | | Interaction toggles: { tooltips?, brush? } |

## `<RecordDetails>` — `record:details`

Expand Down
Loading