fix: address Copilot review round 18 — empty-string constants, nested branch reset

- TUI missingRequiredFields treats an empty string as supplied when the
  property's own `const` pins it there. The one-option control cannot produce
  anything else, so reporting the seeded value as missing made such a branch
  permanently uncallable.
- A nested SchemaForm is reset by the branch-aware key too. Two outer
  alternatives can both carry the same nested object field, and a nested form
  left mounted across an outer switch kept displaying the inner branch chosen
  for the other one while the newly seeded values described another shape.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 06:55:20 -04:00
commit 36cf58fe6c
2 changed files with 76 additions and 3 deletions

View file

@ -2215,6 +2215,51 @@ describe("SchemaForm multiline strings (#2042)", () => {
);
});
it("resets a nested form's own branch when the outer branch changes", async () => {
const user = userEvent.setup();
const nested: InspectorFormSchema = {
type: "object",
properties: {
config: {
type: "object",
title: "Config",
properties: {},
anyOf: [
{
type: "object",
title: "Inner A",
properties: { alpha: { type: "string", title: "Alpha" } },
},
{
type: "object",
title: "Inner B",
properties: { beta: { type: "string", title: "Beta" } },
},
],
},
},
anyOf: [
{ type: "object", title: "Outer A", properties: { x: {} } },
{ type: "object", title: "Outer B", properties: { y: {} } },
],
};
renderWithMantine(
<SchemaForm schema={nested} values={{}} onChange={vi.fn()} />,
);
const [outer, inner] = screen.getAllByRole("textbox", {
name: /Variant/,
});
await user.click(inner!);
await user.click(screen.getByRole("option", { name: "Inner B" }));
expect(screen.getByRole("textbox", { name: /Beta/ })).toBeTruthy();
await user.click(outer!);
await user.click(screen.getByRole("option", { name: "Outer B" }));
// The nested form is still mounted, so only a changed reset key can stop
// it displaying a branch the newly seeded values do not describe.
expect(screen.getByRole("textbox", { name: /Alpha/ })).toBeTruthy();
});
it("renders no picker for a single-branch union but still shows its fields", () => {
const schema: InspectorFormSchema = {
type: "object",

View file

@ -972,8 +972,12 @@ export function SchemaForm({
handleFieldChange(fieldName, nestedValues)
}
disabled={disabled}
// Sub-fields belong to the same entity, so they reset with it.
resetKey={resetKey}
// Sub-fields belong to the same entity, so they reset with it —
// and to the branch it is being edited under, since two outer
// alternatives can both carry this field and a nested form left
// mounted across the switch would keep displaying the nested
// branch chosen for the other one.
resetKey={draftKey}
// A nested form's invalid draft is the outer form's invalid draft,
// so it reports through the same channel under this field's name.
onValidityChange={(nestedInvalid) =>