mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +08:00
fix(modal): backdropBreakpoint allows interactivity behind sheet (#24798)
resolves #24797
This commit is contained in:
@ -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')!)
|
||||
|
@ -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')!);
|
||||
|
@ -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%)' },
|
||||
|
@ -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);
|
||||
|
@ -50,6 +50,10 @@
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.modal-wrapper, ion-backdrop {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
:host(.overlay-hidden) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -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();
|
||||
})
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -93,6 +93,8 @@
|
||||
<ion-button id="custom-height-modal" onclick="presentModal({ cssClass: 'custom-height' })">Present Sheet Modal (Custom Height)</ion-button>
|
||||
<ion-button id="custom-handle-modal" onclick="presentModal({ cssClass: 'custom-handle' })">Present Sheet Modal (Custom Handle)</ion-button>
|
||||
<ion-button id="no-handle-modal" onclick="presentModal({ handle: false })">Present Sheet Modal (No Handle)</ion-button>
|
||||
<ion-button id="backdrop-active" onclick="presentModal({ backdropBreakpoint: 0.3, initialBreakpoint: 0.5, breakpoints: [0.3, 0.5, 0.7, 1] })">Backdrop is active</ion-button>
|
||||
<ion-button id="backdrop-inactive" onclick="presentModal({ cssClass: 'backdrop-inactive', backdropBreakpoint: 0.5, initialBreakpoint: 0.3, breakpoints: [0.3, 0.5, 0.7, 1] })">Backdrop is inactive</ion-button>
|
||||
|
||||
<div class="grid">
|
||||
<div class="grid-item red"></div>
|
||||
|
Reference in New Issue
Block a user