Skip to content
This repository has been archived by the owner on Mar 10, 2023. It is now read-only.

Commit

Permalink
Fix buefy#1387 maintain tab items order (buefy#1535)
Browse files Browse the repository at this point in the history
  • Loading branch information
service-paradis authored and LeoMouyna committed Jan 6, 2020
1 parent 5cc34be commit 36d9dc2
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 32 deletions.
10 changes: 10 additions & 0 deletions docs/pages/components/tabs/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
<div>
<Example :component="ExSimple" :code="ExSimpleCode"/>

<Example :component="ExDynamic" :code="ExDynamicCode" title="Dynamic">
<p>Items can be created / modified / deleted and will always keep the defined order.</p>
<p>You can also use <code>v-if</code> to show (or not) a Tab Item.</p>
</Example>

<Example :component="ExPosition" :code="ExPositionCode" title="Position"/>

<Example :component="ExIcons" :code="ExIconsCode" title="Icons"/>
Expand Down Expand Up @@ -35,6 +40,9 @@
import ExSimple from './examples/ExSimple'
import ExSimpleCode from '!!raw-loader!./examples/ExSimple'
import ExDynamic from './examples/ExDynamic'
import ExDynamicCode from '!!raw-loader!./examples/ExDynamic'
import ExPosition from './examples/ExPosition'
import ExPositionCode from '!!raw-loader!./examples/ExPosition'
Expand All @@ -61,12 +69,14 @@
return {
api,
ExSimple,
ExDynamic,
ExPosition,
ExIcons,
ExSizes,
ExTypes,
ExExpanded,
ExSimpleCode,
ExDynamicCode,
ExPositionCode,
ExIconsCode,
ExSizesCode,
Expand Down
67 changes: 67 additions & 0 deletions docs/pages/components/tabs/examples/ExDynamic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<section>
<div class="block">
<b-switch v-model="showMusic"> Show Music item (using <code>v-if</code>) </b-switch>
</div>
<div class="block">
<b-switch v-model="showBooks"> Show books item (by adding / removing from the array) </b-switch>
</div>
<b-tabs v-model="activeTab">
<template v-for="(tab, index) in tabs">
<b-tab-item
v-if="tab.displayed"
:key="index"
:label="tab.label">
{{ tab.content }}
</b-tab-item>
</template>
</b-tabs>
</section>
</template>

<script>
export default {
data() {
return {
activeTab: 0,
showMusic: true,
showBooks: false
}
},
computed: {
baseTabs() {
return [
{
label: 'Pictures',
content: 'Lorem ipsum dolor sit amet.',
displayed: true,
},
{
label: 'Music',
content: 'Lorem ipsum dolor sit amet.',
displayed: this.showMusic,
},
{
label: 'Videos',
content: 'Lorem ipsum dolor sit amet.',
displayed: true,
}
]
},
tabs() {
const tabs = [...this.baseTabs]
if (this.showBooks) {
tabs.splice(tabs.length - 1, 0, this.bookTab);
}
return tabs
},
bookTab() {
return {
label: 'Books',
content: 'Lorem ipsum dolor sit amet.',
displayed: true,
}
}
}
}
</script>
17 changes: 15 additions & 2 deletions src/components/tabs/TabItem.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const BTabs = {
tabItems: [],
_isTabs: true
}
},
methods: {
refreshSlots: jest.fn()
}
}

Expand All @@ -29,12 +32,22 @@ describe('BTabItem', () => {
})

it('set isActive when activate is called', () => {
wrapper.vm.activate()
wrapper.vm.activate(0, 1)
expect(wrapper.vm.transitionName).toBe('slide-prev')
expect(wrapper.vm.isActive).toBeTruthy()

wrapper.vm.activate(1, 0)
expect(wrapper.vm.transitionName).toBe('slide-next')
expect(wrapper.vm.isActive).toBeTruthy()
})

