diff --git a/packages/react-aria/src/interactions/utils.ts b/packages/react-aria/src/interactions/utils.ts index 21878b6833a..429afde9cb4 100644 --- a/packages/react-aria/src/interactions/utils.ts +++ b/packages/react-aria/src/interactions/utils.ts @@ -13,7 +13,7 @@ import {FocusableElement} from '@react-types/shared'; import {focusWithoutScrolling} from '../utils/focusWithoutScrolling'; import {getActiveElement, getEventTarget, nodeContains} from '../utils/shadowdom/DOMFunctions'; -import {getOwnerWindow, isShadowRoot} from '../utils/domHelpers'; +import {getOwnerWindow, isNode, isShadowRoot} from '../utils/domHelpers'; import {isFocusable} from '../utils/isFocusable'; import {FocusEvent as ReactFocusEvent, SyntheticEvent, useCallback, useRef} from 'react'; import {useLayoutEffect} from '../utils/useLayoutEffect'; @@ -138,12 +138,12 @@ export function preventFocus(target: FocusableElement | null): (() => void) | un // Focus is "moving to target" when it moves to the button or to a descendant of the button // (e.g. SVG icon) let isFocusMovingToTarget = (focusTarget: Element | null) => - focusTarget === target || (focusTarget != null && nodeContains(target, focusTarget)); + focusTarget === target || (isNode(focusTarget) && nodeContains(target, focusTarget)); // Blur/focusout events have their target as the element losing focus. Stop propagation when // that is the previously focused element (activeElement) or a descendant (e.g. in shadow DOM). let isBlurFromActiveElement = (eventTarget: Element | null) => eventTarget === activeElement || - (activeElement != null && eventTarget != null && nodeContains(activeElement, eventTarget)); + (activeElement != null && isNode(eventTarget) && nodeContains(activeElement, eventTarget)); ignoreFocusEvent = true; let isRefocusing = false; diff --git a/packages/react-aria/test/interactions/usePress.test.js b/packages/react-aria/test/interactions/usePress.test.js index ec848c7467f..1f944d2d9ca 100644 --- a/packages/react-aria/test/interactions/usePress.test.js +++ b/packages/react-aria/test/interactions/usePress.test.js @@ -1846,6 +1846,25 @@ describe('usePress', function () { expect(document.activeElement).not.toBe(el); }); + it('should not throw if the window is focused or blurred during a press', function () { + let res = render(); + + let el = res.getByText('test'); + fireEvent.mouseDown(el); + + // preventFocus listens for focus and blur on the window. The browser fires + // those at the window itself when it gains or loses focus, e.g. returning + // from a native dialog, another tab, or an iframe. The target of those + // events is the Window, which is not a Node. + fireEvent(window, new FocusEvent('focus')); + fireEvent(window, new FocusEvent('blur')); + + fireEvent.mouseUp(el); + fireEvent.click(el); + + expect(document.activeElement).not.toBe(el); + }); + it('should focus the element on click by default', function () { let res = render();