Skip to content

fix(plugin-form): an ObjectForm block's showReset draws the Reset button instead of clearing the form after a save - #27599

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-qcbptz
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-qcbptz

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Requested by Bill · project thread
Fixes #27247

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:ObjectForm 区块的 showReset: true 现在会在表单按钮栏画出「重置」按钮,点了把表单恢复到打开时的值、不保存;保存后不再因为 showReset 清空表单。
  • 为什么改:规范把 showReset 写作「显示重置按钮」,渲染器却把它当成「保存后清空」:设 true 没按钮、每次保存清空表单,设 false 没作用。
  • 风险与回滚:依赖旧的「保存后清空」副作用的表单会不再清空(应改用 submitBehavior: { kind: 'continue' });回滚即 revert 本 PR,无数据或协议迁移。
  • 先看:ui/packages/components/src/renderers/form/form.tsx(按钮与 reset 事件处理)、ui/packages/plugin-form/src/ObjectForm.tsx(去掉 OR 进 resetOnSubmit 的那行)、ui/packages/plugin-form/src/ModalForm.tsx(表单外的页脚按钮经表单 id 触发重置)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/plugin-form/src/ObjectForm.showReset-27247.test.tsx,预期 8 条全过。
  • 需要你做的:无,席位落地。

What changes

The object-form block declares showReset as "Show the reset button" (the spec text is unchanged). The renderer OR-ed it into the form's clear-after-save flag, so true drew no button and emptied the form after every save, and false did nothing. The triage ruling: the renderer honours showReset as declared, and resetOnSubmit stays its own key.

  • @object-ui/components (form renderer). showReset draws a Reset button in the action bar, between Cancel and Save, labelled resetLabel or the session language's common.reset (already in all ten packs; added to the no-provider fallback). The form element handles its own reset event: it restores react-hook-form's opening values (the defaults the defaultValues reset keeps current), clears the submit error and the rejected-field markers, and calls preventDefault so the browser does not also restore the DOM inputs. So any type="reset" button that names the form's id reaches the same restore.
  • @object-ui/plugin-form.
    • ObjectForm: the flat path no longer reads showReset into resetOnSubmit. The flat and sectioned simple paths hand showReset (off in view mode) to the bar.
    • TabbedForm forwards it to its bar.
    • DrawerForm and ModalForm draw the button in their own footers as type="reset" form={formId}, gated like Save (no button in view mode or on a record closed for edit). A footer with only Reset still shows.
  • @object-ui/types. FormSchema (the renderer-internal form node, which @objectstack/spec does not declare) gains showReset?: boolean and resetLabel?: string, with JSDoc and zod rows. The seat approved this file-surface addition before any push.

Not covered (no clean slot). WizardForm and MasterDetailForm have their own action bars. A wizard has one inner form per step plus merged step data, and a master-detail form has a header form plus line items, so neither has one form whose opening values a Reset restores. Both still read no showReset, and nothing there changes.

Declarations, measured. Clause-②: yes (widening): the two keys reach the published entry of @object-ui/types (dist/form.d.ts declares showReset?: boolean and resetLabel?: string on the exported FormSchema; dist/zod/form.zod.d.ts has both rows). Metadata-protocol: none: what a metadata author writes is unchanged. The object-form block's showReset and its text are as before, and the new keys sit on the form node, which is not in the spec's block vocabulary or the public-tier sdui.manifest.json (check-sdui-manifest exit 0, manifest unchanged). Changeset level patch for the three packages.

Tests

  • ui/packages/plugin-form/src/ObjectForm.showReset-27247.test.tsx (8): the flat form's button restores initialValues and does not call create; a save with showReset: true keeps the typed value (the pin against the old clear); unset draws no button; view mode draws none; the sectioned form restores the opening values; the tabbed form draws one button; the modal footer's button names the form's id and a click restores the opening values; the drawer footer draws it with the form's id.
  • ui/packages/components/src/renderers/form/__tests__/form-reset-button-27247.test.tsx (3): no button unless showReset; resetLabel names it and a click restores defaultValues without onSubmit; a reset button outside the form reaches it through the form id.
Ablations (each committed first, mutated through scripts/ablation-replace.mjs, restored to HEAD with git diff HEAD empty)
  • Put schema.showReset || back at the head of the flat path's resetOnSubmit: a save does not clear the form because of showReset goes red, 1 failed and 7 passed.
  • Delete onReset={handleReset} from the form element: 5 red (restores the opening values; outside reset button; flat form; sectioned form; modal footer), 6 passed.
Local verification (pre-merge head 42f8831, merged head 61baeb3)
  • pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-form/ packages/components/src/renderers/form/ packages/types/: pre-merge 674 files passed, 13326 tests passed, 18 skipped; post-merge 676 files passed, 13337 tests passed, 18 skipped.
  • type-check for @object-ui/types, @object-ui/components, @object-ui/plugin-form: exit 0 on both heads.
  • dispatch-gates --commands (34 families) run on the merged head and reconciled with --ran: 34 derived, 34 run, every one exit 0, 0 NOT-MEASURED, 0 UNRUN.

