From fc059ef75829b37a11fc88b6c6d67e6e1cd71857 Mon Sep 17 00:00:00 2001 From: maxatwork <397263+maxatwork@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:39:38 +0100 Subject: [PATCH] fix: align dom checkbox and radio parsing with browsers --- README.md | 4 ++-- apps/docs/test/standard-variants.test.tsx | 1 - docs/api.md | 7 +++---- packages/dom/src/index.ts | 22 ---------------------- packages/dom/test/dom.test.ts | 14 +++++++++----- 5 files changed, 14 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 55628dc..917a3d6 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ Compatibility with the old project is intentional. - Name paths define output shape (`person.name.first`). - Array and indexed syntax is preserved (`items[]`, `items[5].name`). - Rails-style names are supported (`rails[field][value]`). -- Checkbox/radio `"true"` and `"false"` quirks are preserved. +- DOM extraction follows native browser form submission semantics for checkbox and radio values. - Unsafe key path segments (`__proto__`, `prototype`, `constructor`) are rejected by default. - This library does data shaping, not JSON/XML serialization. @@ -274,7 +274,7 @@ Compatibility with the old project is intentional. These boundaries are intentional and are used for issue triage. - Sparse indexes are compacted in first-seen order (`items[5]`, `items[8]` -> `items[0]`, `items[1]`). -- Type inference is minimal by design; only legacy checkbox/radio `"true"` and `"false"` coercion is built in. +- Type inference is minimal by design; DOM extraction keeps native string values instead of coercing checkbox/radio fields. - `formToObject` reads successful form control values, not option labels. Disabled controls (including disabled fieldset descendants) and button-like inputs are excluded unless you explicitly opt in to disabled values. - `extractPairs`/`formToObject` support `nodeCallback`; return `SKIP_NODE` to exclude a node entirely, or `{ name|key, value }` to inject a custom entry. - Parser inputs reject unsafe path segments by default. Use `allowUnsafePathSegments: true` only with trusted inputs. diff --git a/apps/docs/test/standard-variants.test.tsx b/apps/docs/test/standard-variants.test.tsx index 0482d52..7f90c7b 100644 --- a/apps/docs/test/standard-variants.test.tsx +++ b/apps/docs/test/standard-variants.test.tsx @@ -74,7 +74,6 @@ describe("standard playground variants", () => { errorMessage: null, parsedPayload: { person: { - approved: false, city: "lancre", guild: "witches", name: { first: "Esme", last: "Weatherwax" }, diff --git a/docs/api.md b/docs/api.md index 71fe978..0d28074 100644 --- a/docs/api.md +++ b/docs/api.md @@ -224,10 +224,9 @@ export function form2js( ### Behavior notes - `select name="colors[]"` is emitted as key `colors` (the trailing `[]` is removed for selects). -- Legacy checkbox/radio quirks are preserved: - - checked `"true"` -> `true` - - unchecked `"true"` -> `false` - - checked `"false"` (radio/checkbox) -> `false` +- Checkbox and radio values follow native browser form submission semantics: + - checked controls emit their string `value` + - unchecked controls are omitted - Button-like inputs (`button`, `reset`, `submit`, `image`) are excluded from extraction. - Can merge multiple roots (`NodeList`, arrays, `HTMLCollection`) into one object. - If callback returns `SKIP_NODE`, that node is excluded from extraction entirely. diff --git a/packages/dom/src/index.ts b/packages/dom/src/index.ts index 8792cc8..6523eac 100644 --- a/packages/dom/src/index.ts +++ b/packages/dom/src/index.ts @@ -208,29 +208,7 @@ function getFieldValue(fieldNode: Node, getDisabled: boolean): unknown { switch (inputType) { case "radio": - if (fieldNode.checked && fieldNode.value === "false") { - return false; - } - - if (fieldNode.checked && fieldNode.value === "true") { - return true; - } - - if (fieldNode.checked) { - return fieldNode.value; - } - - return null; - case "checkbox": - if (fieldNode.checked && fieldNode.value === "true") { - return true; - } - - if (!fieldNode.checked && fieldNode.value === "true") { - return false; - } - if (fieldNode.checked) { return fieldNode.value; } diff --git a/packages/dom/test/dom.test.ts b/packages/dom/test/dom.test.ts index 6efa943..6996b11 100644 --- a/packages/dom/test/dom.test.ts +++ b/packages/dom/test/dom.test.ts @@ -116,11 +116,14 @@ describe("extractPairs", () => { }); describe("formToObject", () => { - it("keeps legacy checkbox and radio coercion quirks", () => { + it("keeps checkbox and radio values aligned with native form submission", () => { document.body.innerHTML = `
- - + + + + +
`; @@ -129,8 +132,9 @@ describe("formToObject", () => { expect(result).toEqual({ person: { - agree: false, - optOut: false + checkboxTrueChecked: "true", + checkboxFalseChecked: "false", + radio: "false" } }); });