Skip to content

Commit 23771ef

Browse files
authored
Merge pull request #1692 from objectstack-ai/feat-trim-charttype-schema
feat(spec)!: trim ChartTypeSchema to distinctly-renderable families
2 parents 038ecc7 + 1c83ee8 commit 23771ef

6 files changed

Lines changed: 71 additions & 51 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"@objectstack/spec": major
3+
---
4+
5+
BREAKING: `ChartTypeSchema` drops 8 variant types that only rendered as their
6+
base chart, so the taxonomy now advertises only families the renderer draws
7+
distinctly.
8+
9+
Removed: `grouped-bar`, `stacked-bar`, `bi-polar-bar` (→ bar — no multi-series
10+
grouping/stacking), `stacked-area` (→ area), `step-line`, `spline` (→ line),
11+
`pyramid` (→ funnel), `bubble` (→ scatter — no size encoding).
12+
13+
Kept: bar / horizontal-bar / column, line / area, pie / donut / funnel, scatter,
14+
treemap / sankey, radar, table / pivot, and the single-value performance family
15+
(metric / kpi / gauge / solid-gauge / bullet — these render an honest value
16+
today and gain a dial when a gauge renderer lands).
17+
18+
Migration: a widget/series using a removed type should switch to its base
19+
(`stacked-bar``bar`, `spline``line`, `pyramid``funnel`, `bubble``scatter`,
20+
etc.). These can return via an opt-in renderer once a real renderer + data model
21+
backs them.

.objectui-sha

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
76094e0d4ed79b8732ba9879c75aa5ab8283180d
1+
ec530a92ba2cdb6dbb0a1f5ee3b800503296e2f1

content/docs/references/ui/chart.mdx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const result = ChartAnnotation.parse(data);
6969