it('reset isActive when deactivate is called', () => {
wrapper.vm.deactivate()
wrapper.vm.deactivate(0, 1)
expect(wrapper.vm.transitionName).toBe('slide-prev')
expect(wrapper.vm.isActive).toBeFalsy()

wrapper.vm.deactivate(1, 0)
expect(wrapper.vm.transitionName).toBe('slide-next')
expect(wrapper.vm.isActive).toBeFalsy()
})
})
10 changes: 4 additions & 6 deletions src/components/tabs/TabItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export default {
data() {
return {
isActive: false,
transitionName: null
transitionName: null,
_isTabItem: true // Used internally by Tab
}
},
methods: {
Expand Down Expand Up @@ -43,13 +44,10 @@ export default {
this.$destroy()
throw new Error('You should wrap bTabItem on a bTabs')
}
this.$parent.tabItems.push(this)
this.$parent.refreshSlots()
},
beforeDestroy() {
const index = this.$parent.tabItems.indexOf(this)
if (index >= 0) {
this.$parent.tabItems.splice(index, 1)
}
this.$parent.refreshSlots()
},
render(createElement) {
// if destroy apply v-if
Expand Down
65 changes: 44 additions & 21 deletions src/components/tabs/Tabs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,35 @@ import BTabs from '@components/tabs/Tabs'

let wrapper

const mockTabItems = (active = false) => {
return {
name: 'BTabItem',
template: '<b-tab-item></b-tab-item>',
data() {
return {
_isTabItem: true,
isActive: active,
visible: active,
clickable: active
}
},
methods: {
activate: jest.fn(),
deactivate: jest.fn()
}
}
}

describe('BTabs', () => {
beforeEach(() => {
wrapper = shallowMount(BTabs)
wrapper.setData({
tabItems: [
{
isActive: true,
activate: jest.fn(),
deactivate: jest.fn(),
visible: true,
$slots: {}
},
{
isActive: false,
clickable: false,
activate: jest.fn(),
deactivate: jest.fn(),
visible: true,
$slots: {}
}
]
wrapper = shallowMount(BTabs, {
stub: ['b-tab-item'],
slots: {
default: [
mockTabItems(true),
mockTabItems()
]
}
})
})

Expand All @@ -36,6 +44,17 @@ describe('BTabs', () => {
expect(wrapper.html()).toMatchSnapshot()
})

it('manage main classes accordingly', () => {
wrapper.setProps({
expanded: true,
vertical: true,
position: 'is-centered'
})
expect(wrapper.vm.mainClasses['is-fullwidth']).toBeTruthy()
expect(wrapper.vm.mainClasses['is-vertical']).toBeTruthy()
expect(wrapper.vm.mainClasses['is-centered']).toBeTruthy()
})

it('calls changeTab when value is changed', () => {
wrapper.vm.changeTab = jest.fn()
wrapper.setProps({value: 1})
Expand All @@ -52,10 +71,14 @@ describe('BTabs', () => {

it('emit input event with value when tabClick is called', () => {
const val = 1
wrapper.vm.changeTab = jest.fn()
wrapper.vm.changeTab = jest.fn((newIndex) => {
wrapper.vm.activeTab = newIndex
})
wrapper.vm.tabClick(val)
wrapper.vm.tabClick(val)
const valueEmitted = wrapper.emitted()['input'][0]
expect(valueEmitted).toContainEqual(val)
expect(wrapper.vm.changeTab).toHaveBeenCalled()
// Will be called only once even if we clicked multiple times
expect(wrapper.vm.changeTab).toHaveBeenCalledTimes(1)
})
})
14 changes: 13 additions & 1 deletion src/components/tabs/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default {
data() {
return {
activeTab: this.value || 0,
tabItems: [],
defaultSlots: [],
contentHeight: 0,
isTransitioning: false,
_isTabs: true // Used internally by TabItem
Expand All @@ -85,6 +85,14 @@ export default {
'is-toggle-rounded is-toggle': this.type === 'is-toggle-rounded'
}
]
},
tabItems() {
return this.defaultSlots
.filter((vnode) =>
vnode.componentInstance &&
vnode.componentInstance.$data &&
vnode.componentInstance.$data._isTabItem)
.map((vnode) => vnode.componentInstance)
}
},
watch: {
Expand All @@ -105,6 +113,9 @@ export default {
}
},
methods: {
refreshSlots() {
this.defaultSlots = this.$slots.default
},
/**
* Change the active tab and emit change event.
*/
Expand Down Expand Up @@ -132,6 +143,7 @@ export default {
if (this.activeTab < this.tabItems.length) {
this.tabItems[this.activeTab].isActive = true
}
this.refreshSlots()
}
}
</script>
7 changes: 5 additions & 2 deletions src/components/tabs/__snapshots__/Tabs.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ exports[`BTabs render correctly 1`] = `
<ul>
<li class="is-active"><a>
<!----> <span></span></a></li>
<li class=""><a>
<li class="" style="display: none;"><a>
<!----> <span></span></a></li>
</ul>
</nav>
<section class="tab-content"></section>
<section class="tab-content">
<b-tab-item-stub></b-tab-item-stub>
<b-tab-item-stub></b-tab-item-stub>
</section>
</div>
`;

0 comments on commit 36d9dc2

Please sign in to comment.