fix: address Copilot review round 33 — keep a cleared field cleared across a branch switch

The carry filter dropped a name whose value was `undefined`, but clearing a
number or JSON field leaves exactly that: the name present with no value, which
is the user's answer rather than an absence. The incoming branch's defaults
then put the field's default back and undid the clear. Own-property presence
alone is now the test.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 13:09:54 -04:00
commit ffa08d76f5
2 changed files with 29 additions and 1 deletions

View file

@ -2291,6 +2291,31 @@ describe("SchemaForm multiline strings (#2042)", () => {
expect(onChange).not.toHaveBeenCalled();
});
it("keeps a cleared root field cleared across a branch switch", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
const schema: InspectorFormSchema = {
type: "object",
properties: { count: { type: "number", default: 7, title: "Count" } },
anyOf: [
{ type: "object", title: "A", properties: { x: {} } },
{ type: "object", title: "B", properties: { y: {} } },
],
};
renderWithMantine(
<SchemaForm
schema={schema}
// What clearing a number field leaves behind: the name is present
// with no value, which is the user's answer and not an absence.
values={{ count: undefined }}
onChange={onChange}
/>,
);
await user.click(screen.getByRole("textbox", { name: /Variant/ }));
await user.click(screen.getByRole("option", { name: "B" }));
expect(onChange).toHaveBeenCalledWith({ count: undefined });
});
it("renders no picker for a single-branch union but still shows its fields", () => {
const schema: InspectorFormSchema = {
type: "object",

View file

@ -799,8 +799,11 @@ export function SchemaForm({
Object.entries(nextProperties)
.filter(
([name, fieldSchema]) =>
// Own-property presence alone, `undefined` included: clearing a
// number or JSON field leaves the name present with no value, and
// that is the user's answer. Dropping it would let the defaults
// below put the field's default back and undo the clear.
Object.hasOwn(values, name) &&
values[name] !== undefined &&
fieldSchema.const === undefined &&
// Carried only where the incoming branch leaves the root's
// declaration as it found it. Anything the incoming branch declares