Skip to content

Commit 33f583c

Browse files
committed
refactor: rewrite wrapFocus utils in typescript
1 parent 8d30a69 commit 33f583c

File tree

7 files changed

+208
-162
lines changed

7 files changed

+208
-162
lines changed

packages/react/src/components/ComposedModal/ComposedModal.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
/**
2+
* Copyright IBM Corp. 2021, 2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
18
import React, {
29
useRef,
310
useEffect,
@@ -23,8 +30,9 @@ import mergeRefs from '../../tools/mergeRefs';
2330
import cx from 'classnames';
2431
import toggleClass from '../../tools/toggleClass';
2532
import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy';
26-
import wrapFocus, {
33+
import {
2734
elementOrParentIsFloatingMenu,
35+
wrapFocus,
2836
} from '../../internal/wrapFocus';
2937
import { usePrefix } from '../../internal/usePrefix';
3038
import { keys, match } from '../../internal/keyboard';
@@ -299,11 +307,12 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
299307
}
300308

301309
function handleOnClick(evt: React.MouseEvent<HTMLDivElement>) {
302-
const target = evt.target as Node;
303-
const mouseDownTarget = onMouseDownTarget.current as Node;
310+
const { target } = evt;
311+
const mouseDownTarget = onMouseDownTarget.current;
304312
evt.stopPropagation();
305313
if (
306314
!preventCloseOnClickOutside &&
315+
target instanceof Node &&
307316
!elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) &&
308317
innerModal.current &&
309318
!innerModal.current.contains(target) &&
@@ -327,9 +336,7 @@ const ComposedModal = React.forwardRef<HTMLDivElement, ComposedModalProps>(
327336
endTrapNode: endSentinelNode,
328337
currentActiveNode,
329338
oldActiveNode,
330-
selectorsFloatingMenus: selectorsFloatingMenus?.filter(
331-
Boolean
332-
) as string[],
339+
selectorsFloatingMenus: selectorsFloatingMenus?.filter(Boolean),
333340
});
334341
}
335342
}

packages/react/src/components/Modal/Modal.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import ButtonSet from '../ButtonSet';
2121
import InlineLoading from '../InlineLoading';
2222
import { Layer } from '../Layer';
2323
import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy';
24-
import wrapFocus, {
24+
import {
2525
elementOrParentIsFloatingMenu,
26+
wrapFocus,
2627
} from '../../internal/wrapFocus';
2728
import { debounce } from 'es-toolkit/compat';
2829
import useIsomorphicEffect from '../../internal/useIsomorphicEffect';
@@ -316,10 +317,11 @@ const Modal = React.forwardRef(function Modal(
316317
}
317318

318319
function handleOnClick(evt: React.MouseEvent<HTMLDivElement>) {
319-
const target = evt.target as Node;
320+
const { target } = evt;
320321
evt.stopPropagation();
321322
if (
322323
!preventCloseOnClickOutside &&
324+
target instanceof Node &&
323325
!elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) &&
324326
innerModal.current &&
325327
!innerModal.current.contains(target)
@@ -332,7 +334,11 @@ const Modal = React.forwardRef(function Modal(
332334
target: oldActiveNode,
333335
relatedTarget: currentActiveNode,
334336
}: React.FocusEvent<HTMLDivElement>) {
335-
if (open && currentActiveNode && oldActiveNode) {
337+
if (
338+
open &&
339+
oldActiveNode instanceof HTMLElement &&
340+
currentActiveNode instanceof HTMLElement
341+
) {
336342
const { current: bodyNode } = innerModal;
337343
const { current: startTrapNode } = startTrap;
338344
const { current: endTrapNode } = endTrap;

packages/react/src/components/Notification/Notification.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { keys, matches, match } from '../../internal/keyboard';
3838
import { usePrefix } from '../../internal/usePrefix';
3939
import { useId } from '../../internal/useId';
4040
import { noopFn } from '../../internal/noopFn';
41-
import wrapFocus, { wrapFocusWithoutSentinels } from '../../internal/wrapFocus';
41+
import { wrapFocus, wrapFocusWithoutSentinels } from '../../internal/wrapFocus';
4242
import { useFeatureFlag } from '../FeatureFlags';
4343
import { warning } from '../../internal/warning';
4444
import deprecateValuesWithin from '../../prop-types/deprecateValuesWithin';

packages/react/src/internal/FloatingMenu.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { OptimizedResize } from './OptimizedResize';
2626
import { selectorFocusable, selectorTabbable } from './keyboard/navigation';
2727
import { PrefixContext } from './usePrefix';
2828
import { warning } from './warning';
29-
import wrapFocus, { wrapFocusWithoutSentinels } from './wrapFocus';
29+
import { wrapFocus, wrapFocusWithoutSentinels } from './wrapFocus';
3030

3131
export const DIRECTION_LEFT = 'left';
3232
export const DIRECTION_TOP = 'top';
@@ -452,18 +452,22 @@ export const FloatingMenu = ({
452452
/**
453453
* Blur handler used when focus trapping is enabled.
454454
*/
455-
const handleBlur = (event: FocusEvent<HTMLDivElement>) => {
455+
const handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {
456+
const { target, relatedTarget } = event;
457+
456458
if (
457459
menuBodyRef.current &&
458460
startSentinelRef.current &&
459-
endSentinelRef.current
461+
endSentinelRef.current &&
462+
target instanceof HTMLElement &&
463+
relatedTarget instanceof HTMLElement
460464
) {
461465
wrapFocus({
462466
bodyNode: menuBodyRef.current,
463467
startTrapNode: startSentinelRef.current,
464468
endTrapNode: endSentinelRef.current,
465-
currentActiveNode: event.relatedTarget as HTMLElement,
466-
oldActiveNode: event.target,
469+
currentActiveNode: relatedTarget,
470+
oldActiveNode: target,
467471
});
468472
}
469473
};
@@ -472,7 +476,11 @@ export const FloatingMenu = ({
472476
* Keydown handler for focus wrapping when experimental focus trap is enabled.
473477
*/
474478
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
475-
if (match(event, keys.Tab) && menuBodyRef.current) {
479+
if (
480+
match(event, keys.Tab) &&
481+
menuBodyRef.current &&
482+
event.target instanceof HTMLElement
483+
) {
476484
wrapFocusWithoutSentinels({
477485
containerNode: menuBodyRef.current,
478486
currentActiveNode: event.target,

packages/react/src/internal/__tests__/wrapFocus-test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import wrapFocus from '../wrapFocus';
2-
31
/**
4-
* Copyright IBM Corp. 2020
2+
* Copyright IBM Corp. 2020, 2025
53
*
64
* This source code is licensed under the Apache-2.0 license found in the
75
* LICENSE file in the root directory of this source tree.
86
*/
97

8+
import { wrapFocus } from '../wrapFocus';
9+
1010
describe('wrapFocus', () => {
1111
let node;
1212
let spyInnerModal;

packages/react/src/internal/wrapFocus.js

Lines changed: 0 additions & 143 deletions
This file was deleted.

0 commit comments

Comments
 (0)