Acceptance notes

  • content/docs/ui/components/plugins.mdx lists showReset with no description. That page is generated from sdui.manifest.json, from the object-form registration inputs in plugin-form/src/index.tsx. Adding a description there means regenerating the manifest and the page, which is outside this card's file surface, so it is left as is. The ui README and content/docs/** have no sentence that describes the old effect.
  • skills/objectstack-ui/references/react-blocks.md says "Show the reset button." for showReset. That is now true, so no skills line is wrong.
  • Found while measuring, for the seat to file: on the sectioned simple path, ObjectForm builds its form node without resetOnSubmit. So submitBehavior: { kind: 'continue' } (and resetOnSuccess on a create form) does not clear a sectioned form after a save, while the flat form does. A probe test reproduced this: the flat form cleared, the sectioned form kept Alpha.
  • The merge commit from origin/main carries no trailer pair (git's default merge message; the pre-push hook accepted it).

Verification

Open an object-form block with showReset: true in create mode, type in a field, and press Reset. The field goes back to its opening value and nothing is saved. Save, and the form keeps the saved values.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 61baeb319b726f4bea530e9b616311b2d2fbf0cc
Local-runs: none

Face: the Clause-②: yes (widening) declaration alone (PR body, changeset and the corrected claim 6497765424). No packages/spec/** path and no governed path in the PR's ten files, so no CONTRACT_REVIEW_TIER face.

① Derived judgments

  • Widened, correctly declared: @object-ui/types FormSchema (interface, ui/packages/types/src/form.ts) and its zod twin (ui/packages/types/src/zod/form.zod.ts) gain two optional keys, showReset?: boolean and resetLabel?: string. Both are reachable from the package's exported FormSchema, so the public surface grows; yes (widening) is the honest line. Nothing becomes required and no existing key changes shape: no consumer breaks.
  • Not widened: the object-form block's showReset in @objectstack/spec keeps its key and its text ("Show the reset button"); the renderer now does what the text says. Metadata-protocol: none holds: the form node is ui renderer-internal, not a spec-declared authoring key, and sdui.manifest.json is unchanged (dev's check-sdui-manifest exit 0).
  • Behaviour change on an existing key: showReset: true stops clearing the form after a save. That was the defect (triage 6496463027: "the renderer honours showReset as declared"); a form that wants clearing has submitBehavior: { kind: 'continue' } / resetOnSuccess, named in the changeset.
  • The form registration's inputs gain showReset (with a description) and resetLabel, matching the two type keys.

② Semver level

patch on @object-ui/plugin-form, @object-ui/components, @object-ui/types. Consistent with the repo's rule that the level follows Metadata-protocol: (kind none ⇒ patch); the changeset names the two new optional keys in its body.

③ Boundary flags

Implemented-by: claude/project-thread-qcbptz
Reviewed-by: session_01Utp6CY2n1JmZZKrHgtp9jC

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

席位核对

ACCEPT · head 61baeb319b726f4bea530e9b616311b2d2fbf0cc · 2026-10-29T14:55Z

  1. Shape: draft → main, Fixes #27247 only, attribution block on top, ten files (components form.tsx + test, plugin-form ObjectForm / DrawerForm / ModalForm / TabbedForm + test, types form.ts + zod/form.zod.ts, one changeset). No assignee, no reviewer.
  2. Premise measured: the seat read the diff. ObjectForm no longer ORs showReset into resetOnSubmit; the flat and sectioned paths pass showReset (off in view mode) to the form node, which draws a type="reset" button between Cancel and Save and handles the form's own reset event (form.reset() to the opening values, submit error and rejected-field markers cleared, preventDefault). Drawer and modal footers reach it by form id. Matches triage 6496463027; no packages/spec/** path.
  3. Gates (check-runs on this head): 44 success, 11 skipped, 0 red, including Lint & Repo Gates 123518004845 and TypeScript Type Check 123517087134.
  4. Contract review: declaration face, TIER_DEFAULT PASS on this head, comment 6499686543. The claim's Clause-② line was corrected to yes (widening) from the dev's measurement before it.
  5. Accepted deviations / out of scope: wizard and master-detail draw no Reset button (no single form to restore; stated in the changeset). The sectioned path's missing resetOnSubmit is filed as ObjectForm: a sectioned form ignores submitBehavior: { kind: 'continue' } and resetOnSuccess, so it keeps the saved values while the flat form clears #27602. The plugins.mdx row without a description needs a manifest regeneration and stays a note. Skills-docs: not owed (Metadata-protocol: none). Changeset read sentence by sentence against the diff: matches (FROM → TO, where the button is drawn, the two FormSchema keys, the reset event).
  6. Landing: not governed (check-governed-merges --pr 27599: NOT governed), 357 lines < 3000. The seat lands it: ready + auto-merge now.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 14:56
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 29, 2026 14:56
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 9c51314 Oct 29, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-qcbptz branch October 29, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

1 participant