diff --git a/core/src/components/modal/animations/ios.enter.ts b/core/src/components/modal/animations/ios.enter.ts
index 201cfe0cce..a0e7605e0c 100644
--- a/core/src/components/modal/animations/ios.enter.ts
+++ b/core/src/components/modal/animations/ios.enter.ts
@@ -7,7 +7,11 @@ import { createSheetEnterAnimation } from './sheet';
const createEnterAnimation = () => {
const backdropAnimation = createAnimation()
- .fromTo('opacity', 0.01, 'var(--backdrop-opacity)');
+ .fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
+ .beforeStyles({
+ 'pointer-events': 'none'
+ })
+ .afterClearStyles(['pointer-events']);
const wrapperAnimation = createAnimation()
.fromTo('transform', 'translateY(100vh)', 'translateY(0vh)');
@@ -27,11 +31,7 @@ export const iosEnterAnimation = (
const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetEnterAnimation(opts) : createEnterAnimation();
backdropAnimation
- .addElement(root.querySelector('ion-backdrop')!)
- .beforeStyles({
- 'pointer-events': 'none'
- })
- .afterClearStyles(['pointer-events']);
+ .addElement(root.querySelector('ion-backdrop')!);
wrapperAnimation
.addElement(root.querySelectorAll('.modal-wrapper, .modal-shadow')!)
diff --git a/core/src/components/modal/animations/md.enter.ts b/core/src/components/modal/animations/md.enter.ts
index 971a17d5d8..4b81f3d38c 100644
--- a/core/src/components/modal/animations/md.enter.ts
+++ b/core/src/components/modal/animations/md.enter.ts
@@ -6,7 +6,11 @@ import { createSheetEnterAnimation } from './sheet';
const createEnterAnimation = () => {
const backdropAnimation = createAnimation()
- .fromTo('opacity', 0.01, 'var(--backdrop-opacity)');
+ .fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
+ .beforeStyles({
+ 'pointer-events': 'none'
+ })
+ .afterClearStyles(['pointer-events']);
const wrapperAnimation = createAnimation()
.keyframes([
@@ -29,11 +33,7 @@ export const mdEnterAnimation = (
const { wrapperAnimation, backdropAnimation } = currentBreakpoint !== undefined ? createSheetEnterAnimation(opts) : createEnterAnimation();
backdropAnimation
- .addElement(root.querySelector('ion-backdrop')!)
- .beforeStyles({
- 'pointer-events': 'none'
- })
- .afterClearStyles(['pointer-events']);
+ .addElement(root.querySelector('ion-backdrop')!);
wrapperAnimation
.addElement(root.querySelector('.modal-wrapper')!);
diff --git a/core/src/components/modal/animations/sheet.ts b/core/src/components/modal/animations/sheet.ts
index 79902483cf..cac05102f9 100644
--- a/core/src/components/modal/animations/sheet.ts
+++ b/core/src/components/modal/animations/sheet.ts
@@ -16,6 +16,14 @@ export const createSheetEnterAnimation = (opts: ModalAnimationOptions) => {
const backdropAnimation = createAnimation('backdropAnimation')
.fromTo('opacity', 0, initialBackdrop);
+ if (shouldShowBackdrop) {
+ backdropAnimation
+ .beforeStyles({
+ 'pointer-events': 'none'
+ })
+ .afterClearStyles(['pointer-events']);
+ }
+
const wrapperAnimation = createAnimation('wrapperAnimation')
.keyframes([
{ offset: 0, opacity: 1, transform: 'translateY(100%)' },
diff --git a/core/src/components/modal/gestures/sheet.ts b/core/src/components/modal/gestures/sheet.ts
index 1dff393e4b..1b4230311e 100644
--- a/core/src/components/modal/gestures/sheet.ts
+++ b/core/src/components/modal/gestures/sheet.ts
@@ -56,10 +56,14 @@ export const createSheetGesture = (
animation.progressStart(true, 1 - currentBreakpoint);
/**
- * Backdrop should become enabled
- * after the backdropBreakpoint value
+ * If backdrop is not enabled, then content
+ * behind modal should be clickable. To do this, we need
+ * to remove pointer-events from ion-modal as a whole.
+ * ion-backdrop and .modal-wrapper always have pointer-events: auto
+ * applied, so the modal content can still be interacted with.
*/
const backdropEnabled = currentBreakpoint > backdropBreakpoint
+ baseEl.style.setProperty('pointer-events', backdropEnabled ? 'auto' : 'none');
backdropEl.style.setProperty('pointer-events', backdropEnabled ? 'auto' : 'none');
}
@@ -187,6 +191,7 @@ export const createSheetGesture = (
* after the backdropBreakpoint value
*/
const backdropEnabled = currentBreakpoint > backdropBreakpoint;
+ baseEl.style.setProperty('pointer-events', backdropEnabled ? 'auto' : 'none');
backdropEl.style.setProperty('pointer-events', backdropEnabled ? 'auto' : 'none');
gesture.enable(true);
diff --git a/core/src/components/modal/modal.scss b/core/src/components/modal/modal.scss
index 8a7b291cac..c7406e2e9e 100644
--- a/core/src/components/modal/modal.scss
+++ b/core/src/components/modal/modal.scss
@@ -50,6 +50,10 @@
contain: strict;
}
+.modal-wrapper, ion-backdrop {
+ pointer-events: auto;
+}
+
:host(.overlay-hidden) {
display: none;
}
diff --git a/core/src/components/modal/test/basic/e2e.ts b/core/src/components/modal/test/basic/e2e.ts
index bf0443fe4d..15b297ed8e 100644
--- a/core/src/components/modal/test/basic/e2e.ts
+++ b/core/src/components/modal/test/basic/e2e.ts
@@ -84,3 +84,15 @@ test('modal: htmlAttributes', async () => {
expect(attribute).toEqual('basic-modal');
});
+
+test('it should dismiss the modal when clicking the backdrop', async () => {
+ const page = await newE2EPage({ url: '/src/components/modal/test/basic?ionic:_testing=true' });
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#basic-modal');
+ await ionModalDidPresent.next();
+
+ await page.mouse.click(20, 20);
+ await ionModalDidDismiss.next();
+})
diff --git a/core/src/components/modal/test/sheet/e2e.ts b/core/src/components/modal/test/sheet/e2e.ts
index 2392b2cada..a63dff96d0 100644
--- a/core/src/components/modal/test/sheet/e2e.ts
+++ b/core/src/components/modal/test/sheet/e2e.ts
@@ -43,3 +43,47 @@ test('modal - open', async () => {
expect(screenshotCompare).toMatchScreenshot();
}
});
+
+test('should click to dismiss sheet modal', async () => {
+ const page = await newE2EPage({ url: '/src/components/modal/test/sheet?ionic:_testing=true' });
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#sheet-modal');
+
+ const modal = await page.find('ion-modal');
+ await modal.waitForVisible();
+
+ await page.mouse.click(50, 50);
+
+ await ionModalDidDismiss.next();
+});
+
+test('should click to dismiss sheet modal when backdrop is active', async () => {
+ const page = await newE2EPage({ url: '/src/components/modal/test/sheet?ionic:_testing=true' });
+ const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
+
+ await page.click('#backdrop-active');
+
+ const modal = await page.find('ion-modal');
+ await modal.waitForVisible();
+
+ await page.mouse.click(50, 50);
+
+ await ionModalDidDismiss.next();
+});
+
+test('should click to present another modal when backdrop is inactive', async () => {
+ const page = await newE2EPage({ url: '/src/components/modal/test/sheet?ionic:_testing=true' });
+ const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
+
+ await page.click('#backdrop-inactive');
+
+ await ionModalDidPresent.next();
+
+ await page.click('#custom-height-modal');
+
+ await ionModalDidPresent.next();
+
+ const customModal = await page.find('.custom-height');
+ expect(customModal).not.toBe(null);
+});
diff --git a/core/src/components/modal/test/sheet/index.html b/core/src/components/modal/test/sheet/index.html
index 7179436fa2..5eb650f219 100644
--- a/core/src/components/modal/test/sheet/index.html
+++ b/core/src/components/modal/test/sheet/index.html
@@ -93,6 +93,8 @@