Repository navigation
feat(components): the edit form names the fields a visible update action would require, before it is pressed - #28459
Conversation
…ld require on the edit form An update action with a static patch writes it over the record, and the server runs requiredWhen over the result. The edit form judged the record as it stands, so a field only the action makes required drew no mark and the first press was refused. The form now reads the object's declarative update actions, evaluates each field's requiredWhen over the record merged with the patch, and notes the action under an empty field that comes out required. A heads-up only: Save is never blocked. Co-Authored-By: Claude <noreply@anthropic.com>
…n's patch would require Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…s effect setState Co-Authored-By: Claude <noreply@anthropic.com>
…uiredForAction Co-Authored-By: Claude <noreply@anthropic.com>
|
domain:ui#4 · os-sales · session_01L6bhgWqjXeTcS3jtug46Dv 维护者速读
席位意见:ACCEPT。按分诊 6541943879 的 A 路线实现:纯 ui,不加 spec 键,不改服务端。动作读取走数据源已有的 席位核对 — ACCEPT · domain:ui#4 · Head-sha
|
…pec export Co-Authored-By: Claude <noreply@anthropic.com>
|
domain:ui#4 · os-sales · session_01L6bhgWqjXeTcS3jtug46Dv 维护者速读
席位意见:ACCEPT,钉到新 head。 席位核对 — ACCEPT · domain:ui#4 · Head-sha
|
…requiredWhen With no requiredWhen on the form there is nothing an action's patch could make required, so the form makes no read beyond the ones its host made. Keeps the hosts' one-schema-read pins. Co-Authored-By: Claude <noreply@anthropic.com>
|
domain:ui#4 · os-sales · session_01L6bhgWqjXeTcS3jtug46Dv 维护者速读
席位意见:ACCEPT,钉到新 head。 席位核对 — ACCEPT · domain:ui#4 · Head-sha
|
Requested by Sales · project thread
Fixes #28382
Clause-②: yes (widening:
@object-ui/i18ngains theform.requiredForActionkey in all ten locales; no accept set moves)Metadata-protocol: none
Branch note:
claude/project-thread-3lhyz5is the branch the platform assigned to this card thread (claim 6546347620 names it), not the repo'sclaude/issue-N-slugconvention.维护者速读
requiredWhen,而表单只看当前记录,所以第一次点「Submit for Sign-off」总会被拒(Issued On is required),事前没有任何提示。ui/packages/components/src/renderers/form/form.tsx的actionOutcomes块和renderFieldDescription调用处;ui/packages/components/src/renderers/form/actionRequirements.ts(哪些动作计入)。cd ui && pnpm exec vitest run packages/components/src/renderers/form/__tests__/form-required-for-action-28382.test.tsx,预期 12 条全过;或在 showcase 打开一张 draft 发票点 Edit,Issued On 下出现该提示。What changes
Measured on
mainf91ab5a0before any code, with the showcase (objectstack dev --seed-admin) and the console (vite, proxied):mainPOST /api/v1/actions/showcase_invoice/showcase_submit_signoffon draft INV-1004 (issued_onempty)400 VALIDATION_FAILED, "Issued On is required"actionRequirements.ts(new, in the form renderer's directory).patchingUpdateActionskeeps a declared action only when its whole write is known before it runs:operation: 'update', a non-empty staticpatch, noparams(an empty list is fine), and at least one location (a locationless action is placed nowhere, peractionRendersAt).usePatchingUpdateActionsreads the object's actions once per object through the data source the renderer already has (getObjectSchema, the read every form host makes and the adapter caches), on edit forms only.form.tsx.actionOutcomesis the record each kept action would leave:{ ...ruleRecord, ...patch }, the merge the server makes. An action is dropped when it is switched off, when the caller lacks itsrequiredPermissions(theaction:bargate), or when itsvisiblepredicate does not hold over the live record. That predicate fails closed and quietly, because the action's own button reports its fault. InrenderFormField, a field with arequiredWhenthat is not required now, is editable and is empty gets one line per action whose record makes it required. The line sits inside the field'sFormDescription, so the control'saria-describedbyannounces it.aria-required, and the submit-time required check reads the same live verdict as before.form.requiredForActionin all ten locales, plus the renderer's provider-less fallback row (byte-identical toen).Tests
form-required-for-action-28382.test.tsx(new, 12 tests) through the realformrenderer with a data source inSchemaRendererContext: the note on an edit form and itsaria-describedbydelivery; Save still writes with the field empty; the note goes when the field is filled; it gives way to the asterisk when the record as it stands requires the field; a create form reads nothing; an action whosevisibledoes not hold, or that collectsparams, adds nothing; two actions give two lines and an unrelated patch none; andpatchingUpdateActionsunit cases.scripts/ablation-replace.mjs, each leg restored withgit diff HEADempty):actionOutcomes.length > 0 &&to< 0: 5 tests red (every case that expects a note).visible !== true &&tofalse &&: 1 red, thevisibleskip case.!required &&totrue &&: 1 red, the asterisk case.packages/components/src/renderers/form/+packages/i18n/(one run): 177 of 178 files passed; the only red was the German quote census below (2 tests). After moving it,packages/i18n/reran: 94/94 files, 1438 passed, 13 skipped.ui/packages/i18n/src/__tests__/de-quote-pairing-3876.test.ts. The German value „{{action}}“ is one more matched pair, so its census goes from 103 to 104 (both literals), with the history comment extended.turbo run type-check --filter=@object-ui/components --filter=@object-ui/i18n: exit 0. eslint on each changed file: 0 errors.form.tsxhas the same warning count as onmain(58).Acceptance notes
FormSchemakey, so every form host (modal, drawer, page, tabbed, wizard) gets the note with no host or@object-ui/typeschange. This is the same pattern the renderer already uses for the record-currency used codes.params(the dialog's values are unknown before the press), and any non-declarative action (api/flow/scriptbodies). The optional "say what is missing on the button" half of the card is not done here: the button lives in the record header, which another card holds.Verification
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 34 commands; on headc56ed27dall 34 ran with exit 0, and--ranreconciles 34 derived, 34 run, 0 NOT-MEASURED (the full list is in the os-dev-report on the card).node scripts/check-sdui-manifest.mjsexit 0.pnpm --dir ui check:i18n-keys,check:i18n-dead-keysandcheck:i18n-driftexit 0.ui/packages/components/src/renderers/form/**orui/packages/i18n/src/locales/**at this head.Human Minimum
packages/specholds no runtime logic. (not touched)pnpm lintpasses. (per-file eslint locally; the repo-wide run is CI's)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (not touched)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgXCPFJQiBaT5tcJurf