Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/vue-router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,32 @@ export const createIonRouter = (
* is not good because we would have two /tabs/tab1/child1 entries
* separated by a /tabs/tab1/child2 entry.
*/
router.go(prevInfo.position - routeInfo.position);
const positionDelta = prevInfo.position! - routeInfo.position!;
if (positionDelta < 0) {
router.go(positionDelta);
} else if (prevInfo.pathname) {
/**
* prevInfo's history position was wiped when the user went
* back then pushed a new route, so router.go can't
* reach it. Replace falls through to afterEach with the
* pop/back `incomingRouteParams` set above, which preserves
* the back animation and consumes the params so they don't
* leak into the next navigation. We replace even when
* `positionDelta === 0` for the same consumption reason.
*/
router.replace({
path: prevInfo.pathname,
query: parseQuery(prevInfo.search),
});
} else {
/**
* prevInfo has no pathname (synthesized root entry). Route
* to `defaultHref` so the pop/back `incomingRouteParams`
* set above gets consumed instead of leaking into the next
* navigation.
*/
handleNavigate(defaultHref, "pop", "back", routerAnimation);
}
}
} else {
handleNavigate(defaultHref, "pop", "back", routerAnimation);
Expand Down
5 changes: 5 additions & 0 deletions packages/vue/test/base/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ const routes: Array<RouteRecordRaw> = [
path: 'tab2',
component: () => import('@/views/tabs/Tab2.vue')
},
{
path: 'tab2/:id',
component: () => import('@/views/tabs/Tab2Parameter.vue'),
props: true
},
{
path: 'tab3',
beforeEnter: (to, from, next) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/test/base/src/views/tabs/Tab2.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
<ion-item router-link="/routing" id="routing">
<ion-label>Go to /routing</ion-label>
</ion-item>

<ion-item router-link="/tabs/tab2/childone" id="child-one">
<ion-label>Go to Tab 2 Child 1</ion-label>
</ion-item>
</ion-content>
</ion-page>
</template>
Expand Down
57 changes: 57 additions & 0 deletions packages/vue/test/base/src/views/tabs/Tab2Parameter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<ion-page :data-pageid="'tab2' + $props.id">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>Tab 2 Child {{ $props.id }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tab 2 Child {{ $props.id }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-item router-link="childone" id="child-one">
<ion-label>Tab 2 Child 1</ion-label>
</ion-item>

<ion-item router-link="childtwo" id="child-two">
<ion-label>Tab 2 Child 2</ion-label>
</ion-item>

</ion-content>
</ion-page>
</template>

<script>
import {
IonButtons,
IonBackButton,
IonPage,
IonHeader,
IonItem,
IonLabel,
IonToolbar,
IonTitle,
IonContent
} from '@ionic/vue';

export default {
props: { id: String },
components: {
IonButtons,
IonBackButton,
IonPage,
IonHeader,
IonItem,
IonLabel,
IonToolbar,
IonTitle,
IonContent
}
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { test, expect } from './utils/test-base';
import { ionBackClick, ionPageVisible, ionPageHidden, ionPageDoesNotExist, tabClick } from './utils/test-utils';

/**
* When the user has child pages open in
* two tabs and switches between them, the back button on the active tab's
* child must pop within that tab's stack, not jump to the other tab.
*/
test.describe('Tabs: back button after switching between tabs with child pages', () => {
test('back button on tab2 child still returns to tab2 root after multiple tab switches', async ({ page }) => {
await page.goto('/tabs');
await ionPageVisible(page, 'tab1');

await page.locator('.ion-page[data-pageid="tab1"] #child-one').click();
await ionPageVisible(page, 'tab1childone');

await tabClick(page, 'tab2');
await ionPageVisible(page, 'tab2');
await ionPageHidden(page, 'tab1childone');

await page.locator('.ion-page[data-pageid="tab2"] #child-one').click();
await ionPageVisible(page, 'tab2childone');

await tabClick(page, 'tab1');
await ionPageVisible(page, 'tab1childone');
await ionPageHidden(page, 'tab2childone');

await tabClick(page, 'tab2');
await ionPageVisible(page, 'tab2childone');
await ionPageHidden(page, 'tab1childone');

await ionBackClick(page, 'tab2childone');

await expect.poll(() => new URL(page.url()).pathname).toBe('/tabs/tab2');
await ionPageVisible(page, 'tab2');
await ionPageDoesNotExist(page, 'tab2childone');
});

// Go back on tab1's child first, then re-enter tab2. Back on
// tab2's child must still land on /tabs/tab2 after that sequence.
test('back button on tab2 child works after going back in tab1 then re-entering tab2 (FW-6472 scenario B)', async ({ page }) => {
await page.goto('/tabs');
await ionPageVisible(page, 'tab1');

await page.locator('.ion-page[data-pageid="tab1"] #child-one').click();
await ionPageVisible(page, 'tab1childone');

await tabClick(page, 'tab2');
await ionPageVisible(page, 'tab2');

await page.locator('.ion-page[data-pageid="tab2"] #child-one').click();
await ionPageVisible(page, 'tab2childone');

await tabClick(page, 'tab1');
await ionPageVisible(page, 'tab1childone');

await ionBackClick(page, 'tab1childone');
await expect.poll(() => new URL(page.url()).pathname).toBe('/tabs/tab1');
await ionPageVisible(page, 'tab1');

await tabClick(page, 'tab2');
await ionPageVisible(page, 'tab2childone');

await ionBackClick(page, 'tab2childone');

await expect.poll(() => new URL(page.url()).pathname).toBe('/tabs/tab2');
await ionPageVisible(page, 'tab2');
await ionPageDoesNotExist(page, 'tab2childone');
});
});