From 1772b0ceb6b4fc93003ddd4d8fa9c3a39f90b098 Mon Sep 17 00:00:00 2001 From: sonsu Date: Thu, 5 Feb 2026 22:59:58 +0900 Subject: [PATCH] fix(form): prevent TypeError when form contains elements without validity --- .../@react-aria/form/src/useFormValidation.ts | 2 +- .../react-aria-components/test/Form.test.js | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/@react-aria/form/src/useFormValidation.ts b/packages/@react-aria/form/src/useFormValidation.ts index b4699bf8046..707984cbdac 100644 --- a/packages/@react-aria/form/src/useFormValidation.ts +++ b/packages/@react-aria/form/src/useFormValidation.ts @@ -145,7 +145,7 @@ function getNativeValidity(input: ValidatableElement): ValidationResult { function getFirstInvalidInput(form: HTMLFormElement): ValidatableElement | null { for (let i = 0; i < form.elements.length; i++) { let element = form.elements[i] as ValidatableElement; - if (!element.validity.valid) { + if (element.validity?.valid === false) { return element; } } diff --git a/packages/react-aria-components/test/Form.test.js b/packages/react-aria-components/test/Form.test.js index ad0d3642f91..b2b931f2e11 100644 --- a/packages/react-aria-components/test/Form.test.js +++ b/packages/react-aria-components/test/Form.test.js @@ -188,4 +188,50 @@ describe('Form', () => { let form = getByTestId('form'); expect(form).toHaveAttribute('data-custom', 'true'); }); + + it('should not throw when form contains elements without validity property', async () => { + function Test() { + return ( +
+ + + + + + +
+ ); + } + + let {getByTestId, getByRole} = render(); + let form = getByTestId('form'); + let input = getByRole('textbox'); + + // Mock form.elements to include an element without validity property (simulates Web Component) + let originalElements = form.elements; + let mockElement = {name: 'custom-element'}; // No validity property + Object.defineProperty(form, 'elements', { + get: () => ({ + length: originalElements.length + 1, + [Symbol.iterator]: function* () { + yield mockElement; + yield* originalElements; + }, + 0: mockElement, + ...Array.from(originalElements).reduce((acc, el, i) => ({...acc, [i + 1]: el}), {}) + }), + configurable: true + }); + + // Should not throw when iterating form.elements with element lacking validity + await user.click(getByRole('button')); + + expect(document.activeElement).toBe(input); + + // Restore original elements + Object.defineProperty(form, 'elements', { + get: () => originalElements, + configurable: true + }); + }); });