feat(modal): data and role are passed to canDismiss (#26384)

resolves #26292
This commit is contained in:
Liam DeBeasi
2022-12-02 10:46:41 -05:00
committed by GitHub
parent 5d035abdb0
commit 1b30fc97d3
7 changed files with 66 additions and 17 deletions

View File

@ -780,7 +780,7 @@ ion-modal,prop,animated,boolean,true,false,false
ion-modal,prop,backdropBreakpoint,number,0,false,false
ion-modal,prop,backdropDismiss,boolean,true,false,false
ion-modal,prop,breakpoints,number[] | undefined,undefined,false,false
ion-modal,prop,canDismiss,(() => Promise<boolean>) | boolean | undefined,undefined,false,false
ion-modal,prop,canDismiss,((data?: any, role?: string | undefined) => Promise<boolean>) | boolean | undefined,undefined,false,false
ion-modal,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-modal,prop,handle,boolean | undefined,undefined,false,false
ion-modal,prop,handleBehavior,"cycle" | "none" | undefined,'none',false,false

View File

@ -1560,7 +1560,7 @@ export namespace Components {
/**
* Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss.
*/
"canDismiss"?: undefined | boolean | (() => Promise<boolean>);
"canDismiss"?: undefined | boolean | ((data?: any, role?: string) => Promise<boolean>);
/**
* The component to display inside of the modal.
*/
@ -5548,7 +5548,7 @@ declare namespace LocalJSX {
/**
* Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss.
*/
"canDismiss"?: undefined | boolean | (() => Promise<boolean>);
"canDismiss"?: undefined | boolean | ((data?: any, role?: string) => Promise<boolean>);
/**
* The component to display inside of the modal.
*/

View File

@ -1,3 +1,5 @@
import { GESTURE } from '@utils/overlays';
import type { Animation } from '../../../interface';
export const handleCanDismiss = async (el: HTMLIonModalElement, animation: Animation) => {
@ -18,7 +20,7 @@ export const handleCanDismiss = async (el: HTMLIonModalElement, animation: Anima
* If the function returns `true`,
* then we can proceed with dismiss.
*/
const shouldDismiss = await el.canDismiss();
const shouldDismiss = await el.canDismiss(undefined, GESTURE);
if (!shouldDismiss) {
return;
}

View File

@ -23,7 +23,15 @@ import type { Attributes } from '../../utils/helpers';
import { KEYBOARD_DID_OPEN } from '../../utils/keyboard/keyboard';
import { printIonWarning } from '../../utils/logging';
import { Style as StatusBarStyle, StatusBar } from '../../utils/native/status-bar';
import { BACKDROP, activeAnimations, dismiss, eventMethod, prepareOverlay, present } from '../../utils/overlays';
import {
GESTURE,
BACKDROP,
activeAnimations,
dismiss,
eventMethod,
prepareOverlay,
present,
} from '../../utils/overlays';
import { getClassMap } from '../../utils/theme';
import { deepReady } from '../../utils/transition';
@ -267,7 +275,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
* If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss.
* If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss.
*/
@Prop() canDismiss?: undefined | boolean | (() => Promise<boolean>);
@Prop() canDismiss?: undefined | boolean | ((data?: any, role?: string) => Promise<boolean>);
/**
* Emitted after the modal has presented.
@ -449,7 +457,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
* modal is allowed to dismiss based
* on the state of the canDismiss prop.
*/
private async checkCanDismiss() {
private async checkCanDismiss(data?: any, role?: string) {
const { canDismiss } = this;
/**
@ -461,7 +469,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
}
if (typeof canDismiss === 'function') {
return canDismiss();
return canDismiss(data, role);
}
return canDismiss;
@ -603,7 +611,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
*/
this.gestureAnimationDismissing = true;
this.animation!.onFinish(async () => {
await this.dismiss(undefined, 'gesture');
await this.dismiss(undefined, GESTURE);
this.gestureAnimationDismissing = false;
});
});
@ -665,7 +673,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
this.animation!.onFinish(async () => {
this.currentBreakpoint = 0;
this.ionBreakpointDidChange.emit({ breakpoint: this.currentBreakpoint });
await this.dismiss(undefined, 'gesture');
await this.dismiss(undefined, GESTURE);
this.gestureAnimationDismissing = false;
});
}
@ -678,7 +686,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
*/
@Method()
async dismiss(data?: any, role?: string): Promise<boolean> {
if (this.gestureAnimationDismissing && role !== 'gesture') {
if (this.gestureAnimationDismissing && role !== GESTURE) {
return false;
}
@ -687,7 +695,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
* for calling the dismiss method, we should
* not run the canDismiss check again.
*/
if (role !== 'handler' && !(await this.checkCanDismiss())) {
if (role !== 'handler' && !(await this.checkCanDismiss(data, role))) {
return false;
}

View File

@ -116,13 +116,13 @@
handler = false;
break;
case 'promise-true':
handler = () => {
handler = (data, role) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
setTimeout(() => {
const event = new CustomEvent('ionHandlerDone');
const event = new CustomEvent('ionHandlerDone', { detail: { data, role } });
window.dispatchEvent(event);
}, 1000);
}, 250);
@ -132,11 +132,11 @@
case 'promise-false':
handler = () => {
return new Promise((resolve) => {
setTimeout(() => {
setTimeout((data, role) => {
resolve(false);
setTimeout(() => {
const event = new CustomEvent('ionHandlerDone');
const event = new CustomEvent('ionHandlerDone', { detail: { data, role } });
window.dispatchEvent(event);
}, 1000);
}, 250);

View File

@ -378,4 +378,42 @@ test.describe('modal: canDismiss', () => {
await ionModalDidDismiss.next();
});
});
test.describe('function params', () => {
test.beforeEach(({ skip }) => {
skip.rtl();
skip.mode('md');
});
test('should pass data and role when calling dismiss', async ({ page }) => {
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const ionHandlerDone = await page.spyOnEvent('ionHandlerDone');
await page.click('#radio-promise-true');
await page.click('#show-modal');
await ionModalDidPresent.next();
const modal = await page.locator('ion-modal');
await modal.evaluate((el: HTMLIonModalElement) => el.dismiss('my data', 'my role'));
await ionHandlerDone.next();
await expect(ionHandlerDone).toHaveReceivedEventDetail({ data: 'my data', role: 'my role' });
});
test('should pass data and role when swiping', async ({ page }) => {
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
const ionHandlerDone = await page.spyOnEvent('ionHandlerDone');
await page.click('#radio-card');
await page.click('#radio-promise-true');
await page.click('#show-modal');
await ionModalDidPresent.next();
const modalHeader = await page.locator('#modal-header');
await dragElementBy(modalHeader, page, 0, 500);
await ionHandlerDone.next();
await expect(ionHandlerDone).toHaveReceivedEventDetail({ data: undefined, role: 'gesture' });
});
});
});

View File

@ -507,7 +507,7 @@ export const dismiss = async <OverlayDismissOptions>(
: config.get(name, mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
// If dismissed via gesture, no need to play leaving animation again
if (role !== 'gesture') {
if (role !== GESTURE) {
await overlayAnimation(overlay, animationBuilder, overlay.el, opts);
}
overlay.didDismiss.emit({ data, role });
@ -612,3 +612,4 @@ export const safeCall = (handler: any, arg?: any) => {
};
export const BACKDROP = 'backdrop';
export const GESTURE = 'gesture';