Skip to content

[FocusTrap] Guard focus restoration against non-focusable relatedTarget - #49164

Open
Dextheking1 wants to merge 2 commits into
mui:masterfrom
Dextheking1:fix/focustrap-restore-focus-guard
Open

Dextheking1 wants to merge 2 commits into
mui:masterfrom
Dextheking1:fix/focustrap-restore-focus-guard

Conversation

@Dextheking1

Copy link
Copy Markdown

Closes #49159.

Problem

When a FocusTrap unmounts (or open flips), the cleanup restores focus to
nodeToRestore.current, which is captured from event.relatedTarget:

(nodeToRestore.current as HTMLElement).focus();

relatedTarget is typed as EventTarget | null, but nothing guarantees it
exposes a focus method. Since jsdom 30.1.0 it can resolve to a truthy value
without one (see #49159 for the full analysis), so unmounting a trap throws:

TypeError: (nodeToRestore.current as HTMLElement).focus is not a function

This previously had a defensive guard, removed in #48368 when the restore
logic was reworked.

Fix

Restore the guard, in typeof form, before calling .focus():

if (typeof (nodeToRestore.current as HTMLElement).focus === 'function') {
  ignoreNextEnforceFocus.current = true;
  (nodeToRestore.current as HTMLElement).focus();
}
nodeToRestore.current = null;

The ref is still cleared unconditionally so a subsequent focus event
re-captures a fresh target. No behavior change in real browsers, where the
target always has .focus.

Test

Added a regression test that dispatches a focusin whose relatedTarget is
truthy but has no focus method, then unmounts the trap and asserts nothing
is thrown. Verified it fails without the fix (TypeError) and passes with it.

  • pnpm test:unit FocusTrap passes

@code-infra-dashboard

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-49164--material-ui.netlify.app/
QR code for https://deploy-preview-49164--material-ui.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/material 🔺+38B(+0.01%) 🔺+10B(+0.01%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant