Skip to content

Commit 6e9ba6e

Browse files
authored
fix(form): prevent TypeError when form contains elements without validity (#9614)
1 parent ce01db5 commit 6e9ba6e

2 files changed

Lines changed: 47 additions & 1 deletion

File tree

packages/@react-aria/form/src/useFormValidation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ function getNativeValidity(input: ValidatableElement): ValidationResult {
145145
function getFirstInvalidInput(form: HTMLFormElement): ValidatableElement | null {
146146
for (let i = 0; i < form.elements.length; i++) {
147147
let element = form.elements[i] as ValidatableElement;
148-
if (!element.validity.valid) {
148+
if (element.validity?.valid === false) {
149149
return element;
150150
}
151151
}

packages/react-aria-components/test/Form.test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,50 @@ describe('Form', () => {
188188
let form = getByTestId('form');
189189
expect(form).toHaveAttribute('data-custom', 'true');
190190
});
191+
192+
it('should not throw when form contains elements without validity property', async () => {
193+
function Test() {
194+
return (
195+
<Form data-testid="form">
196+
<TextField name="name" isRequired>
197+
<Label>Name</Label>
198+
<Input />
199+
<FieldError />
200+
</TextField>
201+
<Button type="submit">Submit</Button>
202+
</Form>
203+
);
204+
}
205+
206+
let {getByTestId, getByRole} = render(<Test />);
207+
let form = getByTestId('form');
208+
let input = getByRole('textbox');
209+
210+
// Mock form.elements to include an element without validity property (simulates Web Component)
211+
let originalElements = form.elements;
212+
let mockElement = {name: 'custom-element'}; // No validity property
213+
Object.defineProperty(form, 'elements', {
214+
get: () => ({
215+
length: originalElements.length + 1,
216+
[Symbol.iterator]: function* () {
217+
yield mockElement;
218+
yield* originalElements;
219+
},
220+
0: mockElement,
221+
...Array.from(originalElements).reduce((acc, el, i) => ({...acc, [i + 1]: el}), {})
222+
}),
223+
configurable: true
224+
});
225+
226+
// Should not throw when iterating form.elements with element lacking validity
227+
await user.click(getByRole('button'));
228+
229+
expect(document.activeElement).toBe(input);
230+
231+
// Restore original elements
232+
Object.defineProperty(form, 'elements', {
233+
get: () => originalElements,
234+
configurable: true
235+
});
236+
});
191237
});

0 commit comments

Comments
 (0)