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 @@ Present Sheet Modal (Custom Height) Present Sheet Modal (Custom Handle) Present Sheet Modal (No Handle) + Backdrop is active + Backdrop is inactive