Skip to content

Commit 5bddd43

Browse files
committed
fix: Enable radiogroups to Properly Partake in Field Revalidation
Our previous implementation was a little naive... Because only the _first_ radio button in a `radiogroup` will get the `data-fvo-revalidate` attribute, we _need_ to check THAT radio button when running the revalidation event handler. This new code change guarantees that.
1 parent a86fce3 commit 5bddd43

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

packages/core/FormValidityObserver.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ class FormValidityObserver extends FormObserver {
166166
if (typeof options?.revalidateOn === "string") {
167167
types.push(options.revalidateOn);
168168
listeners.push((event) => {
169-
const field = event.target;
170-
if (field.hasAttribute(attrs["data-fvo-revalidate"])) this.validateField(field.name);
169+
const field = this.#getTargetField(event.target.name);
170+
if (field?.hasAttribute(attrs["data-fvo-revalidate"])) this.validateField(field.name);
171171
});
172172
}
173173

packages/core/__tests__/FormValidityObserver.test.ts

+45
Original file line numberDiff line numberDiff line change
@@ -2086,6 +2086,51 @@ describe("Form Validity Observer (Class)", () => {
20862086
expectErrorFor(field, stringMessage, "a11y");
20872087
expect(renderer).toHaveBeenCalledTimes(1); // `renderer` wasn't used this time
20882088
});
2089+
2090+
it("Correctly handles field revalidation for `radiogroup`s", async () => {
2091+
/* ---------- Setup ---------- */
2092+
// Render Form
2093+
document.body.innerHTML = `
2094+
<form aria-label="Test Form">
2095+
<fieldset role="radiogroup" aria-describedby="radio-error">
2096+
<input name="radio" type="radio" value="1" required />
2097+
<input name="radio" type="radio" value="2" />
2098+
<input name="radio" type="radio" value="3" />
2099+
</fieldset>
2100+
2101+
<div id="radio-error" role="alert"></div>
2102+
</form>
2103+
`;
2104+
2105+
const form = screen.getByRole<HTMLFormElement>("form");
2106+
const radiogroup = screen.getByRole<HTMLFieldSetElement>("radiogroup");
2107+
const radios = screen.getAllByRole<HTMLInputElement>("radio");
2108+
2109+
// Setup `FormValidityObserver`
2110+
const formValidityObserver = new FormValidityObserver(null, { revalidateOn: "input" });
2111+
formValidityObserver.observe(form);
2112+
2113+
/* ---------- Assertions ---------- */
2114+
// Activate revalidation for the `radiogroup`
2115+
expect(formValidityObserver.validateField(radios[0].name, { enableRevalidation: true })).toBe(false);
2116+
expect(radios[0].validationMessage).not.toBe("");
2117+
expect(radiogroup).toHaveAttribute("aria-invalid", String(true));
2118+
expect(radiogroup).toHaveAccessibleDescription(radios[0].validationMessage);
2119+
2120+
/*
2121+
* Note: Yes, manual DOM manipulation is ugly... But `userEvent` has a bug where it dispatches `input` events
2122+
* on the wrong `radio` button(s) during User Keyboard Interactions. So we have to do things manually, sadly.
2123+
* (Perhaps we should try `Playwright` in the future, after all...)
2124+
*/
2125+
// Use revalidation to clear the error message for the `radiogroup`.
2126+
// DO NOT select the first radio button! (Otherwise, the bug won't surface properly.)
2127+
radios[radios.length - 1].checked = true;
2128+
radios[radios.length - 1].dispatchEvent(new InputEvent("input", { bubbles: true }));
2129+
2130+
expect(radios[0].validationMessage).toBe("");
2131+
expect(radiogroup).toHaveAttribute("aria-invalid", String(false));
2132+
expect(radiogroup).not.toHaveAccessibleDescription();
2133+
});
20892134
});
20902135
});
20912136

0 commit comments

Comments
 (0)