Skip to content

Commit 1b30fc9

Browse files
authoredDec 2, 2022
feat(modal): data and role are passed to canDismiss (#26384)
resolves #26292
1 parent 5d035ab commit 1b30fc9

File tree

7 files changed

+66
-17
lines changed

7 files changed

+66
-17
lines changed
 

‎core/api.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ ion-modal,prop,animated,boolean,true,false,false
780780
ion-modal,prop,backdropBreakpoint,number,0,false,false
781781
ion-modal,prop,backdropDismiss,boolean,true,false,false
782782
ion-modal,prop,breakpoints,number[] | undefined,undefined,false,false
783-
ion-modal,prop,canDismiss,(() => Promise<boolean>) | boolean | undefined,undefined,false,false
783+
ion-modal,prop,canDismiss,((data?: any, role?: string | undefined) => Promise<boolean>) | boolean | undefined,undefined,false,false
784784
ion-modal,prop,enterAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
785785
ion-modal,prop,handle,boolean | undefined,undefined,false,false
786786
ion-modal,prop,handleBehavior,"cycle" | "none" | undefined,'none',false,false

‎core/src/components.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ export namespace Components {
15601560
/**
15611561
* 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.
15621562
*/
1563-
"canDismiss"?: undefined | boolean | (() => Promise<boolean>);
1563+
"canDismiss"?: undefined | boolean | ((data?: any, role?: string) => Promise<boolean>);
15641564
/**
15651565
* The component to display inside of the modal.
15661566
*/
@@ -5548,7 +5548,7 @@ declare namespace LocalJSX {
55485548
/**
55495549
* 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.
55505550
*/
5551-
"canDismiss"?: undefined | boolean | (() => Promise<boolean>);
5551+
"canDismiss"?: undefined | boolean | ((data?: any, role?: string) => Promise<boolean>);
55525552
/**
55535553
* The component to display inside of the modal.
55545554
*/

‎core/src/components/modal/gestures/utils.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { GESTURE } from '@utils/overlays';
2+
13
import type { Animation } from '../../../interface';
24

35
export const handleCanDismiss = async (el: HTMLIonModalElement, animation: Animation) => {
@@ -18,7 +20,7 @@ export const handleCanDismiss = async (el: HTMLIonModalElement, animation: Anima
1820
* If the function returns `true`,
1921
* then we can proceed with dismiss.
2022
*/
21-
const shouldDismiss = await el.canDismiss();
23+
const shouldDismiss = await el.canDismiss(undefined, GESTURE);
2224
if (!shouldDismiss) {
2325
return;
2426
}

‎core/src/components/modal/modal.tsx

+16-8
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ import type { Attributes } from '../../utils/helpers';
2323
import { KEYBOARD_DID_OPEN } from '../../utils/keyboard/keyboard';
2424
import { printIonWarning } from '../../utils/logging';
2525
import { Style as StatusBarStyle, StatusBar } from '../../utils/native/status-bar';
26-
import { BACKDROP, activeAnimations, dismiss, eventMethod, prepareOverlay, present } from '../../utils/overlays';
26+
import {
27+
GESTURE,
28+
BACKDROP,
29+
activeAnimations,
30+
dismiss,
31+
eventMethod,
32+
prepareOverlay,
33+
present,
34+
} from '../../utils/overlays';
2735
import { getClassMap } from '../../utils/theme';
2836
import { deepReady } from '../../utils/transition';
2937

@@ -267,7 +275,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
267275
* If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss.
268276
* If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss.
269277
*/
270-
@Prop() canDismiss?: undefined | boolean | (() => Promise<boolean>);
278+
@Prop() canDismiss?: undefined | boolean | ((data?: any, role?: string) => Promise<boolean>);
271279

272280
/**
273281
* Emitted after the modal has presented.
@@ -449,7 +457,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
449457
* modal is allowed to dismiss based
450458
* on the state of the canDismiss prop.
451459
*/
452-
private async checkCanDismiss() {
460+
private async checkCanDismiss(data?: any, role?: string) {
453461
const { canDismiss } = this;
454462

455463
/**
@@ -461,7 +469,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
461469
}
462470

463471
if (typeof canDismiss === 'function') {
464-
return canDismiss();
472+
return canDismiss(data, role);
465473
}
466474

467475
return canDismiss;
@@ -603,7 +611,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
603611
*/
604612
this.gestureAnimationDismissing = true;
605613
this.animation!.onFinish(async () => {
606-
await this.dismiss(undefined, 'gesture');
614+
await this.dismiss(undefined, GESTURE);
607615
this.gestureAnimationDismissing = false;
608616
});
609617
});
@@ -665,7 +673,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
665673
this.animation!.onFinish(async () => {
666674
this.currentBreakpoint = 0;
667675
this.ionBreakpointDidChange.emit({ breakpoint: this.currentBreakpoint });
668-
await this.dismiss(undefined, 'gesture');
676+
await this.dismiss(undefined, GESTURE);
669677
this.gestureAnimationDismissing = false;
670678
});
671679
}
@@ -678,7 +686,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
678686
*/
679687
@Method()
680688
async dismiss(data?: any, role?: string): Promise<boolean> {
681-
if (this.gestureAnimationDismissing && role !== 'gesture') {
689+
if (this.gestureAnimationDismissing && role !== GESTURE) {
682690
return false;
683691
}
684692

@@ -687,7 +695,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
687695
* for calling the dismiss method, we should
688696
* not run the canDismiss check again.
689697
*/
690-
if (role !== 'handler' && !(await this.checkCanDismiss())) {
698+
if (role !== 'handler' && !(await this.checkCanDismiss(data, role))) {
691699
return false;
692700
}
693701

‎core/src/components/modal/test/canDismiss/index.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,13 @@
116116
handler = false;
117117
break;
118118
case 'promise-true':
119-
handler = () => {
119+
handler = (data, role) => {
120120
return new Promise((resolve) => {
121121
setTimeout(() => {
122122
resolve(true);
123123

124124
setTimeout(() => {
125-
const event = new CustomEvent('ionHandlerDone');
125+
const event = new CustomEvent('ionHandlerDone', { detail: { data, role } });
126126
window.dispatchEvent(event);
127127
}, 1000);
128128
}, 250);
@@ -132,11 +132,11 @@
132132
case 'promise-false':
133133
handler = () => {
134134
return new Promise((resolve) => {
135-
setTimeout(() => {
135+
setTimeout((data, role) => {
136136
resolve(false);
137137

138138
setTimeout(() => {
139-
const event = new CustomEvent('ionHandlerDone');
139+
const event = new CustomEvent('ionHandlerDone', { detail: { data, role } });
140140
window.dispatchEvent(event);
141141
}, 1000);
142142
}, 250);

‎core/src/components/modal/test/canDismiss/modal.e2e.ts

+38
Original file line numberDiff line numberDiff line change
@@ -378,4 +378,42 @@ test.describe('modal: canDismiss', () => {
378378
await ionModalDidDismiss.next();
379379
});
380380
});
381+
382+
test.describe('function params', () => {
383+
test.beforeEach(({ skip }) => {
384+
skip.rtl();
385+
skip.mode('md');
386+
});
387+
test('should pass data and role when calling dismiss', async ({ page }) => {
388+
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
389+
const ionHandlerDone = await page.spyOnEvent('ionHandlerDone');
390+
391+
await page.click('#radio-promise-true');
392+
await page.click('#show-modal');
393+
394+
await ionModalDidPresent.next();
395+
396+
const modal = await page.locator('ion-modal');
397+
await modal.evaluate((el: HTMLIonModalElement) => el.dismiss('my data', 'my role'));
398+
399+
await ionHandlerDone.next();
400+
await expect(ionHandlerDone).toHaveReceivedEventDetail({ data: 'my data', role: 'my role' });
401+
});
402+
test('should pass data and role when swiping', async ({ page }) => {
403+
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
404+
const ionHandlerDone = await page.spyOnEvent('ionHandlerDone');
405+
406+
await page.click('#radio-card');
407+
await page.click('#radio-promise-true');
408+
await page.click('#show-modal');
409+
410+
await ionModalDidPresent.next();
411+
412+
const modalHeader = await page.locator('#modal-header');
413+
await dragElementBy(modalHeader, page, 0, 500);
414+
415+
await ionHandlerDone.next();
416+
await expect(ionHandlerDone).toHaveReceivedEventDetail({ data: undefined, role: 'gesture' });
417+
});
418+
});
381419
});

‎core/src/utils/overlays.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ export const dismiss = async <OverlayDismissOptions>(
507507
: config.get(name, mode === 'ios' ? iosLeaveAnimation : mdLeaveAnimation);
508508

509509
// If dismissed via gesture, no need to play leaving animation again
510-
if (role !== 'gesture') {
510+
if (role !== GESTURE) {
511511
await overlayAnimation(overlay, animationBuilder, overlay.el, opts);
512512
}
513513
overlay.didDismiss.emit({ data, role });
@@ -612,3 +612,4 @@ export const safeCall = (handler: any, arg?: any) => {
612612
};
613613

614614
export const BACKDROP = 'backdrop';
615+
export const GESTURE = 'gesture';

0 commit comments

Comments
 (0)
Please sign in to comment.