Merge branch 'feature-7.5' into FW-4612

This commit is contained in:
Liam DeBeasi
2023-10-10 12:18:37 -04:00
committed by GitHub
5 changed files with 191 additions and 34 deletions

View File

@@ -30,7 +30,6 @@ export interface MenuControllerI {
_setOpen(menu: MenuI, shouldOpen: boolean, animated: boolean): Promise<boolean>;
_register(menu: MenuI): void;
_unregister(menu: MenuI): void;
_setActiveMenu(menu: MenuI): void;
getMenus(): Promise<HTMLIonMenuElement[]>;
getOpenSync(): HTMLIonMenuElement | undefined;

View File

@@ -1,7 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Listen, Method, Prop, State, Watch, h } from '@stencil/core';
import { getTimeGivenProgression } from '@utils/animation/cubic-bezier';
import { doc } from '@utils/browser';
import { GESTURE_CONTROLLER } from '@utils/gesture';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers';
@@ -762,18 +761,6 @@ export class Menu implements ComponentInterface, MenuI {
*/
this.afterAnimation(false);
}
if (doc?.contains(this.el)) {
/**
* Only set the active menu if the menu element is
* present in the DOM. Otherwise if it was destructively
* re-hydrated (through Angular Universal), then ignore
* setting the removed node as the active menu.
*/
if (!this.disabled) {
menuController._setActiveMenu(this);
}
}
}
render() {

View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Menu - Multiple</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"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { menuController } from '../../../../dist/ionic/index.esm.js';
window.menuController = menuController;
</script>
</head>
<body>
<ion-app>
<ion-menu side="start" menu-id="primary-menu" id="primary-menu" content-id="main">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Primary</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<ion-menu side="start" menu-id="secondary-menu" id="secondary-menu" content-id="main">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Secondary</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<ion-menu disabled="true" side="end" menu-id="success-menu" id="success-menu" content-id="main">
<ion-header>
<ion-toolbar color="success">
<ion-title>Success</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<ion-menu disabled="true" side="end" menu-id="danger-menu" id="danger-menu" content-id="main">
<ion-header>
<ion-toolbar color="danger">
<ion-title>Danger</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button menu="primary-menu" color="primary"></ion-menu-button>
<ion-menu-button menu="secondary-menu" color="secondary"></ion-menu-button>
</ion-buttons>
<ion-title>Menu - Multiple</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Content</ion-content>
</div>
</ion-app>
</body>
<scrip
</html>

View File

@@ -0,0 +1,79 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not vary across modes/directions
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('menu: multiple'), () => {
test.beforeEach(async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/18974',
});
await page.goto(`/src/components/menu/test/multiple`, config);
});
test('should present each menu on the same side individually', async ({ page }) => {
const primaryMenu = page.locator('ion-menu#primary-menu');
const secondaryMenu = page.locator('ion-menu#secondary-menu');
await primaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(primaryMenu).toBeVisible();
await primaryMenu.evaluate((el: HTMLIonMenuElement) => el.close());
await expect(primaryMenu).toBeHidden();
await secondaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(secondaryMenu).toBeVisible();
await secondaryMenu.evaluate((el: HTMLIonMenuElement) => el.close());
await expect(secondaryMenu).toBeHidden();
});
test('should close first menu when showing another menu on same side', async ({ page }) => {
const primaryMenu = page.locator('ion-menu#primary-menu');
const secondaryMenu = page.locator('ion-menu#secondary-menu');
await primaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(primaryMenu).toBeVisible();
await secondaryMenu.evaluate((el: HTMLIonMenuElement) => el.open());
await expect(primaryMenu).toBeHidden();
await expect(secondaryMenu).toBeVisible();
});
test('passing side to the menuController when multiple menus have that side should result in a warning', async ({
page,
}) => {
const logs: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'warning') {
logs.push(msg.text());
}
});
await page.evaluate(() => (window as any).menuController.open('start'));
expect(logs.length).toBe(1);
});
test('passing side to the menuController when multiple disabled menus have that side should result in a warning', async ({
page,
}) => {
const logs: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'warning') {
logs.push(msg.text());
}
});
await page.evaluate(() => (window as any).menuController.open('end'));
expect(logs.length).toBe(1);
});
});
});

