mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
feat(modal): data and role are passed to canDismiss (#26384)
resolves #26292
This commit is contained in:
@ -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
|
||||
|
||||
4
core/src/components.d.ts
vendored
4
core/src/components.d.ts
vendored
@ -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.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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';
|
||||
|
||||
Reference in New Issue
Block a user