7070
| Property | Type | Required | Description |
7171
| :--- | :--- | :--- | :--- |
72-
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'grouped-bar' \| 'stacked-bar' \| 'bi-polar-bar' \| 'line' \| 'area' \| 'stacked-area' \| 'step-line' \| 'spline' \| 'pie' \| 'donut' \| 'funnel' \| 'pyramid' \| 'scatter' \| 'bubble' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
72+
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'line' \| 'area' \| 'pie' \| 'donut' \| 'funnel' \| 'scatter' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` || |
7373
| **title** | `string` | optional | Chart title |
7474
| **subtitle** | `string` | optional | Chart subtitle |
7575
| **description** | `string` | optional | Accessibility description |
@@ -109,7 +109,7 @@ const result = ChartAnnotation.parse(data);
109109
| :--- | :--- | :--- | :--- |
110110
| **name** | `string` || Field name or series identifier |
111111
| **label** | `string` | optional | Series display label |
112-
| **type** | `Enum<'bar' \| 'horizontal-bar' \| 'column' \| 'grouped-bar' \| 'stacked-bar' \| 'bi-polar-bar' \| 'line' \| 'area' \| 'stacked-area' \| 'step-line' \| 'spline' \| 'pie' \| 'donut' \| 'funnel' \| 'pyramid' \| 'scatter' \| 'bubble' \| 'treemap' \| 'sankey' \| 'gauge' \| 'solid-gauge' \| 'metric' \| 'kpi' \| 'bullet' \| 'radar' \| 'table' \| 'pivot'>` | optional | Override chart type for this series |
112+
| **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 |
113113
| **color** | `string` | optional | Series color (hex/rgb/token) |
114114
| **stack** | `string` | optional | Stack identifier to group series |
115115
| **yAxis** | `Enum<'left' \| 'right'>` || Bind to specific Y-Axis |
@@ -127,20 +127,12 @@ const result = ChartAnnotation.parse(data);
127127
* `bar`
128128
* `horizontal-bar`
129129
* `column`
130-
* `grouped-bar`
131-
* `stacked-bar`
132-
* `bi-polar-bar`
133130
* `line`
134131
* `area`
135-
* `stacked-area`
136-
* `step-line`
137-
* `spline`
138132
* `pie`
139133
* `donut`
140134
* `funnel`
141-
* `pyramid`
142135
* `scatter`
143-
* `bubble`
144136
* `treemap`
145137
* `sankey`
146138
* `gauge`

examples/app-showcase/test/coverage.test.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,14 @@ describe('showcase coverage (introspected against the spec)', () => {
4949
});
5050

5151
it('covers every distinctly-renderable ChartType', () => {
52-
// The Chart Gallery demonstrates only chart families the renderer draws
53-
// DISTINCTLY — advertising a type that renders as something else (a "sankey"
54-
// that draws bars) is worse than not offering it. The families below have no
55-
// distinct renderer yet and fall back to a near-relative, so the showcase
56-
// intentionally does not duplicate them: grouped/stacked/bi-polar bars (→bar),
57-
// stacked-area (→area), step-line/spline (→line), pyramid (→funnel), bubble
58-
// (→scatter), and the dial-less performance variants kpi/gauge/solid-gauge/
59-
// bullet (→ the same KPI value as `metric`). Follow-up: trim these from
60-
// `ChartTypeSchema` so spec ↔ renderer ↔ showcase stay in lockstep.
61-
const FALLBACK_ONLY = new Set([
62-
'grouped-bar', 'stacked-bar', 'bi-polar-bar', 'stacked-area', 'step-line',
63-
'spline', 'pyramid', 'bubble', 'kpi', 'gauge', 'solid-gauge', 'bullet',
64-
]);
65-
const expected = enumValues(ui.ChartTypeSchema).filter((t) => !FALLBACK_ONLY.has(t));
52+
// The fallback-only VARIANTS (grouped/stacked/bi-polar bar, stacked-area,
53+
// step-line, spline, pyramid, bubble) were removed from `ChartTypeSchema`, so
54+
// the enum now lists only families that render. The remaining exception is
55+
// the performance group: `metric` represents the single-value KPI, and
56+
// `kpi`/`gauge`/`solid-gauge`/`bullet` render the SAME value today (no dial),
57+
// so the gallery demonstrates `metric` once rather than duplicating them.
58+
const SAME_AS_METRIC = new Set(['kpi', 'gauge', 'solid-gauge', 'bullet']);
59+
const expected = enumValues(ui.ChartTypeSchema).filter((t) => !SAME_AS_METRIC.has(t));
6660
const used = new Set<string>();
6761
for (const w of ChartGalleryDashboard.widgets ?? []) if (w.type) used.add(w.type);
6862
expectFullCoverage('ChartType', expected, used);

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ import {
88

99
describe('ChartTypeSchema', () => {
1010
it('should accept all comparison chart types', () => {
11-
const types = ['bar', 'horizontal-bar', 'column', 'grouped-bar', 'stacked-bar'] as const;
12-
11+
const types = ['bar', 'horizontal-bar', 'column'] as const;
12+
1313
types.forEach(type => {
1414
expect(() => ChartTypeSchema.parse(type)).not.toThrow();
1515
});
1616
});
1717

1818
it('should accept all trend chart types', () => {
19-
const types = ['line', 'area', 'stacked-area', 'step-line'] as const;
20-
19+
const types = ['line', 'area'] as const;
20+
2121
types.forEach(type => {
2222
expect(() => ChartTypeSchema.parse(type)).not.toThrow();
2323
});
@@ -32,8 +32,8 @@ describe('ChartTypeSchema', () => {
3232
});
3333

3434
it('should accept all relationship chart types', () => {
35-
const types = ['scatter', 'bubble'] as const;
36-
35+
const types = ['scatter'] as const;
36+
3737
types.forEach(type => {
3838
expect(() => ChartTypeSchema.parse(type)).not.toThrow();
3939
});
@@ -72,6 +72,17 @@ describe('ChartTypeSchema', () => {
7272
});
7373
});
7474

75+
it('should reject variant types that only render as their base chart', () => {
76+
// Removed: each fell back to a base family the renderer already draws, so
77+
// advertising them lied about the output (see the taxonomy NOTE in chart.zod).
78+
const fallbackOnly = ['grouped-bar', 'stacked-bar', 'bi-polar-bar', 'stacked-area',
79+
'step-line', 'spline', 'pyramid', 'bubble'] as const;
80+
81+
fallbackOnly.forEach(type => {
82+
expect(() => ChartTypeSchema.parse(type)).toThrow();
83+
});
84+
});
85+
7586
it('should accept all tabular chart types', () => {
7687
const types = ['table', 'pivot'] as const;
7788

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

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,25 @@ export const ChartTypeSchema = lazySchema(() => z.enum([
2020
'bar',
2121
'horizontal-bar',
2222
'column',
23-
'grouped-bar',
24-
'stacked-bar',
25-
'bi-polar-bar',
26-
23+
2724
// Trend
2825
'line',
2926
'area',
30-
'stacked-area',
31-
'step-line',
32-
'spline',
33-
27+
3428
// Distribution
3529
'pie',
3630
'donut',
3731
'funnel',
38-
'pyramid',
39-
32+
4033
// Relationship
4134
'scatter',
42-
'bubble',
43-
35+
4436
// Composition
4537
'treemap',
4638
'sankey',
4739

48-
// Performance
40+
// Performance (single value — metric/kpi render a number; gauge/solid-gauge/
41+
// bullet are honest single-value variants pending a real dial/target renderer)
4942
'gauge',
5043
'solid-gauge',
5144
'metric',
@@ -60,13 +53,22 @@ export const ChartTypeSchema = lazySchema(() => z.enum([
6053
'pivot',
6154
]));
6255

63-
// NOTE: chart families that require data shapes the platform does not model
64-
// (OHLC for candlestick/stock, per-record distributions for box-plot/violin),
65-
// geographic data (choropleth/bubble-map/gl-map), or dependencies the default
66-
// Recharts renderer lacks (sunburst, heatmap, word-cloud, waterfall) were
67-
// removed from this taxonomy: advertising a chart type the renderer can't draw
68-
// is worse than not offering it. They can return via an opt-in renderer plugin
69-
// once there is real demand and a data model to back them.
56+
// NOTE: the taxonomy lists only chart families the default Recharts renderer
57+
// draws DISTINCTLY. Two groups are intentionally absent:
58+
//
59+
// 1. Families requiring data/dependencies the platform does not model — OHLC
60+
// (candlestick/stock), per-record distributions (box-plot/violin), geo
61+
// (choropleth/bubble-map/gl-map), or extra renderers (sunburst, heatmap,
62+
// word-cloud, waterfall).
63+
// 2. VARIANTS that only render as their base chart, so advertising them lies
64+
// about the output: grouped-bar / stacked-bar / bi-polar-bar (→ bar, no
65+
// multi-series grouping/stacking), stacked-area (→ area), step-line / spline
66+
// (→ line), pyramid (→ funnel), bubble (→ scatter, no size encoding).
67+
//
68+
// Both can return via an opt-in renderer once there is a real renderer and a
69+
// data model to back them. (`metric`/`kpi` are kept as honest single-value
70+
// synonyms; `gauge`/`solid-gauge`/`bullet` render a value today and gain a dial
71+
// when a gauge renderer lands.)
7072

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

0 commit comments

Comments
 (0)