diff --git a/.changeset/21714-report-form-joined-block-dataset-picker.md b/.changeset/21714-report-form-joined-block-dataset-picker.md new file mode 100644 index 00000000000..a1e18766ed5 --- /dev/null +++ b/.changeset/21714-report-form-joined-block-dataset-picker.md @@ -0,0 +1,11 @@ +--- +"@objectstack/spec": patch +--- + +`reportForm`: the "Joined blocks" repeater's `dataset` column now declares `widget: 'ref:dataset'` and `required: true`. Studio's report inspector draws a joined report's block `dataset` as the dataset picker, with the required marker, instead of a free-text cell with no marker. + +Clause-②: no + +- **Why.** A `joined` report refuses a block that binds no `dataset`, at `blocks.N.dataset`. The form offered that column as plain text and did not mark it, so a newly added block failed on save. +- **Which renderer honours it.** The pinned console registers `ref:dataset` in its widget registry. Its repeater takes each column's widget and `required` from the row spec, in both the grid and the card layout. +- ⛔ Only this one form row changes. No schema, parse, export or accept-set change: `JoinedReportBlockSchema.dataset` stays optional on the block shape, and the joined arm's refusal is unchanged. diff --git a/packages/spec/src/ui/report-joined-block-dataset.test.ts b/packages/spec/src/ui/report-joined-block-dataset.test.ts index 71921e4d23f..73462b9739e 100644 --- a/packages/spec/src/ui/report-joined-block-dataset.test.ts +++ b/packages/spec/src/ui/report-joined-block-dataset.test.ts @@ -27,6 +27,7 @@ import { describe, expect, it } from 'vitest'; import { getMetadataTypeSchema } from '../kernel/metadata-type-schemas'; import { MIGRATIONS_BY_MAJOR, RETIRED_KEYS_BY_MAJOR } from '../migrations/registry'; import { ObjectStackDefinitionSchema, defineStack } from '../stack.zod'; +import { reportForm } from './report.form'; import { JoinedReportBlockSchema, ReportSchema, defineReport } from './report.zod'; const ENTRY_ID = 'ui-report-joined-block-dataset-required'; @@ -245,6 +246,22 @@ describe('the joined reports measured at the census parse unchanged', () => { }); }); +describe('the report form offers a block\'s `dataset` as the dataset picker, marked required (#21714)', () => { + it('the "Joined blocks" repeater\'s `dataset` row declares `widget: \'ref:dataset\'` and `required: true`', () => { + // Studio's report inspector renders this row spec: the widget hint picks + // the cell's control, and `required` draws the column's marker and the + // cell's `aria-required`. Without both, a new block is a free-text cell the + // refusal above rejects on save. + const blocks = (reportForm.sections as any[]) + .flatMap((s) => s.fields ?? []) + .find((f: any) => f?.field === 'blocks'); + expect(blocks?.type).toBe('repeater'); + const row = (blocks.fields as any[]).find((f) => f?.field === 'dataset'); + expect(row?.widget).toBe('ref:dataset'); + expect(row?.required).toBe(true); + }); +}); + describe('the ADR-0087 ledger', () => { it('registers one D3 entry at protocol 18, with no D2 conversion, prescribing the binding', () => { const entries = MIGRATIONS_BY_MAJOR[18]!.semantic.filter((e) => e.id === ENTRY_ID); diff --git a/packages/spec/src/ui/report.form.ts b/packages/spec/src/ui/report.form.ts index 37ef33194b0..8bc1038e361 100644 --- a/packages/spec/src/ui/report.form.ts +++ b/packages/spec/src/ui/report.form.ts @@ -82,7 +82,11 @@ export const reportForm = defineForm({ // A `chart` input sat here until #20161: the key was removed from // `JoinedReportBlockSchema` because no renderer ever drew a block // chart, so this control offered a setting that plotted nothing. - { field: 'dataset', label: 'Dataset' }, + // #21714: a joined report refuses a block that binds no `dataset` + // (#21702), so this column declares the `ref:dataset` picker the + // top-level binding declares, marked required: a block is bound + // in the form before save. + { field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }, { field: 'rows', label: 'Rows' }, { field: 'columns', label: 'Columns' }, { field: 'values', label: 'Values' },