|
14 | 14 | // (e.g. `onSucces` → `onSuccess`) → warning. We do NOT flag arbitrary |
15 | 15 | // unknown props (the contract's data props are a curated subset) — only the |
16 | 16 | // likely typos, to keep false positives near zero. |
17 | | -// - <ObjectChart>'s data BINDINGS, by reading the attribute VALUES (#3701). |
18 | | -// See the block comment above `checkObjectChart` for why that block, and |
19 | | -// why only now. |
| 17 | +// - <ObjectChart>'s data BINDINGS, by reading the attribute VALUES (#3701, |
| 18 | +// retargeted to the spec shape in #3729). See the block comment above |
| 19 | +// `checkObjectChart`. |
20 | 20 | // |
21 | 21 | // Reading values is opt-in per block and per prop: everything below evaluates |
22 | 22 | // only STATIC literals (`objectName="invoice"`, an `aggregate={{…}}` object |
@@ -185,13 +185,20 @@ function attrValue(tsc: typeof ts, sf: ts.SourceFile, attr: ts.JsxAttribute): un |
185 | 185 | // |
186 | 186 | // * `aggregate.field` / `aggregate.groupBy` are RAW FIELD names → check them |
187 | 187 | // against the object's declared fields. |
188 | | -// * `xAxisKey` / `series[].dataKey` are RESULT COLUMN names → check them |
189 | | -// against what this aggregate produces. |
| 188 | +// * the axes are RESULT COLUMN names → check them against what this |
| 189 | +// aggregate produces. |
| 190 | +// |
| 191 | +// The axes are read in the SPEC spelling — `xAxis.field`, `yAxis[].field`, |
| 192 | +// `series[].name` (#3729). #3701 had to read `xAxisKey`/`series[].dataKey` |
| 193 | +// because those were the only spellings the renderer honored; objectui#2880 |
| 194 | +// made it honor ChartConfig, so the gate follows the protocol again. The |
| 195 | +// internal spellings are still accepted, silently: dashboards and the console's |
| 196 | +// own chart-view wiring emit them, and they remain a valid (if unpublished) |
| 197 | +// way to write the same binding. |
190 | 198 |
|
191 | 199 | export const REACT_CHART_FIELD_UNKNOWN = 'react-chart-field-unknown'; |
192 | 200 | export const REACT_CHART_AGGREGATE_INVALID = 'react-chart-aggregate-invalid'; |
193 | 201 | export const REACT_CHART_AXIS_UNKNOWN = 'react-chart-axis-unknown'; |
194 | | -export const REACT_CHART_AXIS_INERT = 'react-chart-axis-inert'; |
195 | 202 |
|
196 | 203 | const CHART_FUNCTIONS = ['count', 'sum', 'avg', 'min', 'max'] as const; |
197 | 204 |
|
@@ -261,21 +268,6 @@ function checkObjectChart( |
261 | 268 | const push = (severity: ReactPropSeverity, rule: string, message: string, hint: string) => |
262 | 269 | findings.push({ severity, rule, where, path, message, hint }); |
263 | 270 |
|
264 | | - // The ChartConfig axis shapes reach this block but are never read by it — |
265 | | - // it consumes `xAxisKey` + `series[].dataKey`. Silently inert props are what |
266 | | - // ADR-0078 forbids, so say so rather than let the chart render blank. |
267 | | - for (const inert of ['xAxis', 'yAxis'] as const) { |
268 | | - if (!values.has(inert)) continue; |
269 | | - push( |
270 | | - 'warning', |
271 | | - REACT_CHART_AXIS_INERT, |
272 | | - `<ObjectChart> prop "${inert}" is not read by this block — it renders nothing.`, |
273 | | - inert === 'xAxis' |
274 | | - ? 'Use xAxisKey="<result column>" (the aggregate\'s groupBy).' |
275 | | - : 'Use series={[{ dataKey: "<result column>" }]} (the aggregate\'s field).', |
276 | | - ); |
277 | | - } |
278 | | - |
279 | 271 | // Inline `data` wins over the aggregate query: the columns then come from |
280 | 272 | // the author's own rows, which this rule cannot see. Nothing further to say. |
281 | 273 | if (values.has('data')) return; |
@@ -353,23 +345,41 @@ function checkObjectChart( |
353 | 345 | ); |
354 | 346 | }; |
355 | 347 |
|
356 | | - axisRef(strOf(values.get('xAxisKey')), 'xAxisKey'); |
| 348 | + // The category axis: spec `xAxis: { field }`, the report surface's bare |
| 349 | + // string, or the internal `xAxisKey`. All three name the same column. |
| 350 | + const xAxisRaw = values.get('xAxis'); |
| 351 | + const categoryAxis = |
| 352 | + strOf(values.get('xAxisKey')) ?? |
| 353 | + strOf(xAxisRaw) ?? |
| 354 | + (isRec(xAxisRaw) ? strOf(xAxisRaw.field) : undefined); |
| 355 | + const categoryProp = values.has('xAxisKey') ? 'xAxisKey' : 'xAxis.field'; |
| 356 | + axisRef(categoryAxis, categoryProp); |
| 357 | + |
| 358 | + // The value axes: spec `yAxis: [{ field }]` (or a single object / bare |
| 359 | + // string) and spec `series: [{ name }]` / internal `series: [{ dataKey }]`. |
| 360 | + const yAxisRaw = values.get('yAxis'); |
| 361 | + const yAxisList = Array.isArray(yAxisRaw) ? yAxisRaw : yAxisRaw !== undefined ? [yAxisRaw] : []; |
| 362 | + for (const a of yAxisList) { |
| 363 | + axisRef(strOf(a) ?? (isRec(a) ? strOf(a.field) : undefined), 'yAxis[].field'); |
| 364 | + } |
| 365 | + |
357 | 366 | const series = values.get('series'); |
358 | 367 | if (Array.isArray(series)) { |
359 | 368 | for (const s of series) { |
360 | | - if (isRec(s)) axisRef(strOf(s.dataKey), 'series[].dataKey'); |
| 369 | + if (!isRec(s)) continue; |
| 370 | + const dataKey = strOf(s.dataKey); |
| 371 | + axisRef(dataKey ?? strOf(s.name), dataKey ? 'series[].dataKey' : 'series[].name'); |
361 | 372 | } |
362 | 373 | } |
363 | 374 |
|
364 | 375 | // A category axis bound to anything but the groupBy is always wrong, and the |
365 | 376 | // check above lets it through when it happens to equal the VALUE column. |
366 | | - const xAxisKey = strOf(values.get('xAxisKey')); |
367 | | - if (xAxisKey && keys.category && xAxisKey !== keys.category && xAxisKey === keys.value) { |
| 377 | + if (categoryAxis && keys.category && categoryAxis !== keys.category && categoryAxis === keys.value) { |
368 | 378 | push( |
369 | 379 | 'error', |
370 | 380 | REACT_CHART_AXIS_UNKNOWN, |
371 | | - `xAxisKey "${xAxisKey}" is the aggregate's VALUE column, not its category column.`, |
372 | | - `The category axis is keyed by groupBy — use xAxisKey="${keys.category}".`, |
| 381 | + `${categoryProp} "${categoryAxis}" is the aggregate's VALUE column, not its category column.`, |
| 382 | + `The category axis is keyed by groupBy — bind it to "${keys.category}".`, |
373 | 383 | ); |
374 | 384 | } |
375 | 385 | } |
|
0 commit comments