diff --git a/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx b/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx index e963ff8c..3c7193df 100644 --- a/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx +++ b/clients/web/src/components/groups/SchemaForm/SchemaForm.test.tsx @@ -1742,6 +1742,25 @@ describe("SchemaForm defaulted fields (#2026)", () => { expect(n.value).toBe(""); }); + // An explicit `null` is a value, not an absent one, so a non-null default + // must not be substituted for it. Reachable for a nullable number field the + // user cleared, or one the server sent as null. + it("shows an explicit null as empty, not as the default", () => { + const schema: InspectorFormSchema = { + type: "object", + properties: { + n: { + title: "N", + anyOf: [{ type: "integer" }, { type: "null" }], + default: 30, + }, + }, + }; + renderWithMantine(); + + expect((screen.getByLabelText(/^N$/) as HTMLInputElement).value).toBe(""); + }); + it("still re-syncs a defaulted field when the value changes externally", async () => { function ExternalHarness() { const [values, setValues] = useState>({ diff --git a/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx b/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx index 53edeeca..acd59ca7 100644 --- a/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx +++ b/clients/web/src/components/groups/SchemaForm/SchemaForm.tsx @@ -379,8 +379,13 @@ function SchemaNumberInput({ onValidityChange, ...inputProps }: SchemaNumberInputProps) { + // `undefined` means "no value supplied", which is what the default is for. + // An explicit `null` is a value — a nullable number the user cleared, or one + // the server sent — so it must not be overwritten by a non-null default; it + // displays as the empty box `resolveValue` produced before. Hence the + // `undefined` test rather than `??`, matching `SchemaJsonField`. const [draft, setDraft] = useState( - value ?? defaultValue ?? "", + (value === undefined ? defaultValue : value) ?? "", ); // Compared against the raw value, never the default-substituted one, for the