mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 19:57:22 +08:00
fix(overlays): getTop now returns the top-most presented overlay (#24547)
Resolves #19111
This commit is contained in:
@ -120,6 +120,8 @@ export const focusFirstDescendant = (ref: Element, overlay: HTMLIonOverlayElemen
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isOverlayHidden = (overlay: Element) => overlay.classList.contains('overlay-hidden');
|
||||||
|
|
||||||
const focusLastDescendant = (ref: Element, overlay: HTMLIonOverlayElement) => {
|
const focusLastDescendant = (ref: Element, overlay: HTMLIonOverlayElement) => {
|
||||||
const inputs = Array.from(ref.querySelectorAll(focusableQueryString)) as HTMLElement[];
|
const inputs = Array.from(ref.querySelectorAll(focusableQueryString)) as HTMLElement[];
|
||||||
let lastInput = inputs.length > 0 ? inputs[inputs.length - 1] : null;
|
let lastInput = inputs.length > 0 ? inputs[inputs.length - 1] : null;
|
||||||
@ -291,7 +293,7 @@ export const connectListeners = (doc: Document) => {
|
|||||||
|
|
||||||
// handle back-button click
|
// handle back-button click
|
||||||
doc.addEventListener('ionBackButton', ev => {
|
doc.addEventListener('ionBackButton', ev => {
|
||||||
const lastOverlay = getTopOpenOverlay(doc);
|
const lastOverlay = getOverlay(doc);
|
||||||
if (lastOverlay && lastOverlay.backdropDismiss) {
|
if (lastOverlay && lastOverlay.backdropDismiss) {
|
||||||
(ev as BackButtonEvent).detail.register(OVERLAY_BACK_BUTTON_PRIORITY, () => {
|
(ev as BackButtonEvent).detail.register(OVERLAY_BACK_BUTTON_PRIORITY, () => {
|
||||||
return lastOverlay.dismiss(undefined, BACKDROP);
|
return lastOverlay.dismiss(undefined, BACKDROP);
|
||||||
@ -302,7 +304,7 @@ export const connectListeners = (doc: Document) => {
|
|||||||
// handle ESC to close overlay
|
// handle ESC to close overlay
|
||||||
doc.addEventListener('keyup', ev => {
|
doc.addEventListener('keyup', ev => {
|
||||||
if (ev.key === 'Escape') {
|
if (ev.key === 'Escape') {
|
||||||
const lastOverlay = getTopOpenOverlay(doc);
|
const lastOverlay = getOverlay(doc);
|
||||||
if (lastOverlay && lastOverlay.backdropDismiss) {
|
if (lastOverlay && lastOverlay.backdropDismiss) {
|
||||||
lastOverlay.dismiss(undefined, BACKDROP);
|
lastOverlay.dismiss(undefined, BACKDROP);
|
||||||
}
|
}
|
||||||
@ -328,30 +330,14 @@ export const getOverlays = (doc: Document, selector?: string): HTMLIonOverlayEle
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the top-most/last opened
|
* Returns an overlay element
|
||||||
* overlay that is currently presented.
|
* @param doc The document to find the element within.
|
||||||
|
* @param overlayTag The selector for the overlay, defaults to Ionic overlay components.
|
||||||
|
* @param id The unique identifier for the overlay instance.
|
||||||
|
* @returns The overlay element or `undefined` if no overlay element is found.
|
||||||
*/
|
*/
|
||||||
const getTopOpenOverlay = (doc: Document): HTMLIonOverlayElement | undefined => {
|
const getOverlay = (doc: Document, overlayTag?: string, id?: string): HTMLIonOverlayElement | undefined => {
|
||||||
const overlays = getOverlays(doc);
|
const overlays = getOverlays(doc, overlayTag).filter(o => !isOverlayHidden(o));
|
||||||
for (let i = overlays.length - 1; i >= 0; i--) {
|
|
||||||
const overlay = overlays[i];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only consider overlays that
|
|
||||||
* are presented. Presented overlays
|
|
||||||
* will not have the .overlay-hidden
|
|
||||||
* class on the host.
|
|
||||||
*/
|
|
||||||
if (!overlay.classList.contains('overlay-hidden')) {
|
|
||||||
return overlay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getOverlay = (doc: Document, overlayTag?: string, id?: string): HTMLIonOverlayElement | undefined => {
|
|
||||||
const overlays = getOverlays(doc, overlayTag);
|
|
||||||
return (id === undefined)
|
return (id === undefined)
|
||||||
? overlays[overlays.length - 1]
|
? overlays[overlays.length - 1]
|
||||||
: overlays.find(o => o.id === id);
|
: overlays.find(o => o.id === id);
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" dir="ltr">
|
<html lang="en" dir="ltr">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Overlays</title>
|
<title>Overlays</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
|
||||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
|
||||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||||
@ -12,6 +14,7 @@
|
|||||||
window.modalController = modalController;
|
window.modalController = modalController;
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<ion-app>
|
<ion-app>
|
||||||
<ion-menu content-id="main-content">
|
<ion-menu content-id="main-content">
|
||||||
@ -28,6 +31,7 @@
|
|||||||
|
|
||||||
<ion-content class="ion-padding">
|
<ion-content class="ion-padding">
|
||||||
<ion-button id="create" onclick="createModal()">Create a Modal</ion-button>
|
<ion-button id="create" onclick="createModal()">Create a Modal</ion-button>
|
||||||
|
<ion-button id="create-nested" onclick="createNestedOverlayModal()">Create Nested Overlay Modal</ion-button>
|
||||||
<ion-button id="present" onclick="presentHiddenModal()">Present a Hidden Modal</ion-button>
|
<ion-button id="present" onclick="presentHiddenModal()">Present a Hidden Modal</ion-button>
|
||||||
<ion-button id="create-and-present" onclick="createAndPresentModal()">Create and Present a Modal</ion-button>
|
<ion-button id="create-and-present" onclick="createAndPresentModal()">Create and Present a Modal</ion-button>
|
||||||
<ion-button id="simulate" onclick="backButton()">Simulate Hardware Back Button</ion-button>
|
<ion-button id="simulate" onclick="backButton()">Simulate Hardware Back Button</ion-button>
|
||||||
@ -87,6 +91,47 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createNestedOverlayModal = async () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = `
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Modal</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
Modal Content
|
||||||
|
|
||||||
|
<ion-button id="modal-nested-overlay">Datetime inline modal</ion-button>
|
||||||
|
<ion-button id="dismiss-modal-nested-overlay">Dismiss nested overlay</ion-button>
|
||||||
|
|
||||||
|
<ion-modal trigger="modal-nested-overlay">
|
||||||
|
<ion-content>
|
||||||
|
<ion-datetime show-default-buttons></ion-datetime>
|
||||||
|
</ion-content>
|
||||||
|
</ion-modal>
|
||||||
|
|
||||||
|
</ion-content>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const dismissModalNestedOverlay = div.querySelector('ion-button#dismiss-modal-nested-overlay');
|
||||||
|
dismissModalNestedOverlay.onclick = () => {
|
||||||
|
window.modalController.getTop().then(top => {
|
||||||
|
top.dismiss();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = await modalController.create({
|
||||||
|
component: div
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
await modal.present();
|
||||||
|
|
||||||
|
return modal;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
window.Ionic = {
|
window.Ionic = {
|
||||||
config: {
|
config: {
|
||||||
hardwareBackButton: true
|
hardwareBackButton: true
|
||||||
@ -95,4 +140,5 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { newE2EPage } from '@stencil/core/testing';
|
import { newE2EPage } from '@stencil/core/testing';
|
||||||
|
|
||||||
test('overlays: hardware back button: should dismss a presented overlay', async () => {
|
test('overlays: hardware back button: should dismiss a presented overlay', async () => {
|
||||||
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
||||||
|
|
||||||
const createAndPresentButton = await page.find('#create-and-present');
|
const createAndPresentButton = await page.find('#create-and-present');
|
||||||
@ -24,7 +24,7 @@ test('overlays: hardware back button: should dismss a presented overlay', async
|
|||||||
await page.waitForSelector('ion-modal', { hidden: true })
|
await page.waitForSelector('ion-modal', { hidden: true })
|
||||||
});
|
});
|
||||||
|
|
||||||
test('overlays: hardware back button: should dismss the presented overlay, even though another hidden modal was added last', async () => {
|
test('overlays: hardware back button: should dismiss the presented overlay, even though another hidden modal was added last', async () => {
|
||||||
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
||||||
|
|
||||||
const createAndPresentButton = await page.find('#create-and-present');
|
const createAndPresentButton = await page.find('#create-and-present');
|
||||||
@ -56,7 +56,7 @@ test('overlays: hardware back button: should dismss the presented overlay, even
|
|||||||
expect(await modals[1].evaluate(node => node.classList.contains('overlay-hidden'))).toEqual(true);
|
expect(await modals[1].evaluate(node => node.classList.contains('overlay-hidden'))).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('overlays: Esc: should dismss a presented overlay', async () => {
|
test('overlays: Esc: should dismiss a presented overlay', async () => {
|
||||||
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
||||||
|
|
||||||
const createAndPresentButton = await page.find('#create-and-present');
|
const createAndPresentButton = await page.find('#create-and-present');
|
||||||
@ -78,7 +78,7 @@ test('overlays: Esc: should dismss a presented overlay', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('overlays: Esc: should dismss the presented overlay, even though another hidden modal was added last', async () => {
|
test('overlays: Esc: should dismiss the presented overlay, even though another hidden modal was added last', async () => {
|
||||||
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
||||||
|
|
||||||
const createAndPresentButton = await page.find('#create-and-present');
|
const createAndPresentButton = await page.find('#create-and-present');
|
||||||
@ -102,3 +102,21 @@ test('overlays: Esc: should dismss the presented overlay, even though another hi
|
|||||||
|
|
||||||
await page.waitForSelector('ion-modal#ion-overlay-1', { hidden: true });
|
await page.waitForSelector('ion-modal#ion-overlay-1', { hidden: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('overlays: Nested: should dismiss the top overlay', async () => {
|
||||||
|
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
|
||||||
|
|
||||||
|
const createNestedButton = await page.find('#create-nested');
|
||||||
|
|
||||||
|
await createNestedButton.click();
|
||||||
|
|
||||||
|
const modal = await page.find('ion-modal');
|
||||||
|
expect(modal).not.toBe(null);
|
||||||
|
|
||||||
|
const dismissNestedOverlayButton = await page.find('#dismiss-modal-nested-overlay');
|
||||||
|
await dismissNestedOverlayButton.click();
|
||||||
|
|
||||||
|
const modals = await page.$$('ion-modal');
|
||||||
|
expect(modals.length).toEqual(0);
|
||||||
|
|
||||||
|
});
|
||||||
|
Reference in New Issue
Block a user