Skip to content

Commit 6ad41a5

Browse files
authored
fix: all node.contains for shadow dom usage (#9485)
* fix: all node.contains for shadow dom usage * fix lint and yarn lock * fix esm test * fix another esm test
1 parent b321d7d commit 6ad41a5

59 files changed

Lines changed: 405 additions & 140 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

eslint.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ export default [{
249249

250250
"rsp-rules/no-react-key": [ERROR],
251251
"rsp-rules/sort-imports": [ERROR],
252+
"rsp-rules/no-non-shadow-contains": [ERROR],
252253
"rulesdir/imports": [ERROR],
253254
"rulesdir/useLayoutEffectRule": [ERROR],
254255
"rulesdir/pure-render": [ERROR],
@@ -428,6 +429,7 @@ export default [{
428429
"rsp-rules/no-react-key": [ERROR],
429430
"rsp-rules/act-events-test": ERROR,
430431
"rsp-rules/no-getByRole-toThrow": ERROR,
432+
"rsp-rules/no-non-shadow-contains": OFF,
431433
"rulesdir/imports": OFF,
432434
"monorepo/no-internal-import": OFF,
433435
"jsdoc/require-jsdoc": OFF

packages/@react-aria/actiongroup/src/useActionGroup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import {AriaActionGroupProps} from '@react-types/actiongroup';
1414
import {createFocusManager} from '@react-aria/focus';
1515
import {DOMAttributes, FocusableElement, Orientation, RefObject} from '@react-types/shared';
16-
import {filterDOMProps, useLayoutEffect} from '@react-aria/utils';
16+
import {filterDOMProps, nodeContains, useLayoutEffect} from '@react-aria/utils';
1717
import {ListState} from '@react-stately/list';
1818
import {useLocale} from '@react-aria/i18n';
1919
import {useState} from 'react';
@@ -48,7 +48,7 @@ export function useActionGroup<T>(props: AriaActionGroupProps<T>, state: ListSta
4848
let focusManager = createFocusManager(ref);
4949
let flipDirection = direction === 'rtl' && orientation === 'horizontal';
5050
let onKeyDown = (e) => {
51-
if (!e.currentTarget.contains(e.target)) {
51+
if (!nodeContains(e.currentTarget, e.target)) {
5252
return;
5353
}
5454

packages/@react-aria/calendar/src/useRangeCalendar.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
import {AriaRangeCalendarProps, DateValue} from '@react-types/calendar';
1414
import {CalendarAria, useCalendarBase} from './useCalendarBase';
1515
import {FocusableElement, RefObject} from '@react-types/shared';
16+
import {nodeContains, useEvent} from '@react-aria/utils';
1617
import {RangeCalendarState} from '@react-stately/calendar';
17-
import {useEvent} from '@react-aria/utils';
1818
import {useRef} from 'react';
1919

2020
/**
@@ -52,8 +52,8 @@ export function useRangeCalendar<T extends DateValue>(props: AriaRangeCalendarPr
5252
let target = e.target as Element;
5353
if (
5454
ref.current &&
55-
ref.current.contains(document.activeElement) &&
56-
(!ref.current.contains(target) || !target.closest('button, [role="button"]'))
55+
nodeContains(ref.current, document.activeElement) &&
56+
(!nodeContains(ref.current, target) || !target.closest('button, [role="button"]'))
5757
) {
5858
state.selectFocusedDate();
5959
}
@@ -66,7 +66,7 @@ export function useRangeCalendar<T extends DateValue>(props: AriaRangeCalendarPr
6666
if (!ref.current) {
6767
return;
6868
}
69-
if ((!e.relatedTarget || !ref.current.contains(e.relatedTarget)) && state.anchorDate) {
69+
if ((!e.relatedTarget || !nodeContains(ref.current, e.relatedTarget)) && state.anchorDate) {
7070
state.selectFocusedDate();
7171
}
7272
};

packages/@react-aria/combobox/src/useComboBox.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {AriaComboBoxProps} from '@react-types/combobox';
1616
import {ariaHideOutside} from '@react-aria/overlays';
1717
import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';
1818
import {BaseEvent, DOMAttributes, KeyboardDelegate, LayoutDelegate, PressEvent, RefObject, RouterOptions, ValidationResult} from '@react-types/shared';
19-
import {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
19+
import {chain, getActiveElement, getOwnerDocument, isAppleDevice, mergeProps, nodeContains, useEvent, useFormReset, useLabels, useRouter, useUpdateEffect} from '@react-aria/utils';
2020
import {ComboBoxState} from '@react-stately/combobox';
2121
import {dispatchVirtualFocus} from '@react-aria/focus';
2222
import {FocusEvent, InputHTMLAttributes, KeyboardEvent, TouchEvent, useEffect, useMemo, useRef} from 'react';
@@ -181,7 +181,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
181181

182182
let onBlur = (e: FocusEvent<HTMLInputElement>) => {
183183
let blurFromButton = buttonRef?.current && buttonRef.current === e.relatedTarget;
184-
let blurIntoPopover = popoverRef.current?.contains(e.relatedTarget);
184+
let blurIntoPopover = nodeContains(popoverRef.current, e.relatedTarget);
185185
// Ignore blur if focused moved to the button(if exists) or into the popover.
186186
if (blurFromButton || blurIntoPopover) {
187187
return;

packages/@react-aria/datepicker/src/useDatePicker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {CalendarProps} from '@react-types/calendar';
1717
import {createFocusManager} from '@react-aria/focus';
1818
import {DatePickerState} from '@react-stately/datepicker';
1919
import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, RefObject, ValidationResult} from '@react-types/shared';
20-
import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
20+
import {filterDOMProps, mergeProps, nodeContains, useDescription, useId} from '@react-aria/utils';
2121
// @ts-ignore
2222
import intlMessages from '../intl/*.json';
2323
import {privateValidationStateProp} from '@react-stately/form';
@@ -84,7 +84,7 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
8484
onBlurWithin: e => {
8585
// Ignore when focus moves into the popover.
8686
let dialog = document.getElementById(dialogId);
87-
if (!dialog?.contains(e.relatedTarget)) {
87+
if (!nodeContains(dialog, e.relatedTarget)) {
8888
isFocused.current = false;
8989
props.onBlur?.(e);
9090
props.onFocusChange?.(false);

packages/@react-aria/datepicker/src/useDatePickerGroup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus';
22
import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker';
33
import {DOMAttributes, FocusableElement, KeyboardEvent, RefObject} from '@react-types/shared';
4-
import {mergeProps} from '@react-aria/utils';
4+
import {mergeProps, nodeContains} from '@react-aria/utils';
55
import {useLocale} from '@react-aria/i18n';
66
import {useMemo} from 'react';
77
import {usePress} from '@react-aria/interactions';
@@ -12,7 +12,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState
1212

1313
// Open the popover on alt + arrow down
1414
let onKeyDown = (e: KeyboardEvent) => {
15-
if (!e.currentTarget.contains(e.target)) {
15+
if (!nodeContains(e.currentTarget, e.target as Element)) {
1616
return;
1717
}
1818

packages/@react-aria/datepicker/src/useDateRangePicker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {DateRange, RangeCalendarProps} from '@react-types/calendar';
1818
import {DateRangePickerState} from '@react-stately/datepicker';
1919
import {DEFAULT_VALIDATION_RESULT, mergeValidation, privateValidationStateProp} from '@react-stately/form';
2020
import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, RefObject, ValidationResult} from '@react-types/shared';
21-
import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
21+
import {filterDOMProps, mergeProps, nodeContains, useDescription, useId} from '@react-aria/utils';
2222
import {focusManagerSymbol, roleSymbol} from './useDateField';
2323
// @ts-ignore
2424
import intlMessages from '../intl/*.json';
@@ -116,7 +116,7 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
116116
onBlurWithin: e => {
117117
// Ignore when focus moves into the popover.
118118
let dialog = document.getElementById(dialogId);
119-
if (!dialog?.contains(e.relatedTarget)) {
119+
if (!nodeContains(dialog, e.relatedTarget)) {
120120
isFocused.current = false;
121121
props.onBlur?.(e);
122122
props.onFocusChange?.(false);

packages/@react-aria/datepicker/src/useDateSegment.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import {CalendarDate, toCalendar} from '@internationalized/date';
1414
import {DateFieldState, DateSegment} from '@react-stately/datepicker';
15-
import {getScrollParent, isIOS, isMac, mergeProps, scrollIntoViewport, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils';
15+
import {getScrollParent, isIOS, isMac, mergeProps, nodeContains, scrollIntoViewport, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils';
1616
import {hookData} from './useDateField';
1717
import {NumberParser} from '@internationalized/number';
1818
import React, {CSSProperties, useMemo, useRef} from 'react';
@@ -281,7 +281,7 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref:
281281
// Otherwise, when tapping on a segment in Android Chrome and then entering text,
282282
// composition events will be fired that break the DOM structure and crash the page.
283283
let selection = window.getSelection();
284-
if (selection?.anchorNode && ref.current?.contains(selection?.anchorNode)) {
284+
if (selection?.anchorNode && nodeContains(ref.current, selection?.anchorNode)) {
285285
selection.collapse(ref.current);
286286
}
287287
});

packages/@react-aria/dialog/src/useDialog.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import {AriaDialogProps} from '@react-types/dialog';
1414
import {DOMAttributes, FocusableElement, RefObject} from '@react-types/shared';
15-
import {filterDOMProps, useSlotId} from '@react-aria/utils';
15+
import {filterDOMProps, nodeContains, useSlotId} from '@react-aria/utils';
1616
import {focusSafely} from '@react-aria/interactions';
1717
import {useEffect, useRef} from 'react';
1818
import {useOverlayFocusContain} from '@react-aria/overlays';
@@ -40,7 +40,7 @@ export function useDialog(props: AriaDialogProps, ref: RefObject<FocusableElemen
4040

4141
// Focus the dialog itself on mount, unless a child element is already focused.
4242
useEffect(() => {
43-
if (ref.current && !ref.current.contains(document.activeElement)) {
43+
if (ref.current && !nodeContains(ref.current, document.activeElement)) {
4444
focusSafely(ref.current);
4545

4646
// Safari on iOS does not move the VoiceOver cursor to the dialog

packages/@react-aria/dnd/src/DragManager.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {announce} from '@react-aria/live-announcer';
1414
import {ariaHideOutside} from '@react-aria/overlays';
1515
import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget, FocusableElement} from '@react-types/shared';
1616
import {getDragModality, getTypes} from './utils';
17-
import {isVirtualClick, isVirtualPointerEvent} from '@react-aria/utils';
17+
import {isVirtualClick, isVirtualPointerEvent, nodeContains} from '@react-aria/utils';
1818
import type {LocalizedStringFormatter} from '@internationalized/string';
1919
import {RefObject, useEffect, useState} from 'react';
2020

@@ -114,7 +114,7 @@ function endDragging() {
114114

115115
export function isValidDropTarget(element: Element): boolean {
116116
for (let target of dropTargets.keys()) {
117-
if (target.contains(element)) {
117+
if (nodeContains(target, element)) {
118118
return true;
119119
}
120120
}
@@ -243,7 +243,7 @@ class DragSession {
243243
this.cancelEvent(e);
244244

245245
if (e.key === 'Enter') {
246-
if (e.altKey || this.getCurrentActivateButton()?.contains(e.target as Node)) {
246+
if (e.altKey || nodeContains(this.getCurrentActivateButton(), e.target as Node)) {
247247
this.activate(this.currentDropTarget, this.currentDropItem);
248248
} else {
249249
this.drop();
@@ -275,7 +275,7 @@ class DragSession {
275275

276276
let dropTarget =
277277
this.validDropTargets.find(target => target.element === e.target as HTMLElement) ||
278-
this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
278+
this.validDropTargets.find(target => nodeContains(target.element, e.target as HTMLElement));
279279

280280
if (!dropTarget) {
281281
// if (e.target === activateButton) {
@@ -321,10 +321,10 @@ class DragSession {
321321
this.cancelEvent(e);
322322
if (isVirtualClick(e) || this.isVirtualClick) {
323323
let dropElements = dropItems.values();
324-
let item = [...dropElements].find(item => item.element === e.target as HTMLElement || item.activateButtonRef?.current?.contains(e.target as HTMLElement));
325-
let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
324+
let item = [...dropElements].find(item => item.element === e.target as HTMLElement || nodeContains(item.activateButtonRef?.current, e.target as HTMLElement));
325+
let dropTarget = this.validDropTargets.find(target => nodeContains(target.element, e.target as HTMLElement));
326326
let activateButton = item?.activateButtonRef?.current ?? dropTarget?.activateButtonRef?.current;
327-
if (activateButton?.contains(e.target as HTMLElement) && dropTarget) {
327+
if (nodeContains(activateButton, e.target as HTMLElement) && dropTarget) {
328328
this.activate(dropTarget, item);
329329
return;
330330
}
@@ -401,7 +401,7 @@ class DragSession {
401401
// Filter out drop targets that contain valid items. We don't want to stop hiding elements
402402
// other than the drop items that exist inside the collection.
403403
let visibleDropTargets = this.validDropTargets.filter(target =>
404-
!validDropItems.some(item => target.element.contains(item.element))
404+
!validDropItems.some(item => nodeContains(target.element, item.element))
405405
);
406406

407407
this.restoreAriaHidden = ariaHideOutside([

0 commit comments

Comments
 (0)