feat(alert): accept Promise for button handler (#25702)

Resolves #25700

Co-authored-by: Sean Perkins <sean@ionic.io>
This commit is contained in:
Hans Krywalsky
2022-08-03 17:33:37 +02:00
committed by GitHub
parent 11c69c8df5
commit 8e4783c172
4 changed files with 68 additions and 9 deletions

View File

@ -53,10 +53,12 @@ export type AlertTextareaAttributes = { [key: string]: any };
*/ */
export type AlertInputAttributes = { [key: string]: any }; export type AlertInputAttributes = { [key: string]: any };
type AlertButtonOverlayHandler = boolean | void | { [key: string]: any };
export interface AlertButton { export interface AlertButton {
text: string; text: string;
role?: 'cancel' | 'destructive' | string; role?: 'cancel' | 'destructive' | string;
cssClass?: string | string[]; cssClass?: string | string[];
id?: string; id?: string;
handler?: (value: any) => boolean | void | { [key: string]: any }; handler?: (value: any) => AlertButtonOverlayHandler | Promise<AlertButtonOverlayHandler>;
} }

View File

@ -335,24 +335,24 @@ export class Alert implements ComponentInterface, OverlayInterface {
forceUpdate(this); forceUpdate(this);
} }
private buttonClick(button: AlertButton) { private async buttonClick(button: AlertButton) {
const role = button.role; const role = button.role;
const values = this.getValues(); const values = this.getValues();
if (isCancel(role)) { if (isCancel(role)) {
return this.dismiss({ values }, role); return this.dismiss({ values }, role);
} }
const returnData = this.callButtonHandler(button, values); const returnData = await this.callButtonHandler(button, values);
if (returnData !== false) { if (returnData !== false) {
return this.dismiss({ values, ...returnData }, button.role); return this.dismiss({ values, ...returnData }, button.role);
} }
return Promise.resolve(false); return false;
} }
private callButtonHandler(button: AlertButton | undefined, data?: any) { private async callButtonHandler(button: AlertButton | undefined, data?: any) {
if (button?.handler) { if (button?.handler) {
// a handler has been provided, execute it // a handler has been provided, execute it
// pass the handler the values from the inputs // pass the handler the values from the inputs
const returnData = safeCall(button.handler, data); const returnData = await safeCall(button.handler, data);
if (returnData === false) { if (returnData === false) {
// if the return value of the handler is false then do not dismiss // if the return value of the handler is false then do not dismiss
return false; return false;

View File

@ -0,0 +1,28 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('alert: basic', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/src/components/alert/test/basic');
});
test('should dismiss when async handler resolves', async ({ page }) => {
const ionAlertDidPresent = await page.spyOnEvent('ionAlertDidPresent');
const ionAlertDidDismiss = await page.spyOnEvent('ionAlertDidDismiss');
const ionLoadingDidDismiss = await page.spyOnEvent('ionLoadingDidDismiss');
const alert = page.locator('ion-alert');
await page.click('#asyncHandler');
await ionAlertDidPresent.next();
await page.click('.alert-button');
await expect(alert).toBeVisible();
await ionLoadingDidDismiss.next();
await ionAlertDidDismiss.next();
await expect(alert).toBeHidden();
});
});

View File

@ -14,8 +14,9 @@
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script> <script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head> </head>
<script type="module"> <script type="module">
import { alertController } from '../../../../dist/ionic/index.esm.js'; import { alertController, loadingController } from '../../../../dist/ionic/index.esm.js';
window.alertController = alertController; window.alertController = alertController;
window.loadingController = loadingController;
</script> </script>
<body> <body>
@ -30,9 +31,12 @@
<ion-button id="basic" expand="block" onclick="presentAlert()">Alert</ion-button> <ion-button id="basic" expand="block" onclick="presentAlert()">Alert</ion-button>
<ion-button id="longMessage" expand="block" onclick="presentAlertLongMessage()">Alert Long Message</ion-button> <ion-button id="longMessage" expand="block" onclick="presentAlertLongMessage()">Alert Long Message</ion-button>
<ion-button id="multipleButtons" expand="block" onclick="presentAlertMultipleButtons()" <ion-button id="multipleButtons" expand="block" onclick="presentAlertMultipleButtons()"
>Multiple Buttons (>2)</ion-button >Multiple Buttons (>2)
> </ion-button>
<ion-button id="noMessage" expand="block" onclick="presentAlertNoMessage()">Alert No Message</ion-button> <ion-button id="noMessage" expand="block" onclick="presentAlertNoMessage()">Alert No Message</ion-button>
<ion-button id="asyncHandler" expand="block" onclick="presentAlertAsyncHandler()"
>Alert Async Handler</ion-button
>
<ion-button id="confirm" expand="block" onclick="presentAlertConfirm()">Confirm</ion-button> <ion-button id="confirm" expand="block" onclick="presentAlertConfirm()">Confirm</ion-button>
<ion-button id="prompt" expand="block" onclick="presentAlertPrompt()">Prompt</ion-button> <ion-button id="prompt" expand="block" onclick="presentAlertPrompt()">Prompt</ion-button>
<ion-button id="radio" expand="block" onclick="presentAlertRadio()">Radio</ion-button> <ion-button id="radio" expand="block" onclick="presentAlertRadio()">Radio</ion-button>
@ -46,6 +50,7 @@
--min-width: 0; --min-width: 0;
--max-width: 200px; --max-width: 200px;
} }
#delete-button { #delete-button {
color: #eb445a; color: #eb445a;
} }
@ -351,6 +356,30 @@
], ],
}); });
} }
function presentAlertAsyncHandler() {
openAlert({
header: 'Alert',
message: 'This is an alert with async handlers',
buttons: [
{
text: 'Confirm',
handler: () => {
return new Promise(async (resolve) => {
const loading = await loadingController.create({
message: 'Please wait...',
});
await loading.present();
setTimeout(async () => {
await loading.dismiss();
resolve();
}, 1000);
});
},
},
],
});
}
</script> </script>
</body> </body>
</html> </html>