Skip to content

fix(vue): tabs and parameterized routes work with latest vue #28846

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 19, 2024
10 changes: 9 additions & 1 deletion packages/vue/src/components/IonRouterOutlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,15 @@ export const IonRouterOutlet = /*@__PURE__*/ defineComponent({

previousMatchedRouteRef = currentMatchedRouteRef;
previousMatchedPath = currentRoute.path;
}
},
/**
* Future versions of Vue may default watching nested
* reactive objects to "deep: false".
* We explicitly set this watcher to "deep: true" to
* account for that.
* https://github.com/vuejs/core/issues/9965#issuecomment-1875067499
*/
{ deep: true }
);

const canStart = () => {
Expand Down
35 changes: 27 additions & 8 deletions packages/vue/src/components/IonTabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ const DID_CHANGE = "ionTabsDidChange";

// TODO(FW-2969): types

/**
* Vue 3.2.38 fixed an issue where Web Component
* names are respected using kebab case instead of pascal case.
* As a result, we need to account for both here since we support
* versions of Vue < 3.2.38.
*/
// TODO FW-5904
const isRouterOutlet = (node: VNode) => {
return (
node.type &&
((node.type as any).name === "IonRouterOutlet" ||
node.type === "ion-router-outlet")
);
};

const isTabBar = (node: VNode) => {
return (
node.type &&
((node.type as any).name === "IonTabBar" || node.type === "ion-tab-bar")
);
};

export const IonTabs = /*@__PURE__*/ defineComponent({
name: "IonTabs",
emits: [WILL_CHANGE, DID_CHANGE],
Expand All @@ -19,9 +41,8 @@ export const IonTabs = /*@__PURE__*/ defineComponent({
* inside of ion-tabs.
*/
if (slottedContent && slottedContent.length > 0) {
routerOutlet = slottedContent.find(
(child: VNode) =>
child.type && (child.type as any).name === "IonRouterOutlet"
routerOutlet = slottedContent.find((child: VNode) =>
isRouterOutlet(child)
);
}

Expand Down Expand Up @@ -57,13 +78,11 @@ export const IonTabs = /*@__PURE__*/ defineComponent({
* since that needs to be inside of `.tabs-inner`.
*/
const filteredContent = slottedContent.filter(
(child: VNode) =>
!child.type ||
(child.type && (child.type as any).name !== "IonRouterOutlet")
(child: VNode) => !child.type || !isRouterOutlet(child)
);

const slottedTabBar = filteredContent.find(
(child: VNode) => child.type && (child.type as any).name === "IonTabBar"
const slottedTabBar = filteredContent.find((child: VNode) =>
isTabBar(child)
);
const hasTopSlotTabBar =
slottedTabBar && slottedTabBar.props?.slot === "top";
Expand Down
6 changes: 3 additions & 3 deletions packages/vue/test/apps/vue3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"@ionic/vue": "^6.0.12",
"@ionic/vue-router": "^6.0.12",
"ionicons": "^6.0.4",
"vue": "^3.2.31",
"vue-router": "^4.0.14"
"vue": "^3.4.14",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.4.0",
Expand All @@ -35,7 +35,7 @@
"jsdom": "^20.0.0",
"typescript": "~4.5.5",
"vite": "^3.1.4",
"vitest": "^0.23.4",
"vitest": "^1.2.1",
"wait-on": "^5.3.0"
},
"engines": {
Expand Down
17 changes: 6 additions & 11 deletions packages/vue/test/base/tests/unit/hooks.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonPage, useIonRouter, createAnimation } from '@ionic/vue';
import { IonicVue, IonRouterOutlet, IonPage, useIonRouter } from '@ionic/vue';
import { mockAnimation, waitForRouter } from './utils';

const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}

const BasePage = {
template: '<ion-page></ion-page>',
components: { IonPage },
Expand Down Expand Up @@ -40,7 +35,7 @@ describe('useIonRouter', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down Expand Up @@ -85,7 +80,7 @@ describe('useIonRouter', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down Expand Up @@ -137,7 +132,7 @@ describe('useIonRouter', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down Expand Up @@ -181,7 +176,7 @@ describe('useIonRouter', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down Expand Up @@ -229,7 +224,7 @@ describe('useIonRouter', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down
13 changes: 4 additions & 9 deletions packages/vue/test/base/tests/unit/lifecycle.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { mount } from '@vue/test-utils';
import { describe, expect, it, vi } from 'vitest';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonTabs, IonPage } from '@ionic/vue';
import { IonicVue, IonRouterOutlet, IonTabs, IonPage } from '@ionic/vue';
import { defineComponent } from 'vue';
import { waitForRouter } from './utils';

const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}

const BasePage = {
template: '<ion-page :data-pageid="name"></ion-page>',
components: { IonPage },
Expand Down Expand Up @@ -54,7 +49,7 @@ describe('Lifecycle Events', () => {
// Initial render
router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down Expand Up @@ -108,7 +103,7 @@ describe('Lifecycle Events', () => {
template: `
<ion-page>
<ion-tabs>
<ion-router-outlet></ion-router-outlet>
<IonRouterOutlet />
</ion-tabs>
</ion-page>
`,
Expand Down Expand Up @@ -148,7 +143,7 @@ describe('Lifecycle Events', () => {
// Initial render
router.push('/tab1');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down
11 changes: 3 additions & 8 deletions packages/vue/test/base/tests/unit/page.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonPage } from '@ionic/vue';

const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}
import { IonicVue, IonRouterOutlet, IonPage } from '@ionic/vue';

describe('IonPage', () => {
it('should add ion-page class', async () => {
Expand All @@ -25,7 +20,7 @@ describe('IonPage', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand All @@ -50,7 +45,7 @@ describe('IonPage', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down
12 changes: 2 additions & 10 deletions packages/vue/test/base/tests/unit/router-outlet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import {
IonicVue,
IonApp,
IonRouterOutlet,
IonPage,
useIonRouter,
createAnimation
} from '@ionic/vue';
import { onBeforeRouteLeave } from 'vue-router';
import { mockAnimation, waitForRouter } from './utils';

enableAutoUnmount(afterEach);

const App = {
components: { IonApp, IonRouterOutlet },
template: '<ion-app><ion-router-outlet /></ion-app>',
}

const BasePage = {
template: '<ion-page :data-pageid="name"></ion-page>',
components: { IonPage },
Expand Down Expand Up @@ -60,7 +52,7 @@ describe('Routing', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down Expand Up @@ -122,7 +114,7 @@ describe('Routing', () => {

router.push('/');
await router.isReady();
const wrapper = mount(App, {
const wrapper = mount(IonRouterOutlet, {
global: {
plugins: [router, IonicVue]
}
Expand Down
Loading