Skip to content

Commit

Permalink
fix(vue): tab bar now works with slot="top" (#22461)
Browse files Browse the repository at this point in the history
resolves #22456
  • Loading branch information
liamdebeasi committed Nov 10, 2020
1 parent f5b0299 commit e17c822
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 21 deletions.
5 changes: 3 additions & 2 deletions packages/vue/src/components/IonTabBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export const IonTabBar = defineComponent({
* to a tab from another tab, we can correctly
* show any child pages if necessary.
*/
(currentInstance.subTree.children as VNode[]).forEach((child: VNode) => {
const children = (currentInstance.subTree.children || []) as VNode[];
children.forEach((child: VNode) => {
if (child.type && (child.type as any).name === 'IonTabButton') {
tabState.tabs[child.props.tab] = {
originalHref: child.props.href,
Expand All @@ -45,7 +46,7 @@ export const IonTabBar = defineComponent({
});

const checkActiveTab = (currentRoute: any) => {
const childNodes = currentInstance.subTree.children as VNode[];
const childNodes = (currentInstance.subTree.children || []) as VNode[];
const { tabs, activeTab: prevActiveTab } = tabState;
const tabKeys = Object.keys(tabs);
const activeTab = tabKeys
Expand Down
60 changes: 42 additions & 18 deletions packages/vue/src/components/IonTabs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,50 @@
import { h, defineComponent } from 'vue';
import { h, defineComponent, VNode } from 'vue';
import { IonRouterOutlet } from './IonRouterOutlet';

export const IonTabs = defineComponent({
name: 'IonTabs',
render() {
const { $slots: slots } = this;
const slottedContent = slots.default && slots.default();
let childrenToRender = [
h('div', {
class: 'tabs-inner',
style: {
'position': 'relative',
'flex': '1',
'contain': 'layout size style'
}
}, [
h(IonRouterOutlet, { tabs: true })
])
];

/**
* If ion-tab-bar has slot="top" it needs to be
* rendered before `.tabs-inner` otherwise it will
* not show above the tab content.
*/
if (slottedContent && slottedContent.length > 0) {
const topSlottedTabBar = slottedContent.find((child: VNode) => {
const isTabBar = child.type && (child.type as any).name === 'IonTabBar';
const hasTopSlot = child.props?.slot === 'top';

return isTabBar && hasTopSlot;
});

if (topSlottedTabBar) {
childrenToRender = [
...slottedContent,
...childrenToRender
];
} else {
childrenToRender = [
...childrenToRender,
...slottedContent
]
}
}

return h(
'ion-tabs',
{
Expand All @@ -22,23 +62,7 @@ export const IonTabs = defineComponent({
'z-index': '0'
}
},
[
h(
'div',
{
class: 'tabs-inner',
style: {
'position': 'relative',
'flex': '1',
'contain': 'layout size style'
}
},
[
h(IonRouterOutlet, { tabs: true })
]
),
...slots.default && slots.default()
]
childrenToRender
)
}
});
2 changes: 1 addition & 1 deletion packages/vue/test-app/src/views/TabsSecondary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<ion-page data-pageid="tabs-secondary">
<ion-content>
<ion-tabs id="tabs">
<ion-tab-bar slot="bottom">
<ion-tab-bar slot="top">
<ion-tab-button tab="tab1-secondary" href="/tabs-secondary/tab1">
<ion-icon :icon="triangle" />
<ion-label>Tab 1</ion-label>
Expand Down
105 changes: 105 additions & 0 deletions packages/vue/test-app/tests/unit/tab-bar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { mount } from '@vue/test-utils';
import { createRouter, createWebHistory } from '@ionic/vue-router';
import { IonicVue, IonApp, IonRouterOutlet, IonPage, IonTabs, IonTabBar } from '@ionic/vue';

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

describe('ion-tab-bar', () => {
it('should render in the top slot', async () => {
const Tabs = {
components: { IonPage, IonTabs, IonTabBar },
template: `
<ion-page>
<ion-tabs>
<ion-tab-bar slot="top"></ion-tab-bar>
</ion-tabs>
</ion-page>
`,
}

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Tabs }
]
});

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

const innerHTML = wrapper.find('ion-tabs').html();
expect(innerHTML).toContain(`<ion-tab-bar slot="top"></ion-tab-bar><div class="tabs-inner" style="position: relative; flex: 1; contain: layout size style;">`);

});

it('should render in the bottom slot', async () => {
const Tabs = {
components: { IonPage, IonTabs, IonTabBar },
template: `
<ion-page>
<ion-tabs>
<ion-tab-bar slot="bottom"></ion-tab-bar>
</ion-tabs>
</ion-page>
`,
}

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Tabs }
]
});

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

const innerHTML = wrapper.find('ion-tabs').html();
expect(innerHTML).toContain(`<div class="tabs-inner" style="position: relative; flex: 1; contain: layout size style;"><ion-router-outlet tabs="true"></ion-router-outlet></div><ion-tab-bar slot="bottom"></ion-tab-bar>`);

});

it('should render in the default slot', async () => {
const Tabs = {
components: { IonPage, IonTabs, IonTabBar },
template: `
<ion-page>
<ion-tabs>
<ion-tab-bar></ion-tab-bar>
</ion-tabs>
</ion-page>
`,
}

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
{ path: '/', component: Tabs }
]
});

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

const innerHTML = wrapper.find('ion-tabs').html();
expect(innerHTML).toContain(`<div class="tabs-inner" style="position: relative; flex: 1; contain: layout size style;"><ion-router-outlet tabs="true"></ion-router-outlet></div><ion-tab-bar></ion-tab-bar></ion-tabs>`)
});
});

0 comments on commit e17c822

Please sign in to comment.