Skip to content

Commit 846b7cd

Browse files
committed
fix(modal): allow sheet modals to skip focus trap
1 parent 3c08630 commit 846b7cd

File tree

1 file changed

+20
-13
lines changed

1 file changed

+20
-13
lines changed

core/src/utils/overlays.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -529,9 +529,14 @@ export const present = async <OverlayPresentOptions>(
529529
* focus traps.
530530
*
531531
* All other overlays should have focus traps to prevent
532-
* the keyboard focus from leaving the overlay.
532+
* the keyboard focus from leaving the overlay unless
533+
* developers explicitly opt out (for example, sheet
534+
* modals that should permit background interaction).
533535
*/
534-
if (overlay.el.tagName !== 'ION-TOAST') {
536+
const overlayEl = overlay.el as HTMLIonOverlayElement & { focusTrap?: boolean };
537+
const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false;
538+
539+
if (shouldTrapFocus) {
535540
setRootAriaHidden(true);
536541
document.body.classList.add(BACKDROP_NO_SCROLL);
537542
}
@@ -653,22 +658,24 @@ export const dismiss = async <OverlayDismissOptions>(
653658
* For accessibility, toasts lack focus traps and don't receive
654659
* `aria-hidden` on the root element when presented.
655660
*
656-
* All other overlays use focus traps to keep keyboard focus
657-
* within the overlay, setting `aria-hidden` on the root element
658-
* to enhance accessibility.
659-
*
660-
* Therefore, we must remove `aria-hidden` from the root element
661-
* when the last non-toast overlay is dismissed.
661+
* Overlays that opt into focus trapping set `aria-hidden`
662+
* on the root element to keep keyboard focus and pointer
663+
* events inside the overlay. We must remove `aria-hidden`
664+
* from the root element when the last focus-trapping overlay
665+
* is dismissed.
662666
*/
663-
const overlaysNotToast = presentedOverlays.filter((o) => o.tagName !== 'ION-TOAST');
664-
665-
const lastOverlayNotToast = overlaysNotToast.length === 1 && overlaysNotToast[0].id === overlay.el.id;
667+
const overlaysTrappingFocus = presentedOverlays.filter((o) => o.tagName !== 'ION-TOAST' && (o as any).focusTrap !== false);
668+
const overlayEl = overlay.el as HTMLIonOverlayElement & { focusTrap?: boolean };
669+
const trapsFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false;
666670

667671
/**
668-
* If this is the last visible overlay that is not a toast
672+
* If this is the last visible overlay that is trapping focus
669673
* then we want to re-add the root to the accessibility tree.
670674
*/
671-
if (lastOverlayNotToast) {
675+
const lastOverlayTrappingFocus =
676+
trapsFocus && overlaysTrappingFocus.length === 1 && overlaysTrappingFocus[0].id === overlayEl.id;
677+
678+
if (lastOverlayTrappingFocus) {
672679
setRootAriaHidden(false);
673680
document.body.classList.remove(BACKDROP_NO_SCROLL);
674681
}

0 commit comments

Comments
 (0)