mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 19:57:22 +08:00
fix(vue): correctly remove old view when replacing route (#22566)
resolves #22492
This commit is contained in:
@ -238,7 +238,11 @@ export const IonRouterOutlet = defineComponent({
|
|||||||
leavingEl.classList.add('ion-page-hidden');
|
leavingEl.classList.add('ion-page-hidden');
|
||||||
leavingEl.setAttribute('aria-hidden', 'true');
|
leavingEl.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
if (!(routerAction === 'push' && routerDirection === 'forward')) {
|
if (routerAction === 'replace') {
|
||||||
|
leavingViewItem.mount = false;
|
||||||
|
leavingViewItem.ionPageElement = undefined;
|
||||||
|
leavingViewItem.ionRoute = false;
|
||||||
|
} else if (!(routerAction === 'push' && routerDirection === 'forward')) {
|
||||||
const shouldLeavingViewBeRemoved = routerDirection !== 'none' && leavingViewItem && (enteringViewItem !== leavingViewItem);
|
const shouldLeavingViewBeRemoved = routerDirection !== 'none' && leavingViewItem && (enteringViewItem !== leavingViewItem);
|
||||||
if (shouldLeavingViewBeRemoved) {
|
if (shouldLeavingViewBeRemoved) {
|
||||||
leavingViewItem.mount = false;
|
leavingViewItem.mount = false;
|
||||||
|
@ -114,7 +114,7 @@ describe('Routing', () => {
|
|||||||
cy.get('#replace').click();
|
cy.get('#replace').click();
|
||||||
|
|
||||||
cy.ionPageVisible('navigation');
|
cy.ionPageVisible('navigation');
|
||||||
cy.ionPageHidden('routing');
|
cy.ionPageDoesNotExist('routing');
|
||||||
|
|
||||||
cy.ionSwipeToGoBack(true);
|
cy.ionSwipeToGoBack(true);
|
||||||
cy.ionPageVisible('navigation');
|
cy.ionPageVisible('navigation');
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { mount, flushPromises } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||||
import { IonicVue, IonApp, IonRouterOutlet, IonPage } from '@ionic/vue';
|
import { IonicVue, IonApp, IonRouterOutlet, IonPage } from '@ionic/vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
import { waitForRouter } from './utils';
|
||||||
|
|
||||||
const App = {
|
const App = {
|
||||||
components: { IonApp, IonRouterOutlet },
|
components: { IonApp, IonRouterOutlet },
|
||||||
@ -48,6 +49,9 @@ const router = createRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Lifecycle Events', () => {
|
describe('Lifecycle Events', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
(HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn();
|
||||||
|
});
|
||||||
it('Triggers lifecycle events', async () => {
|
it('Triggers lifecycle events', async () => {
|
||||||
// Initial render
|
// Initial render
|
||||||
router.push('/');
|
router.push('/');
|
||||||
@ -78,10 +82,7 @@ describe('Lifecycle Events', () => {
|
|||||||
// Navigate to 2nd page
|
// Navigate to 2nd page
|
||||||
router.push('/2');
|
router.push('/2');
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
await flushPromises();
|
await waitForRouter();
|
||||||
|
|
||||||
(HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn();
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
|
||||||
|
|
||||||
// Page 1 lifecycle hooks
|
// Page 1 lifecycle hooks
|
||||||
expect(Page1.ionViewDidEnter).not.toHaveBeenCalled();
|
expect(Page1.ionViewDidEnter).not.toHaveBeenCalled();
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
import { mount, flushPromises } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||||
import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar } from '@ionic/vue';
|
import {
|
||||||
|
IonicVue,
|
||||||
|
IonApp,
|
||||||
|
IonRouterOutlet,
|
||||||
|
IonPage,
|
||||||
|
IonTabs,
|
||||||
|
IonTabBar,
|
||||||
|
IonTabButton,
|
||||||
|
IonLabel
|
||||||
|
} from '@ionic/vue';
|
||||||
import { onBeforeRouteLeave } from 'vue-router';
|
import { onBeforeRouteLeave } from 'vue-router';
|
||||||
|
import { waitForRouter } from './utils';
|
||||||
|
|
||||||
const App = {
|
const App = {
|
||||||
components: { IonApp, IonRouterOutlet },
|
components: { IonApp, IonRouterOutlet },
|
||||||
@ -14,6 +24,9 @@ const BasePage = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('Routing', () => {
|
describe('Routing', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
(HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn();
|
||||||
|
});
|
||||||
it('should pass no props', async () => {
|
it('should pass no props', async () => {
|
||||||
const Page1 = {
|
const Page1 = {
|
||||||
...BasePage,
|
...BasePage,
|
||||||
@ -153,8 +166,83 @@ describe('Routing', () => {
|
|||||||
|
|
||||||
// Navigate to 2nd page
|
// Navigate to 2nd page
|
||||||
router.push('/page2');
|
router.push('/page2');
|
||||||
await flushPromises();
|
await waitForRouter();
|
||||||
|
|
||||||
expect(leaveHook).toBeCalled();
|
expect(leaveHook).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Verifies fix for https://github.com/ionic-team/ionic-framework/issues/22492
|
||||||
|
it('should show correct view when replacing', async () => {
|
||||||
|
const Tabs = {
|
||||||
|
components: { IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel },
|
||||||
|
template: `
|
||||||
|
<ion-page>
|
||||||
|
<ion-tabs>
|
||||||
|
<ion-tab-bar slot="top">
|
||||||
|
<ion-tab-button tab="tab1" href="/tabs/tab1">
|
||||||
|
<ion-label>Tab 1</ion-label>
|
||||||
|
</ion-tab-button>
|
||||||
|
<ion-tab-button tab="tab2" href="/tabs/tab2">
|
||||||
|
<ion-label>Tab 2</ion-label>
|
||||||
|
</ion-tab-button>
|
||||||
|
</ion-tab-bar>
|
||||||
|
</ion-tabs>
|
||||||
|
</ion-page>
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
const Tab1 = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page>Tab 1</ion-page>`
|
||||||
|
}
|
||||||
|
const Tab2 = {
|
||||||
|
components: { IonPage },
|
||||||
|
template: `<ion-page>Tab 2</ion-page>`
|
||||||
|
}
|
||||||
|
const Parent = {
|
||||||
|
...BasePage,
|
||||||
|
template: `<ion-page>Parent Page</ion-page>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{ path: '/', redirect: '/tabs/tab1' },
|
||||||
|
{ path: '/parent', component: Parent },
|
||||||
|
{ path: '/tabs/', component: Tabs, children: [
|
||||||
|
{ path: '/', redirect: 'tab1' },
|
||||||
|
{ path: 'tab1', component: Tab1 },
|
||||||
|
{ path: 'tab2', component: Tab2 }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push('/');
|
||||||
|
await router.isReady();
|
||||||
|
const wrapper = mount(App, {
|
||||||
|
global: {
|
||||||
|
plugins: [router, IonicVue]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go to Tab 2
|
||||||
|
const tabButtons = wrapper.findAllComponents(IonTabButton);
|
||||||
|
tabButtons[1].trigger('click');
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Tab2).exists()).toBe(true);
|
||||||
|
expect(wrapper.findComponent(Parent).exists()).toBe(false);
|
||||||
|
|
||||||
|
router.replace('/parent')
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Parent).exists()).toBe(true);
|
||||||
|
expect(wrapper.findComponent(Tabs).exists()).toBe(false);
|
||||||
|
|
||||||
|
router.replace('/tabs/tab1');
|
||||||
|
await waitForRouter();
|
||||||
|
|
||||||
|
expect(wrapper.findComponent(Parent).exists()).toBe(false);
|
||||||
|
expect(wrapper.findComponent(Tab1).exists()).toBe(true);
|
||||||
|
expect(wrapper.findComponent(Tab2).exists()).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { mount, flushPromises } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
import { createRouter, createWebHistory } from '@ionic/vue-router';
|
||||||
import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel } from '@ionic/vue';
|
import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar, IonTabButton, IonLabel } from '@ionic/vue';
|
||||||
|
import { waitForRouter } from './utils';
|
||||||
|
|
||||||
const App = {
|
const App = {
|
||||||
components: { IonApp, IonRouterOutlet },
|
components: { IonApp, IonRouterOutlet },
|
||||||
@ -34,8 +35,9 @@ const Tab2 = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('ion-tabs', () => {
|
describe('ion-tabs', () => {
|
||||||
(HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn();
|
beforeAll(() => {
|
||||||
|
(HTMLElement.prototype as HTMLIonRouterOutletElement).commit = jest.fn();
|
||||||
|
});
|
||||||
it('should emit will change and did change events when changing tab', async () => {
|
it('should emit will change and did change events when changing tab', async () => {
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(process.env.BASE_URL),
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
@ -76,7 +78,7 @@ describe('ion-tabs', () => {
|
|||||||
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
||||||
|
|
||||||
router.push('/tab2')
|
router.push('/tab2')
|
||||||
await flushPromises()
|
await waitForRouter()
|
||||||
|
|
||||||
expect(tabs.emitted().ionTabsWillChange.length).toEqual(2);
|
expect(tabs.emitted().ionTabsWillChange.length).toEqual(2);
|
||||||
expect(tabs.emitted().ionTabsWillChange[1]).toEqual([{ tab: 'tab2' }]);
|
expect(tabs.emitted().ionTabsWillChange[1]).toEqual([{ tab: 'tab2' }]);
|
||||||
@ -124,7 +126,7 @@ describe('ion-tabs', () => {
|
|||||||
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
||||||
|
|
||||||
router.push('/tab1')
|
router.push('/tab1')
|
||||||
await flushPromises()
|
await waitForRouter()
|
||||||
|
|
||||||
expect(tabs.emitted().ionTabsWillChange.length).toEqual(1);
|
expect(tabs.emitted().ionTabsWillChange.length).toEqual(1);
|
||||||
expect(tabs.emitted().ionTabsDidChange.length).toEqual(1);
|
expect(tabs.emitted().ionTabsDidChange.length).toEqual(1);
|
||||||
@ -178,9 +180,7 @@ describe('ion-tabs', () => {
|
|||||||
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
||||||
|
|
||||||
router.push('/sibling');
|
router.push('/sibling');
|
||||||
await flushPromises();
|
await waitForRouter()
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
|
||||||
|
|
||||||
expect(tabs.emitted().ionTabsWillChange.length).toEqual(1);
|
expect(tabs.emitted().ionTabsWillChange.length).toEqual(1);
|
||||||
expect(tabs.emitted().ionTabsDidChange.length).toEqual(1);
|
expect(tabs.emitted().ionTabsDidChange.length).toEqual(1);
|
||||||
@ -236,9 +236,7 @@ describe('ion-tabs', () => {
|
|||||||
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
expect(tabs.emitted().ionTabsDidChange[0]).toEqual([{ tab: 'tab1' }]);
|
||||||
|
|
||||||
router.push('/tab1/child');
|
router.push('/tab1/child');
|
||||||
await flushPromises();
|
await waitForRouter()
|
||||||
|
|
||||||
await new Promise((r) => setTimeout(r, 100));
|
|
||||||
|
|
||||||
expect(tabs.emitted().ionTabsWillChange.length).toEqual(1);
|
expect(tabs.emitted().ionTabsWillChange.length).toEqual(1);
|
||||||
expect(tabs.emitted().ionTabsDidChange.length).toEqual(1);
|
expect(tabs.emitted().ionTabsDidChange.length).toEqual(1);
|
||||||
|
6
packages/vue/test-app/tests/unit/utils.ts
Normal file
6
packages/vue/test-app/tests/unit/utils.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { flushPromises } from '@vue/test-utils';
|
||||||
|
|
||||||
|
export const waitForRouter = async () => {
|
||||||
|
await flushPromises();
|
||||||
|
await new Promise((r) => setTimeout(r, 100));
|
||||||
|
}
|
Reference in New Issue
Block a user