Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2edab45

Browse files
committed
fix(sheet): disable focus trap with string-based logic as well
1 parent 3b80473 commit 2edab45

File tree

7 files changed

+80
-19
lines changed

7 files changed

+80
-19
lines changed

‎core/src/components/modal/gestures/sheet.ts‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ export const createSheetGesture = (
9797
const enableBackdrop = () => {
9898
// Respect explicit opt-out of focus trapping/backdrop interactions
9999
// If focusTrap is false or showBackdrop is false, do not enable the backdrop or re-enable focus trap
100-
const el = baseEl as HTMLIonModalElement & { focusTrap?: boolean; showBackdrop?: boolean };
101-
if (el.focusTrap === false || el.showBackdrop === false) {
100+
const el = baseEl as HTMLIonModalElement & { focusTrap?: boolean | string; showBackdrop?: boolean | string };
101+
const focusTrapDisabled = el.focusTrap === false || el.focusTrap === 'false';
102+
const backdropDisabled = el.showBackdrop === false || el.showBackdrop === 'false';
103+
if (focusTrapDisabled || backdropDisabled) {
102104
return;
103105
}
104106
baseEl.style.setProperty('pointer-events', 'auto');
@@ -241,10 +243,10 @@ export const createSheetGesture = (
241243
* ion-backdrop and .modal-wrapper always have pointer-events: auto
242244
* applied, so the modal content can still be interacted with.
243245
*/
244-
const shouldEnableBackdrop=
245-
currentBreakpoint>backdropBreakpoint&&
246-
(baseElasHTMLIonModalElement&{focusTrap?: boolean}).focusTrap!==false&&
247-
(baseElasHTMLIonModalElement&{showBackdrop?: boolean}).showBackdrop!==false;
246+
const modalEl=baseElasHTMLIonModalElement&{focusTrap?: boolean|string;showBackdrop?: boolean|string};
247+
constfocusTrapDisabled=modalEl.focusTrap===false||modalEl.focusTrap==='false';
248+
constbackdropDisabled=modalEl.showBackdrop===false||modalEl.showBackdrop==='false';
249+
constshouldEnableBackdrop=currentBreakpoint>backdropBreakpoint&&!focusTrapDisabled&&!backdropDisabled;
248250
if (shouldEnableBackdrop) {
249251
enableBackdrop();
250252
} else {
@@ -591,10 +593,14 @@ export const createSheetGesture = (
591593
* Backdrop should become enabled
592594
* after the backdropBreakpoint value
593595
*/
596+
const modalEl = baseEl as HTMLIonModalElement & {
597+
focusTrap?: boolean | string;
598+
showBackdrop?: boolean | string;
599+
};
600+
const focusTrapDisabled = modalEl.focusTrap === false || modalEl.focusTrap === 'false';
601+
const backdropDisabled = modalEl.showBackdrop === false || modalEl.showBackdrop === 'false';
594602
const shouldEnableBackdrop =
595-
currentBreakpoint > backdropBreakpoint &&
596-
(baseEl as HTMLIonModalElement & { focusTrap?: boolean }).focusTrap !== false &&
597-
(baseEl as HTMLIonModalElement & { showBackdrop?: boolean }).showBackdrop !== false;
603+
currentBreakpoint > backdropBreakpoint && !focusTrapDisabled && !backdropDisabled;
598604
if (shouldEnableBackdrop) {
599605
enableBackdrop();
600606
} else {

‎core/src/components/modal/modal.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
12531253
[`modal-sheet`]: isSheetModal,
12541254
[`modal-no-expand-scroll`]: isSheetModal && !expandToScroll,
12551255
'overlay-hidden': true,
1256-
[FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
1256+
[FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false||focusTrap==='false',
12571257
...getClassMap(this.cssClass),
12581258
}}
12591259
onIonBackdropTap={this.onBackdropTap}

‎core/src/components/modal/test/basic/modal.spec.tsx‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ describe('modal: focus trap', () => {
2828

2929
expect(modal.classList.contains(FOCUS_TRAP_DISABLE_CLASS)).toBe(true);
3030
});
31+
it('should set the focus trap class when disabled via attribute string', async () => {
32+
const page = await newSpecPage({
33+
components: [Modal],
34+
html: `
35+
<ion-modal focus-trap="false"></ion-modal>
36+
`,
37+
});
38+
39+
const modal = page.body.querySelector('ion-modal')!;
40+
41+
expect(modal.classList.contains(FOCUS_TRAP_DISABLE_CLASS)).toBe(true);
42+
});
3143
it('should not set the focus trap class by default', async () => {
3244
const page = await newSpecPage({
3345
components: [Modal],

‎core/src/components/popover/popover.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ export class Popover implements ComponentInterface, PopoverInterface {
704704
'overlay-hidden': true,
705705
'popover-desktop': desktop,
706706
[`popover-side-${side}`]: true,
707-
[FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
707+
[FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false||focusTrap==='false',
708708
'popover-nested': !!parentPopover,
709709
}}
710710
onIonPopoverDidPresent={onLifecycle}

‎core/src/components/popover/test/basic/popover.spec.tsx‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ describe('popover: focus trap', () => {
2929

3030
expect(popover.classList.contains(FOCUS_TRAP_DISABLE_CLASS)).toBe(true);
3131
});
32+
it('should set the focus trap class when disabled via attribute string', async () => {
33+
const page = await newSpecPage({
34+
components: [Popover],
35+
html: `
36+
<ion-popover focus-trap="false"></ion-popover>
37+
`,
38+
});
39+
40+
const popover = page.body.querySelector('ion-popover')!;
41+
42+
expect(popover.classList.contains(FOCUS_TRAP_DISABLE_CLASS)).toBe(true);
43+
});
3244
it('should not set the focus trap class by default', async () => {
3345
const page = await newSpecPage({
3446
components: [Popover],

‎core/src/utils/overlays.ts‎

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,16 @@ export const present = async <OverlayPresentOptions>(
539539
* view container subtree, skip adding aria-hidden/inert there
540540
* to avoid disabling the overlay.
541541
*/
542-
const overlayEl = overlay.el as HTMLIonOverlayElement & { focusTrap?: boolean; showBackdrop?: boolean };
543-
const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false;
542+
const overlayEl = overlay.el as HTMLIonOverlayElement & {
543+
focusTrap?: boolean | string;
544+
showBackdrop?: boolean | string;
545+
};
546+
const focusTrapDisabled = overlayEl.focusTrap === false || overlayEl.focusTrap === 'false';
547+
const backdropDisabled = overlayEl.showBackdrop === false || overlayEl.showBackdrop === 'false';
548+
const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && !focusTrapDisabled;
544549
// Only lock out root content when backdrop is active. Developers relying on showBackdrop=false
545550
// expect background interaction to remain enabled.
546-
const shouldLockRoot = shouldTrapFocus && overlayEl.showBackdrop!==false;
551+
const shouldLockRoot = shouldTrapFocus && !backdropDisabled;
547552

548553
overlay.presented = true;
549554
overlay.willPresent.emit();
@@ -680,12 +685,18 @@ export const dismiss = async <OverlayDismissOptions>(
680685
* is dismissed.
681686
*/
682687
const overlaysLockingRoot = presentedOverlays.filter((o) => {
683-
const el = o as HTMLIonOverlayElement & { focusTrap?: boolean; showBackdrop?: boolean };
684-
return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && el.showBackdrop !== false;
688+
const el = o as HTMLIonOverlayElement & { focusTrap?: boolean | string; showBackdrop?: boolean | string };
689+
const focusTrapDisabled = el.focusTrap === false || el.focusTrap === 'false';
690+
const backdropDisabled = el.showBackdrop === false || el.showBackdrop === 'false';
691+
return el.tagName !== 'ION-TOAST' && !focusTrapDisabled && !backdropDisabled;
685692
});
686-
const overlayEl = overlay.el as HTMLIonOverlayElement & { focusTrap?: boolean; showBackdrop?: boolean };
687-
const locksRoot =
688-
overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false && overlayEl.showBackdrop !== false;
693+
const overlayEl = overlay.el as HTMLIonOverlayElement & {
694+
focusTrap?: boolean | string;
695+
showBackdrop?: boolean | string;
696+
};
697+
const focusTrapDisabled = overlayEl.focusTrap === false || overlayEl.focusTrap === 'false';
698+
const backdropDisabled = overlayEl.showBackdrop === false || overlayEl.showBackdrop === 'false';
699+
const locksRoot = overlayEl.tagName !== 'ION-TOAST' && !focusTrapDisabled && !backdropDisabled;
689700

690701
/**
691702
* If this is the last visible overlay that is trapping focus

‎core/src/utils/test/overlays/overlays-scroll-blocking.spec.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ describe('overlays: scroll blocking', () => {
3737
expect(body).not.toHaveClass('backdrop-no-scroll');
3838
});
3939

40+
it('should not block scroll when focus-trap attribute is set to "false"', async () => {
41+
const page = await newSpecPage({
42+
components: [Modal],
43+
html: `
44+
<ion-modal focus-trap="false"></ion-modal>
45+
`,
46+
});
47+
48+
const modal = page.body.querySelector('ion-modal')!;
49+
const body = page.doc.querySelector('body')!;
50+
51+
await modal.present();
52+
53+
expect(body).not.toHaveClass('backdrop-no-scroll');
54+
55+
await modal.dismiss();
56+
57+
expect(body).not.toHaveClass('backdrop-no-scroll');
58+
});
59+
4060
it('should not block scroll when the overlay is dismissed', async () => {
4161
const page = await newSpecPage({
4262
components: [Modal],

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /