fix(menu): update location when dynamically changing side or doc dir (#27079)
## Pull request checklist Please check if your PR fulfills the following requirements: - [x] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features) - Some docs updates need to be made in the `ionic-docs` repo, in a separate PR. See the [contributing guide](https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation) for details. - [x] Build (`npm run build`) was run locally and any changes were pushed - [x] Lint (`npm run lint`) has passed locally and any fixes were made for failures ## Pull request type Please check the type of change your PR introduces: - [x] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no api changes) - [ ] Build related changes - [ ] Documentation content changes - [ ] Other (please describe): ## What is the current behavior? - When the document direction is dynamically changed, the menu does not switch to the correct position. For example, `<ion-menu side='start'>` will always be on the left side regardless of direction. - When the side property is dynamically changed, the animation starts in the middle of the screen instead of off screen. Issue URL: resolves #25601 #19489 ## What is the new behavior? - Animation is smooth regardless of side property or document direction being dynamically change. - Menu appears on the correct side based on document direction when dynamically updated. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information N/A --------- Co-authored-by: ionitron <hi@ionicframework.com>
@ -37,7 +37,6 @@
|
|||||||
|
|
||||||
|
|
||||||
.menu-inner {
|
.menu-inner {
|
||||||
@include position(0, auto, 0, 0);
|
|
||||||
@include transform(translateX(-9999px));
|
@include transform(translateX(-9999px));
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -62,21 +61,13 @@
|
|||||||
:host(.menu-side-start) .menu-inner {
|
:host(.menu-side-start) .menu-inner {
|
||||||
--ion-safe-area-right: 0px;
|
--ion-safe-area-right: 0px;
|
||||||
|
|
||||||
@include multi-dir() {
|
@include position(0, auto, 0, 0);
|
||||||
/* stylelint-disable property-disallowed-list */
|
|
||||||
right: auto;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(.menu-side-end) .menu-inner {
|
:host(.menu-side-end) .menu-inner {
|
||||||
--ion-safe-area-left: 0px;
|
--ion-safe-area-left: 0px;
|
||||||
|
|
||||||
@include multi-dir() {
|
@include position(0, 0, 0, auto);
|
||||||
right: 0;
|
|
||||||
left: auto;
|
|
||||||
/* stylelint-enable property-disallowed-list */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-backdrop {
|
ion-backdrop {
|
||||||
|
|||||||
@ -130,6 +130,11 @@ export class Menu implements ComponentInterface, MenuI {
|
|||||||
@Watch('side')
|
@Watch('side')
|
||||||
protected sideChanged() {
|
protected sideChanged() {
|
||||||
this.isEndSide = isEnd(this.side);
|
this.isEndSide = isEnd(this.side);
|
||||||
|
/**
|
||||||
|
* Menu direction animation is calculated based on the document direction.
|
||||||
|
* If the document direction changes, we need to create a new animation.
|
||||||
|
*/
|
||||||
|
this.animation = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -413,10 +418,16 @@ export class Menu implements ComponentInterface, MenuI {
|
|||||||
// Menu swipe animation takes the menu's inner width as parameter,
|
// Menu swipe animation takes the menu's inner width as parameter,
|
||||||
// If `offsetWidth` changes, we need to create a new animation.
|
// If `offsetWidth` changes, we need to create a new animation.
|
||||||
const width = this.menuInnerEl!.offsetWidth;
|
const width = this.menuInnerEl!.offsetWidth;
|
||||||
if (width === this.width && this.animation !== undefined) {
|
/**
|
||||||
|
* Menu direction animation is calculated based on the document direction.
|
||||||
|
* If the document direction changes, we need to create a new animation.
|
||||||
|
*/
|
||||||
|
const isEndSide = isEnd(this.side);
|
||||||
|
if (width === this.width && this.animation !== undefined && isEndSide === this.isEndSide) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
this.isEndSide = isEndSide;
|
||||||
|
|
||||||
// Destroy existing animation
|
// Destroy existing animation
|
||||||
if (this.animation) {
|
if (this.animation) {
|
||||||
@ -703,7 +714,7 @@ export class Menu implements ComponentInterface, MenuI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isEndSide, type, disabled, isPaneVisible, inheritedAttributes } = this;
|
const { type, disabled, isPaneVisible, inheritedAttributes, side } = this;
|
||||||
const mode = getIonMode(this);
|
const mode = getIonMode(this);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -714,8 +725,7 @@ export class Menu implements ComponentInterface, MenuI {
|
|||||||
[mode]: true,
|
[mode]: true,
|
||||||
[`menu-type-${type}`]: true,
|
[`menu-type-${type}`]: true,
|
||||||
'menu-enabled': !disabled,
|
'menu-enabled': !disabled,
|
||||||
'menu-side-end': isEndSide,
|
[`menu-side-${side}`]: true,
|
||||||
'menu-side-start': !isEndSide,
|
|
||||||
'menu-pane-visible': isPaneVisible,
|
'menu-pane-visible': isPaneVisible,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -74,6 +74,57 @@ test.describe('menu: basic', () => {
|
|||||||
|
|
||||||
await expect(scrollTop).toBe(200);
|
await expect(scrollTop).toBe(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should render on the correct side when side is changed dynamically', async ({ page, skip }) => {
|
||||||
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/ionic-team/ionic-framework/issues/25601',
|
||||||
|
});
|
||||||
|
|
||||||
|
skip.mode('ios', 'Dynamic side changes are not mode dependent');
|
||||||
|
|
||||||
|
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
|
||||||
|
const ionDidClose = await page.spyOnEvent('ionDidClose');
|
||||||
|
|
||||||
|
await page.locator('[menu-id="start-menu"]').evaluate(async (el: HTMLIonMenuElement) => {
|
||||||
|
el.side = 'end';
|
||||||
|
});
|
||||||
|
await page.click('#open-start');
|
||||||
|
await ionDidOpen.next();
|
||||||
|
|
||||||
|
await expect(page).toHaveScreenshot(`menu-basic-side-toggled-${page.getSnapshotSettings()}.png`);
|
||||||
|
|
||||||
|
await page.locator('[menu-id="start-menu"]').evaluate(async (el: HTMLIonMenuElement) => {
|
||||||
|
await el.close();
|
||||||
|
});
|
||||||
|
await ionDidClose.next();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render on the correct side when document direction is changed dynamically', async ({ page, skip }) => {
|
||||||
|
test.info().annotations.push({
|
||||||
|
type: 'issue',
|
||||||
|
description: 'https://github.com/ionic-team/ionic-framework/issues/25601',
|
||||||
|
});
|
||||||
|
|
||||||
|
skip.rtl('Document direction is not dependent on initial load');
|
||||||
|
skip.mode('ios', 'Dynamic side changes are not mode dependent');
|
||||||
|
|
||||||
|
const ionDidOpen = await page.spyOnEvent('ionDidOpen');
|
||||||
|
const ionDidClose = await page.spyOnEvent('ionDidClose');
|
||||||
|
|
||||||
|
await page.evaluate(() => {
|
||||||
|
document.dir = 'rtl';
|
||||||
|
});
|
||||||
|
await page.click('#open-start');
|
||||||
|
await ionDidOpen.next();
|
||||||
|
|
||||||
|
await expect(page).toHaveScreenshot(`menu-basic-doc-dir-toggled-${page.getSnapshotSettings()}.png`);
|
||||||
|
|
||||||
|
await page.locator('[menu-id="start-menu"]').evaluate(async (el: HTMLIonMenuElement) => {
|
||||||
|
await el.close();
|
||||||
|
});
|
||||||
|
await ionDidClose.next();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function testMenu(page: E2EPage, menu: Locator, menuId: string) {
|
async function testMenu(page: E2EPage, menu: Locator, menuId: string) {
|
||||||
|
|||||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 20 KiB |