mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(menu): preserve scroll position when focusing on open (#25044)
This commit is contained in:
@@ -398,6 +398,7 @@ export class Menu implements ComponentInterface, MenuI {
|
||||
}
|
||||
|
||||
this.beforeAnimation(shouldOpen);
|
||||
|
||||
await this.loadAnimation();
|
||||
await this.startAnimation(shouldOpen, animated);
|
||||
this.afterAnimation(shouldOpen);
|
||||
@@ -619,12 +620,17 @@ export class Menu implements ComponentInterface, MenuI {
|
||||
// emit open event
|
||||
this.ionDidOpen.emit();
|
||||
|
||||
// focus menu content for screen readers
|
||||
if (this.menuInnerEl) {
|
||||
this.focusFirstDescendant();
|
||||
/**
|
||||
* Move focus to the menu to prepare focus trapping, as long as
|
||||
* it isn't already focused. Use the host element instead of the
|
||||
* first descendant to avoid the scroll position jumping around.
|
||||
*/
|
||||
const focusedMenu = document.activeElement?.closest('ion-menu');
|
||||
if (focusedMenu !== this.el) {
|
||||
this.el.focus();
|
||||
}
|
||||
|
||||
// setup focus trapping
|
||||
// start focus trapping
|
||||
document.addEventListener('focus', this.handleFocus, true);
|
||||
} else {
|
||||
// remove css classes and unhide content from screen readers
|
||||
|
||||
@@ -28,11 +28,40 @@ test('menu: focus trap', async () => {
|
||||
await menu.waitForVisible();
|
||||
|
||||
let activeElID = await getActiveElementID(page);
|
||||
expect(activeElID).toEqual('start-menu-button');
|
||||
expect(activeElID).toEqual('start-menu');
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
activeElID = await getActiveElementID(page);
|
||||
expect(activeElID).toEqual('start-menu-button');
|
||||
|
||||
// do it again to make sure focus stays inside menu
|
||||
await page.keyboard.press('Tab');
|
||||
activeElID = await getActiveElementID(page);
|
||||
expect(activeElID).toEqual('start-menu-button');
|
||||
});
|
||||
|
||||
test('menu: preserve scroll position', async () => {
|
||||
const page = await newE2EPage({ url: '/src/components/menu/test/basic?ionic:_testing=true' });
|
||||
|
||||
await page.click('#open-first');
|
||||
const menu = await page.find('#start-menu');
|
||||
await menu.waitForVisible();
|
||||
|
||||
await page.$eval('#start-menu ion-content', (menuContentEl: any) => {
|
||||
return menuContentEl.scrollToPoint(0, 200);
|
||||
});
|
||||
|
||||
await menu.callMethod('close');
|
||||
|
||||
await page.click('#open-first');
|
||||
await menu.waitForVisible();
|
||||
|
||||
const scrollTop = await page.$eval('#start-menu ion-content', async (menuContentEl: any) => {
|
||||
const contentScrollEl = await menuContentEl.getScrollElement();
|
||||
return contentScrollEl.scrollTop;
|
||||
});
|
||||
|
||||
expect(scrollTop).toEqual(200);
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,21 @@
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
<ion-item>Menu Item</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
|
||||
@@ -18,7 +18,7 @@ test('menu: focus trap with overlays', async () => {
|
||||
await menu.callMethod('open');
|
||||
await ionDidOpen.next();
|
||||
|
||||
expect(await getActiveElementID(page)).toEqual('open-modal-button');
|
||||
expect(await getActiveElementID(page)).toEqual('menu');
|
||||
|
||||
const openModal = await page.find('#open-modal-button');
|
||||
await openModal.click();
|
||||
@@ -45,7 +45,7 @@ test('menu: focus trap with content inside overlays', async () => {
|
||||
await menu.callMethod('open');
|
||||
await ionDidOpen.next();
|
||||
|
||||
expect(await getActiveElementID(page)).toEqual('open-modal-button');
|
||||
expect(await getActiveElementID(page)).toEqual('menu');
|
||||
|
||||
const openModal = await page.find('#open-modal-button');
|
||||
await openModal.click();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-menu content-id="main">
|
||||
<ion-menu content-id="main" id="menu">
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Menu</ion-title>
|
||||
|
||||
Reference in New Issue
Block a user