@object-ui/plugin-charts 16.0.0 → 17.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,450 @@
1
1
  # @object-ui/plugin-charts
2
2
 
3
+ ## 17.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f1c04b6: fix(charts): a spec `series[].type` override actually draws, and a spec-shape `series` plots at all (#2945)
8
+
9
+ #2945 listed `combo` (`plugin-charts`) as renderer-local dialect to "promote or
10
+ delete". Neither: the spec **already models a combo chart**, per-series, and its
11
+ own field comment says so —
12
+
13
+ ```ts
14
+ // spec/src/ui/chart.zod.ts — ChartSeriesSchema
15
+ /** Series type override (combo charts) */
16
+ type: ChartTypeSchema.optional().describe('Override chart type for this series'),
17
+ ```
18
+
19
+ — exactly as it models stacking with `ChartSeries.stack` rather than a
20
+ `stacked-bar` family. So `combo` is not a name an author should reach for; it is
21
+ what "the series disagree about their family" looks like from the renderer's
22
+ side. `effectiveChartFamily` now derives it, and `combo` stays a documented
23
+ renderer-local marker (internal callers pass it directly today).
24
+
25
+ Chasing that turned up two live bugs, both silent, on the path a spec author
26
+ takes.
27
+
28
+ **1. The per-series override was parsed, carried, and then dropped.** Only the
29
+ renderer's `chartType === 'combo'` branch read `series[].chartType`, so
30
+
31
+ ```ts
32
+ { type: 'bar', series: [{ name: 'revenue' }, { name: 'margin', type: 'line' }] }
33
+ ```
34
+
35
+ drew `margin` as a bar. Nothing was wrong at any layer but the last — a unit test
36
+ even asserted the value was carried.
37
+
38
+ **2. A spec-shape `series` rendered nothing at all.** `series` is the one binding
39
+ both shapes spell with the same key, so `ChartRenderer`'s blanket "internal props
40
+ win" rule let the author's `[{ name }]` shadow the normalized `[{ dataKey }]` and
41
+ reach a renderer that reads `dataKey`. Blank chart. Every other spec binding has a
42
+ distinct name (`xAxis` vs `xAxisKey`), which is why only this one broke — and why
43
+ the isolated normalization tests all passed over a dead path. The raw array is now
44
+ preferred only when it already speaks the internal shape, so internal callers are
45
+ byte-for-byte unchanged and a mixed array works too.
46
+
47
+ **Also fixed in passing:** the combo branch had an `area` arm under a `BarChart`
48
+ container, and Recharts renders an `<Area>` child of `BarChart` as nothing — so an
49
+ authored combo with an `area` series drew a blank series. The container is now
50
+ `ComposedChart`, which is what Recharts provides for mixed marks.
51
+
52
+ Widening only. A chart whose series all resolve to one family keeps its own
53
+ family, an explicit `combo` is untouched, and a family with no per-series meaning
54
+ (`pie`, `horizontal-bar`, …) is never widened. A derived combo binds series to the
55
+ left axis unless one asks for `yAxis: 'right'` — the spec's own default — so
56
+ widening changes the series' mark and not its scale; the legacy bar→left/line→right
57
+ guess is kept for an authored `combo`, where it was historically the only way to
58
+ reach a second axis.
59
+
60
+ Guards: `effectiveChartFamily` / `comboBaseFamily` are unit-tested over the whole
61
+ family matrix; DOM-level tests assert the **marks** rather than the derived family,
62
+ since the carry was already covered and the drawing was what broke; and
63
+ `spec-derived-unions.test.ts` asserts `combo` is absent from the spec's
64
+ `ChartTypeSchema`, so the day it is adopted upstream the derivation is named as the
65
+ thing to retire.
66
+
67
+ ### Patch Changes
68
+
69
+ - Updated dependencies [62311b6]
70
+ - Updated dependencies [fc0272a]
71
+ - Updated dependencies [9e7349e]
72
+ - Updated dependencies [8864971]
73
+ - Updated dependencies [1cf0de7]
74
+ - Updated dependencies [752e18f]
75
+ - Updated dependencies [c785740]
76
+ - Updated dependencies [b41f401]
77
+ - Updated dependencies [19e9fa0]
78
+ - Updated dependencies [d61efd1]
79
+ - Updated dependencies [95b7214]
80
+ - Updated dependencies [7d9734d]
81
+ - Updated dependencies [6ae818e]
82
+ - Updated dependencies [9eb932b]
83
+ - Updated dependencies [746dd00]
84
+ - Updated dependencies [aebfa4f]
85
+ - Updated dependencies [38ca8be]
86
+ - Updated dependencies [3cb9646]
87
+ - Updated dependencies [68ef584]
88
+ - Updated dependencies [4952edf]
89
+ - Updated dependencies [7f0252e]
90
+ - Updated dependencies [c4d7b20]
91
+ - Updated dependencies [c769d3d]
92
+ - Updated dependencies [7639a61]
93
+ - Updated dependencies [94e63ef]
94
+ - Updated dependencies [c735bf7]
95
+ - Updated dependencies [02aef0c]
96
+ - Updated dependencies [6f29aa5]
97
+ - Updated dependencies [d21794c]
98
+ - Updated dependencies [c4db402]
99
+ - Updated dependencies [5319bf1]
100
+ - Updated dependencies [49e5671]
101
+ - Updated dependencies [9a04d25]
102
+ - Updated dependencies [b5b97e2]
103
+ - Updated dependencies [f59f2c1]
104
+ - Updated dependencies [07de839]
105
+ - Updated dependencies [2a40b5e]
106
+ - Updated dependencies [df613fa]
107
+ - Updated dependencies [4874117]
108
+ - Updated dependencies [ad0183a]
109
+ - Updated dependencies [ce08d55]
110
+ - Updated dependencies [eb4b740]
111
+ - Updated dependencies [5b084eb]
112
+ - Updated dependencies [aa1240a]
113
+ - Updated dependencies [2374a49]
114
+ - Updated dependencies [390c071]
115
+ - Updated dependencies [d10f526]
116
+ - Updated dependencies [2d5d594]
117
+ - Updated dependencies [ea7f477]
118
+ - Updated dependencies [379728f]
119
+ - Updated dependencies [7f23cd0]
120
+ - Updated dependencies [0ded602]
121
+ - Updated dependencies [24e0e0a]
122
+ - Updated dependencies [f8a95e5]
123
+ - Updated dependencies [3a6cf24]
124
+ - Updated dependencies [aa35561]
125
+ - Updated dependencies [03bd53b]
126
+ - Updated dependencies [3c1f321]
127
+ - Updated dependencies [a045a32]
128
+ - Updated dependencies [912496d]
129
+ - Updated dependencies [80edbd4]
130
+ - Updated dependencies [9867281]
131
+ - @object-ui/core@17.1.0
132
+ - @object-ui/components@17.1.0
133
+ - @object-ui/react@17.1.0
134
+ - @object-ui/types@17.1.0
135
+ - @object-ui/i18n@17.1.0
136
+
137
+ ## 17.0.0
138
+
139
+ ### Minor Changes
140
+
141
+ - aa88056: feat(charts): honor `ChartAxis.stepSize`, `ChartConfig.description` and `ChartConfig.height` (framework#3752)
142
+
143
+ The tail of the declared-≠-delivered sweep from framework#3729 / #2880. Three
144
+ `ChartConfig` props reached the renderer and did nothing:
145
+
146
+ - **`ChartAxis.stepSize`** — Recharts has no "a tick every N units" prop
147
+ (`tickCount` is a hint it may ignore, `interval` is for categorical axes), so
148
+ honoring a step means handing it the tick array outright. `ticksFor` builds it
149
+ from the axis's own `min`/`max` where declared and from the plotted values
150
+ otherwise, so a step works with or without a pinned domain. A data-derived max
151
+ rounds UP to the next step (otherwise the topmost bar sits above the last
152
+ gridline and the axis reads as truncated); an explicit `max` clamps instead,
153
+ since a tick outside a pinned domain would be drawn outside the plot. A step
154
+ that would produce more than 200 ticks is refused rather than rendered — that
155
+ is a wrong config, and drawing it would hang the page instead of surfacing the
156
+ mistake.
157
+ - **`ChartConfig.description`** — the accessibility description. A chart is a
158
+ picture to a screen reader; the container now carries `role="img"` +
159
+ `aria-label`. Without a description it stays an ordinary div, because
160
+ stamping `role="img"` on an _unlabelled_ graphic is worse than leaving one a
161
+ screen reader can skip.
162
+ - **`ChartConfig.height`** — was read only by the legacy `ChartBarRenderer`, not
163
+ by the advanced path that draws every real chart. Now applied to the chart
164
+ container as an inline style, which beats its default `h-[350px]` class.
165
+
166
+ `height` and `description` ride on the shared container props, so they apply to
167
+ all eight chart families rather than one branch.
168
+
169
+ - 9b53d72: feat(charts): ObjectChart honors the spec `ChartConfig` author shape (objectui#2880 / framework#3729)
170
+
171
+ `ChartConfigSchema` is the chart protocol, but the renderer only ever read a
172
+ Recharts-flavoured internal shape — `chartType`, `xAxisKey`, `series[].dataKey`.
173
+ Everything an author wrote in the SPEC shape reached the renderer and was
174
+ silently dropped, which is exactly what ADR-0078 forbids. framework#3725
175
+ documented the gap by trimming the published contract down to the props that
176
+ actually worked; this closes it the other way round.
177
+
178
+ **S1 — one normalization boundary.** `normalizeChartSchema` translates the
179
+ author shape into the internal pipeline contract in a single place, rather than
180
+ scattering `??` fallbacks through the render tree (framework PD #12: one
181
+ translation is a contract mapping, N fallbacks are a second dialect):
182
+
183
+ - `type` → `chartType`, `xAxis: { field }` → `xAxisKey`, `series: [{ name }]` →
184
+ `series: [{ dataKey }]`
185
+ - the report surface's bare-string `xAxis`/`yAxis` resolve too
186
+ - `yAxis: [{ field }]` alone plots, with no `series` declared
187
+ - **internal props win**, so `DashboardRenderer`, `ObjectView` and the dataset
188
+ path are byte-for-byte unaffected — there is no migration
189
+
190
+ **The `type` collision.** `ChartConfig.type` is the chart family, but on any
191
+ surface that flattens chart config into a props bag `type` is already the SDUI
192
+ envelope's component discriminator. Spreading props last let an author's
193
+ `type="bar"` replace `object-chart` so the block stopped resolving; stamping the
194
+ discriminator last ate the author's value instead. The react-page wrapper now
195
+ keeps both: the discriminator wins the `type` slot and the author's value is
196
+ preserved beside it as `specType`, which the normalizer reads back.
197
+
198
+ **S2 — axis presentation.** `ChartAxis.format` drives the tick formatter (via
199
+ `Intl.NumberFormat`, no new dependency), `min`/`max` pin the domain,
200
+ `logarithmic` swaps the scale, `title` labels the axis, and `showGridLines` is
201
+ honored. A second `yAxis` entry (or `position: 'right'`) turns on the secondary
202
+ axis that `series[].yAxis` binds to — in combo charts an explicit binding now
203
+ beats the family-derived bar→left/line→right guess. `showLegend` is honored,
204
+ and `title`/`subtitle` render above the plot instead of only titling the
205
+ drill-down drawer.
206
+
207
+ **S3 — `series[].stack`, `annotations`, `interaction`.** Stacking passes the
208
+ author's group name through as Recharts' `stackId`. Annotations render as
209
+ `ReferenceLine` (`type: 'line'`) / `ReferenceArea` (`type: 'region'`) with the
210
+ declared axis, colour, style and label. `interaction.tooltips: false` suppresses
211
+ the hover card and `interaction.brush: true` adds the range selector;
212
+ `showDataLabels` prints values on the marks. `interaction.zoom` has no Recharts
213
+ primitive behind it and is deliberately still unimplemented rather than faked.
214
+
215
+ ### Patch Changes
216
+
217
+ - 7b35e4b: fix(dashboard,charts): resolve `{current_user_id}` in widget filters (framework #3574)
218
+
219
+ A dashboard widget filtered on `{current_user_id}` rendered `0`. The token
220
+ reached SQL as a literal, matched no row, and nothing was logged on the client
221
+ or the server — a silent zero that reads as "you have no work" rather than
222
+ "this filter did not resolve". The same token in a list-view filter resolved
223
+ correctly, so a user-scoped list and a user-scoped widget over the same data
224
+ disagreed.
225
+
226
+ There was no shared resolver. Three ad-hoc implementations had grown up
227
+ independently — `ObjectView` for list views, `ObjectDataPage` for URL filter
228
+ triples, `NavigationRenderer` for hrefs — and each understood only the filter
229
+ shape its own surface used. `ObjectView`'s opened with
230
+ `if (!Array.isArray(filter)) return filter`, so it could not have been reused
231
+ by dashboard widgets even in principle: widget filters are MongoDB-style
232
+ objects. Widgets therefore got no resolution at all — `DatasetWidget` called
233
+ `resolveDateMacros` and nothing else, which is why `{today}` worked in a widget
234
+ and `{current_user_id}` silently did not.
235
+
236
+ - **`@object-ui/core`** — new `utils/filter-tokens.ts` with
237
+ `resolveContextTokens` and `resolveFilterPlaceholders`. The latter expands
238
+ _every_ placeholder vocabulary in one call and is what surfaces should use;
239
+ resolving only some of them is the whole defect. The walk handles arrays and
240
+ plain objects uniformly, so one resolver covers both platform filter shapes.
241
+ - **`@object-ui/react`** — new `FilterScopeProvider` / `useFilterScope`. The
242
+ renderer packages deliberately do not depend on `@object-ui/auth`, so the
243
+ shell supplies the session values. This is a separate context from
244
+ `PredicateScopeContext`, which is the expression evaluation scope and carries
245
+ no organization.
246
+ - **`@object-ui/plugin-dashboard` / `@object-ui/plugin-charts`** — all six
247
+ widgets that previously resolved date macros only now resolve both
248
+ vocabularies: `DatasetWidget`, `ObjectMetricWidget`, `ObjectDataTable`,
249
+ `ObjectPivotTable`, and `ObjectChart` (dataset-bound and inline paths). The
250
+ chart's `compareTo` comparison filter gets the session pass too — otherwise
251
+ the overlay series silently ignored the owner clause the primary series
252
+ honoured.
253
+ - **`@object-ui/app-shell`** — `ObjectView`'s local `substituteFilterTokens`
254
+ and `ObjectDataPage`'s inline `=== '{current_user_id}'` ternary now delegate
255
+ to the shared resolver, so both also gain `{current_org_id}` and date macros.
256
+ Two of the three ad-hoc implementations are gone rather than joined by a
257
+ fourth.
258
+
259
+ An unresolvable token is left intact rather than dropped: leaving it yields an
260
+ empty result, whereas dropping the clause would _widen_ the result set and show
261
+ a signed-out viewer everyone's data. It is no longer silent — the resolver
262
+ warns, naming the token, and suggests the intended spelling for known
263
+ near-misses (`{current_user}`, `{user_id}`, `{organization_id}`). Authoring-time
264
+ enforcement lands separately as `filter-token-unknown` in `@objectstack/lint`.
265
+
266
+ - e16ed2d: fix(dashboard,charts): send widget `dateGranularity`/`sortBy`/`limit` to the query, and give funnels a real stage order (framework#3588)
267
+
268
+ `DatasetWidget` never read `widget.options`. Four keys an author writes there
269
+ change the query the server compiles, so a widget declaring
270
+ `options: { dateGranularity: 'month' }` grouped by the raw timestamp and drew
271
+ one bar per record, and `sortBy`/`sortOrder` produced no ordering at all.
272
+
273
+ - `DatasetWidget` lowers `options.dateGranularity`, `options.sortBy` +
274
+ `options.sortOrder`, and `options.limit` into the `DatasetSelection` it posts.
275
+ A `sortBy` naming something the widget does not project is dropped rather than
276
+ sent, so a stale sort key left by an edit degrades to "unordered" instead of
277
+ failing the widget against the server's stricter validation. These keys also
278
+ join the refetch signature, so editing one in the designer refetches instead
279
+ of re-rendering the previous grid.
280
+ - Funnel stages follow a **declared order**. `AdvancedChartImpl` sorted funnel
281
+ segments by value descending, unconditionally — which overrode any server
282
+ ordering and rendered a sales pipeline as a tidy narrowing shape whatever the
283
+ stages' real sequence, hiding the very anomaly (a bulge at Proposal) the chart
284
+ exists to show. It now honours a `categoryOrder`, which `DatasetWidget`
285
+ derives from the dimension field's picklist option order — the pipeline order
286
+ an author already declared on the object — or from an explicit
287
+ `options.stageOrder`. With no declared order the value-descending default is
288
+ unchanged, and a category missing from the order is kept (after the declared
289
+ ones), never dropped.
290
+ - New `@object-ui/core` helpers `buildCategoryOrder` / `buildCategoryRank`,
291
+ keyed by both the stored value and the display label like the existing
292
+ `buildOptionColorMap`, so ordering works whether or not the server resolved
293
+ the dimension's labels.
294
+
295
+ Requires the framework-side fix in objectstack#3588 for the selection keys to
296
+ take effect server-side.
297
+
298
+ - 6e8fd3c: fix(charts): a fieldless `count` aggregate keyed its value column `undefined`, so the chart plotted nothing (framework#3701)
299
+
300
+ framework#3701 pinned down what an OBJECT-bound chart aggregate names its result
301
+ columns — the raw field names it was given (`groupBy` for the category, `field`
302
+ for the value; no `sum_`-style decoration, unlike a dataset measure), plus the
303
+ literal `count` when a `count` omits `field`, which is the alias the engine
304
+ projects `COUNT(*)` under. `os validate` now lints page sources against that
305
+ convention, so the paths that build these rows have to honour it exactly.
306
+
307
+ Three of the four did. The odd one out was `count` — the one function that may
308
+ legitimately omit `field` — because every row builder read `params.field`
309
+ directly:
310
+
311
+ - `aggregateRecords` / `ObjectDataSource.aggregateClientSide` emitted
312
+ `{ [groupBy]: key, [undefined]: value }`, i.e. a column literally named
313
+ `undefined` that no axis binding could ever name;
314
+ - the legacy analytics path was worse: it remapped the server's `count` measure
315
+ onto `params.field` and **deleted** the original key, so the value the server
316
+ did return was thrown away before the chart saw it.
317
+
318
+ All of them now resolve the column through one helper (`aggregateValueKey`) so a
319
+ fieldless count lands under `count`, matching the framework contract. The
320
+ comparison-overlay column is derived from the same key (`count__comparison`
321
+ instead of `undefined__comparison`), and `aggregate.field` is typed optional to
322
+ match the spec's `ChartAggregateSchema`. Charts that name a field are unchanged.
323
+
324
+ - Updated dependencies [7b21891]
325
+ - Updated dependencies [0b3be01]
326
+ - Updated dependencies [3c4d935]
327
+ - Updated dependencies [4b60d2d]
328
+ - Updated dependencies [952b978]
329
+ - Updated dependencies [de5e40c]
330
+ - Updated dependencies [1a03af6]
331
+ - Updated dependencies [3e886eb]
332
+ - Updated dependencies [cfc675e]
333
+ - Updated dependencies [20df08c]
334
+ - Updated dependencies [1767124]
335
+ - Updated dependencies [8ecf5a6]
336
+ - Updated dependencies [af705b9]
337
+ - Updated dependencies [0502a7c]
338
+ - Updated dependencies [7b35e4b]
339
+ - Updated dependencies [8fb1295]
340
+ - Updated dependencies [e16ed2d]
341
+ - Updated dependencies [c6fd752]
342
+ - Updated dependencies [f9bbddb]
343
+ - Updated dependencies [dfd3705]
344
+ - Updated dependencies [c77108c]
345
+ - Updated dependencies [2735de6]
346
+ - Updated dependencies [c19ac11]
347
+ - Updated dependencies [6dee2cb]
348
+ - Updated dependencies [e05f052]
349
+ - Updated dependencies [0502a7c]
350
+ - Updated dependencies [faad45e]
351
+ - Updated dependencies [09c6a17]
352
+ - Updated dependencies [c7cff19]
353
+ - Updated dependencies [ba73a02]
354
+ - Updated dependencies [cd09a7b]
355
+ - Updated dependencies [f1abf0e]
356
+ - Updated dependencies [f05b84e]
357
+ - Updated dependencies [9b4b952]
358
+ - Updated dependencies [2f947e4]
359
+ - Updated dependencies [7d46648]
360
+ - Updated dependencies [9b53d72]
361
+ - Updated dependencies [bb4aa25]
362
+ - Updated dependencies [75f1cdf]
363
+ - Updated dependencies [662bdf9]
364
+ - Updated dependencies [059a052]
365
+ - Updated dependencies [53642d4]
366
+ - Updated dependencies [8aae006]
367
+ - Updated dependencies [c6cfdf1]
368
+ - Updated dependencies [d147a13]
369
+ - Updated dependencies [c6aaed8]
370
+ - Updated dependencies [263f885]
371
+ - Updated dependencies [dc334da]
372
+ - @object-ui/components@17.0.0
373
+ - @object-ui/i18n@17.0.0
374
+ - @object-ui/react@17.0.0
375
+ - @object-ui/types@17.0.0
376
+ - @object-ui/core@17.0.0
377
+
378
+ ## 16.1.0
379
+
380
+ ### Patch Changes
381
+
382
+ - ebe6494: chore(lint): clear the baseline lint errors in nine more packages (objectui#2713 Wave 2)
383
+
384
+ Second wave of the #2713 lint-gate restoration (after #2730). These nine package
385
+ lints were red at baseline on `main`, so their per-package `lint` gate could not
386
+ catch new violations. Cleared every **error** (no behavior change; warnings out
387
+ of scope):
388
+
389
+ - **`react-hooks/rules-of-hooks`** (`i18n`, `plugin-grid`, `plugin-view`,
390
+ `plugin-list`) — translation helpers (`useSafeFieldLabel`,
391
+ `useRowActionTranslation`, `useViewLabel`, `useViewTabLabel`, `useMoreLabel`)
392
+ wrapped a provider-safe hook (`useObjectTranslation`/`useObjectLabel`, which
393
+ never throw) in try/catch; removed the wrapper (the same fix #2709 applied in
394
+ fields). `plugin-kanban` `ObjectKanban` moved its `if (error)` early return
395
+ below the `useCallback` so hooks run unconditionally. `collaboration`
396
+ `__unsafe_usePresenceContext` keeps its deliberate danger-prefix name via a
397
+ justified scoped disable.
398
+ - **`react-hooks/static-components`** (`layout`, `plugin-list`, `plugin-report`)
399
+ — dynamic-icon / registry lookups (`resolveIcon`, `useRegistryComponent`) are
400
+ stable component references, not components created during render → scoped
401
+ disable with justification. `plugin-charts` `TreemapCell` was a _genuine_
402
+ inline component and is hoisted to module scope (it is purely props-driven).
403
+ - **`no-irregular-whitespace`** (`plugin-grid` `ImportWizard`) — the literal
404
+ U+FEFF BOM prepended to exported CSV/text blobs (so Excel detects UTF-8) is
405
+ now written as the `` escape: byte-identical at runtime, no literal
406
+ irregular-whitespace character in source.
407
+ - **`no-useless-assignment`** (`plugin-grid` `BulkActionDialog`) — dropped a
408
+ dead `= null` initializer that the exhaustive `switch` (incl. `default`)
409
+ overwrites before it is read.
410
+ - **`no-unsafe-function-type`** (`plugin-view` `ViewTabBar`) — the dnd-kit
411
+ render-prop `listeners` map is typed `Record<string, (...args: any[]) => void>`
412
+ instead of bare `Function`.
413
+ - **`no-require-imports`** (`plugin-kanban`, `plugin-view` tests) — hoisted
414
+ `vi.mock` factories use an `async` factory with `await import('react')`.
415
+
416
+ - Updated dependencies [0318118]
417
+ - Updated dependencies [1c8935a]
418
+ - Updated dependencies [af1b0db]
419
+ - Updated dependencies [8b8b744]
420
+ - Updated dependencies [7cf4051]
421
+ - Updated dependencies [803558e]
422
+ - Updated dependencies [2e7d7f0]
423
+ - Updated dependencies [ef14f69]
424
+ - Updated dependencies [94d4876]
425
+ - Updated dependencies [1100a8b]
426
+ - Updated dependencies [7abe4cd]
427
+ - Updated dependencies [69fa5d1]
428
+ - Updated dependencies [549c67d]
429
+ - Updated dependencies [ebe6494]
430
+ - Updated dependencies [2b17339]
431
+ - Updated dependencies [31b77d4]
432
+ - Updated dependencies [6d4fbe6]
433
+ - Updated dependencies [0a3710b]
434
+ - Updated dependencies [f80aaf2]
435
+ - Updated dependencies [62b9ab5]
436
+ - Updated dependencies [1629313]
437
+ - Updated dependencies [29c6040]
438
+ - Updated dependencies [faebac3]
439
+ - Updated dependencies [2331ac9]
440
+ - Updated dependencies [199fa83]
441
+ - Updated dependencies [eee4ded]
442
+ - @object-ui/i18n@16.1.0
443
+ - @object-ui/core@16.1.0
444
+ - @object-ui/types@16.1.0
445
+ - @object-ui/react@16.1.0
446
+ - @object-ui/components@16.1.0
447
+
3
448
  ## 16.0.0
4
449
 
5
450
  ### Patch Changes
@@ -359,7 +804,7 @@
359
804
  `ObjectView` emit the dataset shape. The legacy inline aggregate is kept as a
360
805
  deprecated fallback so pre-ADR-0021 metadata keeps rendering.
361
806
 
362
- Refs objectstack-ai/framework#1890
807
+ Refs objectstack-ai/objectstack#1890
363
808
 
364
809
  - ab168e4: Dashboard charts no longer render blank on first paint. Recharts'
365
810
  `ResponsiveContainer` was a child of a `flex … justify-center` box, so it
@@ -1,4 +1,5 @@
1
1
  import { ChartConfig } from './ChartContainerImpl';
2
+ import { NormalizedAxis, NormalizedSeries } from './normalizeChartSchema';
2
3
  /**
3
4
  * ObjectUI
4
5
  * Copyright (c) 2024-present ObjectStack Inc.
@@ -8,18 +9,20 @@ import { ChartConfig } from './ChartContainerImpl';
8
9
  */
9
10
  import * as React from "react";
10
11
  export interface AdvancedChartImplProps {
12
+ /**
13
+ * Chart family. `combo` is renderer-local and rarely needs to be passed:
14
+ * series declaring different families derive it (`effectiveChartFamily`),
15
+ * which is how `@objectstack/spec` expresses a combo chart.
16
+ */
11
17
  chartType?: 'bar' | 'column' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'donut' | 'radar' | 'scatter' | 'funnel' | 'combo' | 'treemap' | 'sankey';
12
18
  data?: Array<Record<string, any>>;
13
19
  config?: ChartConfig;
14
20
  xAxisKey?: string;
15
- series?: Array<{
16
- dataKey: string;
17
- chartType?: 'bar' | 'line' | 'area';
18
- variant?: 'current' | 'comparison';
19
- opacity?: number;
20
- dashArray?: string;
21
- label?: string;
22
- }>;
21
+ /**
22
+ * Plotted series, in the renderer's internal shape. Authors write the spec
23
+ * `series: [{ name, stack, yAxis, … }]`; `normalizeChartSchema` translates.
24
+ */
25
+ series?: NormalizedSeries[];
23
26
  className?: string;
24
27
  /** Categorical/series colour palette. Overrides the theme's `--chart-1..n`
25
28
  * defaults so a page/dashboard can brand its charts (data-screens). */
@@ -33,6 +36,20 @@ export interface AdvancedChartImplProps {
33
36
  * categories absent here fall back to the positional palette, then the theme.
34
37
  */
35
38
  categoryColors?: Record<string, string>;
39
+ /**
40
+ * Declared category order for ordered-sequence charts (funnel / pyramid),
41
+ * keyed like {@link categoryColors} by the category VALUE or its display
42
+ * LABEL, listed in domain order.
43
+ *
44
+ * A funnel's shape asserts a sequence, so without this the renderer can only
45
+ * guess at one and sorts by value descending. That is right for a generic
46
+ * "biggest first" funnel and wrong for a sales pipeline, where the stages
47
+ * have a declared order (Qualification → Needs Analysis → Proposal →
48
+ * Negotiation) that a healthy pipeline happens to narrow along — and an
49
+ * unhealthy one does not. When supplied, this order wins; categories not
50
+ * listed keep their incoming relative order after the listed ones.
51
+ */
52
+ categoryOrder?: string[];
36
53
  /**
37
54
  * Optional drill-down click handler. Fires when a chart segment is clicked
38
55
  * with `{ category, series, value }`. Wired for bar/horizontal-bar/line/
@@ -43,6 +60,40 @@ export interface AdvancedChartImplProps {
43
60
  series?: string;
44
61
  value?: number;
45
62
  }) => void;
63
+ /**
64
+ * Spec `ChartAxis` presentation for the category axis — `format` (tick
65
+ * formatter), `title`, `showGridLines`. Its `field` already arrived as
66
+ * {@link AdvancedChartImplProps.xAxisKey}. Resolved by
67
+ * `normalizeChartSchema`, so the renderer never parses the author shape.
68
+ */
69
+ xAxis?: NormalizedAxis;
70
+ /**
71
+ * Spec `ChartConfig.yAxis` — one entry per value axis, in declaration order.
72
+ * Index 0 is the primary axis; a second entry (or one with
73
+ * `position: 'right'`) turns on the secondary axis that `series[].yAxis`
74
+ * binds to. Carries `min`/`max` (domain), `format` (ticks), `logarithmic`
75
+ * (scale) and `title`.
76
+ */
77
+ yAxes?: NormalizedAxis[];
78
+ /** Spec `ChartConfig.showLegend`. Omitted → shown (the schema default). */
79
+ showLegend?: boolean;
80
+ /** Spec `ChartConfig.showDataLabels` — print each point's value on the mark. */
81
+ showDataLabels?: boolean;
82
+ /** Spec `ChartConfig.title` / `.subtitle`, rendered above the plot. */
83
+ title?: string;
84
+ subtitle?: string;
85
+ /**
86
+ * Spec `ChartConfig.description` — the accessibility description. A chart is
87
+ * a picture to a screen reader; without this it announces as an unlabelled
88
+ * graphic, so the container carries it as `role="img"` + `aria-label`.
89
+ */
90
+ description?: string;
91
+ /** Spec `ChartConfig.height` — fixed plot height in pixels. */
92
+ height?: number;
93
+ /** Spec `ChartConfig.annotations` — reference lines / bands. */
94
+ annotations?: Array<Record<string, any>>;
95
+ /** Spec `ChartConfig.interaction` — `tooltips`, `brush`. */
96
+ interaction?: Record<string, any>;
46
97
  /**
47
98
  * Disable Recharts' entrance animation when `false`. Animations drive the
48
99
  * reveal via `requestAnimationFrame`, which browsers throttle/pause in
@@ -55,7 +106,10 @@ export interface AdvancedChartImplProps {
55
106
  isAnimationActive?: boolean;
56
107
  }
57
108
  /**
58
- * AdvancedChartImpl - The heavy implementation that imports Recharts with full features
59
- * This component is lazy-loaded to avoid including Recharts in the initial bundle
109
+ * Public entry point. A THIN wrapper purely so the unreadable-data guard can
110
+ * short-circuit without breaking the rules of hooks: the condition depends on
111
+ * `data`, which arrives asynchronously, so an early return inside the renderer
112
+ * would change its hook count between renders. The wrapper's own hook list is
113
+ * fixed, and the renderer below is reached with data it can actually plot.
60
114
  */
61
- export default function AdvancedChartImpl({ chartType: rawChartType, data: rawData, config, xAxisKey, series, className, colors, categoryColors, onChartClick, isAnimationActive, }: AdvancedChartImplProps): React.JSX.Element;
115
+ export default function AdvancedChartImpl(props: AdvancedChartImplProps): React.JSX.Element;
@@ -23,9 +23,16 @@ export type ChartConfig = {
23
23
  theme: Record<keyof typeof THEMES, string>;
24
24
  });
25
25
  };
26
- declare function ChartContainer({ id, className, children, config, ...props }: React.ComponentProps<"div"> & {
26
+ declare function ChartContainer({ id, className, children, config, disableSettleRemount, ...props }: React.ComponentProps<"div"> & {
27
27
  config: ChartConfig;
28
28
  children: React.ComponentProps<typeof ResponsiveContainer>["children"];
29
+ /**
30
+ * Skip the settle re-mount below. Set by callers that render their series with
31
+ * `isAnimationActive={false}` (e.g. dashboard charts, see #2756): with no
32
+ * entrance-animation tween there is nothing to "heal", so re-mounting would
33
+ * only cost a needless 1-frame ResponsiveContainer reflow on first paint.
34
+ */
35
+ disableSettleRemount?: boolean;
29
36
  }): React.JSX.Element;
30
37
  declare const ChartStyle: ({ id, config }: {
31
38
  id: string;
@@ -23,7 +23,19 @@ export interface ChartRendererProps {
23
23
  chartType?: 'bar' | 'column' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'donut' | 'radar' | 'scatter' | 'funnel' | 'combo' | 'treemap' | 'sankey';
24
24
  data?: Array<Record<string, any>>;
25
25
  config?: Record<string, any>;
26
+ /** Internal binding. Authors write the spec `xAxis: { field }` (or the
27
+ * report surface's bare string); `normalizeChartSchema` resolves both. */
26
28
  xAxisKey?: string;
29
+ /**
30
+ * Plotted series — **both shapes**, unlike the bindings below.
31
+ *
32
+ * `series` is the one binding the spec and the internal contract spell with
33
+ * the same key, so declaring only the internal shape here made
34
+ * `series: [{ name: 'total' }]` a type error on an author who was writing
35
+ * the protocol correctly — and, until #2945, one whose chart also rendered
36
+ * blank. `normalizeChartSchema` translates; an array that already speaks the
37
+ * internal shape passes through untouched.
38
+ */
27
39
  series?: Array<{
28
40
  dataKey: string;
29
41
  label?: string;
@@ -31,12 +43,46 @@ export interface ChartRendererProps {
31
43
  opacity?: number;
32
44
  dashArray?: string;
33
45
  chartType?: 'bar' | 'line' | 'area';
46
+ stack?: string;
47
+ yAxis?: 'left' | 'right';
48
+ color?: string;
49
+ } | {
50
+ name: string;
51
+ label?: unknown;
52
+ type?: string;
53
+ variant?: 'current' | 'comparison' | 'primary';
54
+ opacity?: number;
55
+ dashArray?: string;
56
+ stack?: string;
57
+ yAxis?: 'left' | 'right';
58
+ color?: string;
34
59
  }>;
60
+ /** Spec `ChartConfig` shape — honored via `normalizeChartSchema`
61
+ * (objectui#2880). Listed here so the author-facing contract type-checks;
62
+ * the internal props above win when both are present. */
63
+ xAxis?: unknown;
64
+ yAxis?: unknown;
65
+ showLegend?: boolean;
66
+ showDataLabels?: boolean;
67
+ title?: unknown;
68
+ subtitle?: unknown;
69
+ description?: unknown;
70
+ height?: number;
71
+ annotations?: Array<Record<string, any>>;
72
+ interaction?: Record<string, any>;
73
+ /** An author `type` rescued from the SDUI envelope's discriminator
74
+ * collision by the react-page wrapper — see `normalizeChartSchema`. */
75
+ specType?: string;
35
76
  colors?: string[];
36
77
  /** Per-category colour map (value/label → colour). Wins over `colors` per
37
78
  * category; see AdvancedChartImpl. Set by ObjectChart from select/lookup
38
79
  * dimension option colours and/or an explicit author map. */
39
80
  categoryColors?: Record<string, string>;
81
+ /** Declared category order (value/label keys, in domain order) for
82
+ * ordered-sequence charts — funnel/pyramid stages. Set by DatasetWidget
83
+ * from the dimension's picklist order or an explicit `stageOrder`; see
84
+ * AdvancedChartImpl. Omitted → the funnel's value-descending default. */
85
+ categoryOrder?: string[];
40
86
  /** Pass `false` for a deterministic, export-safe render with no entrance
41
87
  * animation (see AdvancedChartImpl). Omitted → animated default. */
42
88
  isAnimationActive?: boolean;