Repository navigation
feat(app-shell): a row-table page block lists the rows a page api binding answers - #31883
Conversation
…ding answers The sixth product-page block: rows from a list (or a whole hole naming one in the bound data), declared columns with path, label and a text, datetime or enum format, every row drawn, an empty label, and the binding's error panel for a list it cannot read. Its strict @object-ui/types arm lands with it, and the cli's known types are regenerated. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTx4LkMSXMTUGZuzQW
…nding's panel The object-binding error panel marks a consumer of ElementDataSourceGate, which an api-bound product block is not. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jTx4LkMSXMTUGZuzQW
|
domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo Contract reviewServed-tier: This is the lane seat's own review of record, owed by the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
|
domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo 席位核对 · ACCEPT · 2026-11-09T09:40Z · head
|
Requested by Steedos · project thread
Fixes #31856
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: none — skills/ teaches no product block (git grep tile-grid|usage-bar skills/ = 0)
维护者速读
row-table,把页面api绑定读到的一组数据画成表格:列在元数据里声明(字段路径、标题、格式:文本 / 日期时间 / 枚举标签),每一行都画,没有数据时显示空状态文字。@objectstack/spec,SDUI 清单不动。回滚即还原本 PR。ui/packages/app-shell/src/console/blocks/productBlocks.tsx(readRowTable的拒收判定与rowTableCell)、ui/packages/types/src/zod/product-blocks.zod.ts(RowTableColumnSchema的严格校验)、ui/packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts(命名空间计数 306→307)。pnpm --dir ui exec vitest run packages/app-shell/src/console/blocks/__tests__/rowTable-31856.test.tsx packages/types/src/__tests__/product-block-arms-12197.test.ts,预期两个文件全绿。What changes
@object-ui/app-shell: therow-tableblock, the sixth product-page block besidehero,stat,usage-bar,checklistandtile-grid, registered under theapp-shellnamespace with the barerow-tableas its authorable key and the sameAPI_BINDING_INPUT.rows: a list, or one whole${…}hole naming a list in the bounddata, astile-grid'sitems. Every row is drawn, in order, with no cap.columns:{ path, label, format?, enum? }, drawn in the order written. The keys areresultDialog.fields' vocabulary (path/label/format,textshared);datetimeandenumare this block's own formats.pathis read per row withselectElementApiPath(@object-ui/react), the binding's own path reader.textdraws as written (an object as compact JSON);datetimegoes throughformatDateTime(value, { locale: useDisplayLocale(), style: 'compact' })from@object-ui/core, thedatetimefield cell's face, in the display zone the host declared, else the viewer's;enumdraws the labelenum: { value: label }names, and a value the map does not name as written.nullor''draws—. An empty list drawsemptyLabel, else the existingtable.noRowskey. No new i18n key.rowshole that resolves to anything but a list, or a column the block cannot draw (nopath, an unknownformat,enumwith no map), draws an error face with the binding's own title (element.component.apiUnreadable) and a sentence naming the input. Never the empty state.@object-ui/types: its arm.RowTableBlockSchemaandRowTableColumnSchema, with the TS twinsRowTableBlockSchema,RowTableBlockPropsandRowTableColumn, joiningProductBlockComponentSchema, soobjectui validateclaims the type. The arm is strict: an unknownformat, anenumcolumn with no map, a map on a column that is notenum, apaththat is not a dotted path, an undeclared key (the card's prose wordheaderincluded) and an emptycolumnsare refused.dataSourceis the spec'sElementApiDataSourceSchema, by reference. The mirror-parity pin registers both pairs (199 → 201) as spec-derived.@object-ui/cli:known-schema-types.tsregenerated (row-table,app-shell:row-table). The 10859 ratchet's namespaced count moves 306 → 307 forapp-shell:row-table, a named stopgap raise of that one key, written beside the objectui#13301 raise it follows: the namespaced twin is refused attypeby design so the bare key stays the one authorable spelling. BothproductBlocks-namespace-13301lists (app-shell and cli) gainrow-table.row-tableexample,content/docs/frontend/data-source.mdxandcontent/docs/ui/pages.mdx(the block lists that take theapibinding).@objectstack/specchange. The type parses throughPageComponentSchema.type's open string arm, andcomponent-type-unknownclaims only colon namespaces. The SDUI manifest does not move:@object-ui/app-shellis not one of the manifest generator's registry modules (node scripts/check-sdui-manifest.mjs: exit 0, 106 components).Tests
rowTable-31856.test.tsx(new, eight cases, through the realSchemaRendererand the hostapiFetch): the registration's inputs; every bound row in authored column order; enum labels and an unnamed value drawn raw; datetime in a pinned display zone (Asia/Tokyo) equal toformatDateTime's face;—for a missing and anullfield; the authored empty label and the bundle's; a non-list hole draws the error face and not the empty state; an unknown format draws the error face.product-block-arms-12197.test.ts: therow-tablefixture joins the shared accept / content-channel / undeclared-prop loop, plus sixrow-tablecases (literal rows, unknown format, enum without a map and a map without enum, a non-dotted path,header, empty columns and a partial hole).Runs (branch on 1ff1d45; head 5e6d804 merges main a57a007, which moves no `packages/spec` or `ui/packages` file, so the runs below stand)
pnpm --dir ui exec vitest run packages/app-shell/src/console/blocks/:Test Files 4 passed (4),Tests 39 passed (39)(re-run after the last source commit).pnpm --dir ui exec vitest run packages/app-shell/src/console/:Test Files 267 passed (267),Tests 2038 passed (2038).pnpm --dir ui exec vitest run packages/types/:Test Files 389 passed (389),Tests 10489 passed (10489);pnpm --filter @object-ui/types run type-check(the mirror-parity type legs): exit 0.pnpm --dir ui exec vitest run packages/cli/:Test Files 31 passed (31),Tests 420 passed (420).pnpm --filter @object-ui/app-shell run type-check(tsc --noEmitandtsc -p tsconfig.test.json, test files included): exit 0.pnpm --dir ui exec vitest run scripts/:Test Files 170 passed | 2 skipped (172),Tests 5027 passed | 2 skipped.check:spec-symbols,check:component-surface-parity,check:element-data-source-declaration,check:registry-bare-names,check:doc-types,check:doc-snippets(537 of 537 blocks),check:doc-examples,check:i18n-keys,check:i18n-drift,check:new-line-citations,check:control-bytes.dispatch-gates --commands: 69 families derived, 69 run, each exit 0 (--ran: 0 NOT-MEASURED, 0 UNRUN; the list is identical after the merge of main).check:skill-examplesfirst answered exit 3 (client SDK unbuilt) and was re-run green after building it;check:query-options-erasurewas re-run green with a longer timeout.Ablation (scripts/ablation-replace.mjs, restored to the HEAD blob)
const rows = props.rows;→const rows = Array.isArray(props.rows) ? props.rows : [];(the non-array guard made an empty list): exactly "refuses a hole that resolves to a non-list with the error panel, never the empty state" goes red,1 failed | 7 passed; restored,git diff HEADempty.Acceptance notes
element:textrows with this block) and cloud#4058 (the AI call trace page). Neither is in this repo; no page here places the block yet.os validatedoes not judge the block'sproperties, as for every product block: there is noComponentPropsMaprow, so the strict arm is judged byobjectui validate/safeValidateSchema. The seat ruled this a family question for all six blocks, not this card's.ElementDataSourceErrorPanel: that panel marks a consumer of the object-binding gate (check:element-data-source-declarationrefused the first draft), which anapi-bound product block is not. It uses the same classes,role="alert"and the binding's own title key.check:sdui-registration-pins(it weighs a built console bundle; CI's Build & E2E UI builds it).check:type-check-debtran under the verify lock: exit 0.