|
| 1 | +--- |
| 2 | +'@objectstack/spec': minor |
| 3 | +'@objectstack/lint': minor |
| 4 | +--- |
| 5 | + |
| 6 | +`<ObjectChart>` aggregate result-column naming is now a contract, and its axis bindings are validated (issue #3701) |
| 7 | + |
| 8 | +Split out of #3583 Phase 2 (#3684), which extended ADR-0021 axis checking to |
| 9 | +report charts, list-view charts, and dataset-bound page chart components but had |
| 10 | +to leave the react `<ObjectChart>` block out: it is OBJECT-bound (`objectName` + |
| 11 | +an inline `aggregate`), `aggregate` existed in the contract only as the |
| 12 | +description string `'{ field, function, groupBy }'`, and nothing in the repo said |
| 13 | +what the aggregated result columns were called. Without that, `xAxis`/`yAxis` had |
| 14 | +nothing to resolve against, and guessing a convention would have manufactured |
| 15 | +false positives (ADR-0072 D1). |
| 16 | + |
| 17 | +**The convention, recorded rather than invented.** Every path that can serve an |
| 18 | +object-bound chart already agreed — the engine's structured-`groupBy` aggregate |
| 19 | +(whose alias objectui sets to `field || function`), the legacy analytics query |
| 20 | +(which remaps its measure key back to `field`), the client-side fallback, and the |
| 21 | +console's own chart-view wiring (`xAxisKey: groupBy`, `series[].dataKey: field`). |
| 22 | +`packages/spec/src/ui/chart-aggregate.ts` writes it down and exports it: |
| 23 | + |
| 24 | +* an object-bound aggregate returns rows keyed by the **raw field names** — |
| 25 | + `groupBy` for the category column, `field` for the value column, the literal |
| 26 | + `count` for a fieldless count, plus `<field>__comparison` under a comparison |
| 27 | + overlay; |
| 28 | +* `chartAggregateCategoryKey` / `chartAggregateValueKey` / `chartAggregateResultKeys` |
| 29 | + derive those columns so producers and checkers cannot re-derive them apart; |
| 30 | +* `ChartAggregateSchema` replaces the description string with a real Zod schema |
| 31 | + and rejects a non-`count` function with no `field` (which used to reach the |
| 32 | + renderer as `sum(undefined)` and render blank). |
| 33 | + |
| 34 | +This is the deliberate opposite of the dataset path, whose rows are keyed by the |
| 35 | +declared measure `name` (`sum_amount`) — the trap `chart-measure-unknown` catches. |
| 36 | +Only the dataset path has an author-chosen name to key by. |
| 37 | + |
| 38 | +**`<ObjectChart>`'s contract now names the props it actually reads.** The block |
| 39 | +consumes `xAxisKey` and `series[].dataKey`; `ChartConfig`'s `xAxis`/`yAxis`/`series` |
| 40 | +shapes reached it and were silently dropped, which ADR-0078 forbids. They are |
| 41 | +removed from the block's `dataProps`; `chartType`, `xAxisKey`, and `series` are |
| 42 | +declared in the React overlay where the other bindings live. |
| 43 | + |
| 44 | +**`validate-react-page-props` now reads attribute VALUES**, not just names, for |
| 45 | +`<ObjectChart>`: |
| 46 | + |
| 47 | +* `react-chart-field-unknown` (error) — `aggregate.field` / `aggregate.groupBy` |
| 48 | + naming a field the bound object does not declare; |
| 49 | +* `react-chart-aggregate-invalid` (error) — an unimplemented aggregation |
| 50 | + function, or a non-`count` function with nothing to aggregate; |
| 51 | +* `react-chart-axis-unknown` (error) — `xAxisKey` / `series[].dataKey` naming a |
| 52 | + column the aggregate does not return (including a dataset-style `sum_total`), |
| 53 | + or a category axis bound to the value column; |
| 54 | +* `react-chart-axis-inert` (warning) — the `xAxis` / `yAxis` shapes this block |
| 55 | + never reads. |
| 56 | + |
| 57 | +Value reading is opt-in per block and evaluates only static literals: a prop |
| 58 | +driven by React state or a variable, a usage carrying a `{...spread}`, a chart |
| 59 | +given inline `data`, and objects another package defines are all skipped |
| 60 | +silently — an unresolvable binding is not a wrong one. |
0 commit comments