View File

@@ -1,3 +1,5 @@
import { printIonWarning } from '@utils/logging';
import type { MenuI } from '../../components/menu/menu-interface';
import type { AnimationBuilder, BackButtonEvent } from '../../interface';
import { MENU_BACK_BUTTON_PRIORITY } from '../hardware-back-button';
@@ -12,7 +14,7 @@ const createMenuController = () => {
const menus: MenuI[] = [];
const open = async (menu?: string | null): Promise<boolean> => {
const menuEl = await get(menu);
const menuEl = await get(menu, true);
if (menuEl) {
return menuEl.open();
}
@@ -20,7 +22,7 @@ const createMenuController = () => {
};
const close = async (menu?: string | null): Promise<boolean> => {
const menuEl = await (menu !== undefined ? get(menu) : getOpen());
const menuEl = await (menu !== undefined ? get(menu, true) : getOpen());
if (menuEl !== undefined) {
return menuEl.close();
}
@@ -28,7 +30,7 @@ const createMenuController = () => {
};
const toggle = async (menu?: string | null): Promise<boolean> => {
const menuEl = await get(menu);
const menuEl = await get(menu, true);
if (menuEl) {
return menuEl.toggle();
}
@@ -70,20 +72,48 @@ const createMenuController = () => {
return false;
};
const get = async (menu?: string | null): Promise<HTMLIonMenuElement | undefined> => {
/**
* Finds and returns the menu specified by "menu" if registered.
* @param menu - The side or ID of the desired menu
* @param logOnMultipleSideMenus - If true, this function will log a warning
* if "menu" is a side but multiple menus on the same side were found. Since this function
* is used in multiple places, we default this log to false so that the calling
* functions can choose whether or not it is appropriate to log this warning.
*/
const get = async (
menu?: string | null,
logOnMultipleSideMenus: boolean = false
): Promise<HTMLIonMenuElement | undefined> => {
await waitUntilReady();
if (menu === 'start' || menu === 'end') {
// there could be more than one menu on the same side
// so first try to get the enabled one
const menuRef = find((m) => m.side === menu && !m.disabled);
if (menuRef) {
return menuRef;
const menuRefs = menus.filter((m) => m.side === menu && !m.disabled);
if (menuRefs.length >= 1) {
if (menuRefs.length > 1 && logOnMultipleSideMenus) {
printIonWarning(
`menuController queried for a menu on the "${menu}" side, but ${menuRefs.length} menus were found. The first menu reference will be used. If this is not the behavior you want then pass the ID of the menu instead of its side.`,
menuRefs.map((m) => m.el)
);
}
return menuRefs[0].el;
}
// didn't find a menu side that is enabled
// so try to get the first menu side found
return find((m) => m.side === menu);
const sideMenuRefs = menus.filter((m) => m.side === menu);
if (sideMenuRefs.length >= 1) {
if (sideMenuRefs.length > 1 && logOnMultipleSideMenus) {
printIonWarning(
`menuController queried for a menu on the "${menu}" side, but ${sideMenuRefs.length} menus were found. The first menu reference will be used. If this is not the behavior you want then pass the ID of the menu instead of its side.`,
sideMenuRefs.map((m) => m.el)
);
}
return sideMenuRefs[0].el;
}
} else if (menu != null) {
// the menuId was not left or right
// so try to get the menu by its "id"
@@ -131,9 +161,6 @@ const createMenuController = () => {
const _register = (menu: MenuI) => {
if (menus.indexOf(menu) < 0) {
if (!menu.disabled) {
_setActiveMenu(menu);
}
menus.push(menu);
}
};
@@ -145,14 +172,6 @@ const createMenuController = () => {
}
};
const _setActiveMenu = (menu: MenuI) => {
// if this menu should be enabled
// then find all the other menus on this same side
// and automatically disable other same side menus
const side = menu.side;
menus.filter((m) => m.side === side && m !== menu).forEach((m) => (m.disabled = true));
};
const _setOpen = async (menu: MenuI, shouldOpen: boolean, animated: boolean): Promise<boolean> => {
if (isAnimatingSync()) {
return false;
@@ -238,7 +257,6 @@ const createMenuController = () => {
_register,
_unregister,
_setOpen,
_setActiveMenu,
};
};