+123
-11
lines changedFilter options
+123
-11
lines changed Original file line number Diff line number Diff line change
@@ -419,6 +419,23 @@ methods are `.activate()` and `.deactivate()`, respectively. If activation or de
419
419
will remain active and the method will return `false`. You will need a reference to the `<b-tab>` in
420
420
order to use these methods.
421
421
422
+
## Preventing a `<b-tab>` from being activated
423
+
424
+
To prevent a tab from activating, simply set the `disabled` prop on the `<b-tab>` component.
425
+
426
+
Alternatively, you can listen for the `activate-tab` event, which provides an option to prevent the
427
+
tab from activating. The `activate-tab` event is emitted with three arguments:
428
+
429
+
- `newTabIndex`: The index of the tab that is going to be activated
430
+
- `prevTabIndex`: The index of the currently active tab
431
+
- `bvEvent`: The `BvEvent` object. Call `bvEvt.preventDefault()` to prevent `newTabIndex` from being
432
+
activated
433
+
434
+
For accessibility reasons, when using the `activate-tab` event to prevent a tab from activating, you
435
+
should provide some means of notification to the user as to why the tab is not able to be activated.
436
+
It is recommended to use the `disabled` attribute on the `<b-tab>` component instead of using the
437
+
`activate-tab` event (as `disabled` is more intuitive for screen reader users).
438
+
422
439
## Advanced examples
423
440
424
441
### External controls using `v-model`
Original file line number Diff line number Diff line change
@@ -93,6 +93,28 @@
93
93
}
94
94
]
95
95
},
96
+
{
97
+
"event": "activate-tab",
98
+
"version": "2.1.0",
99
+
"description": "Emitted just before a tab is shown/activated. Cancelable",
100
+
"args": [
101
+
{
102
+
"arg": "newTabIndex",
103
+
"type": "Number",
104
+
"description": "Tab being activated (0-based index)"
105
+
},
106
+
{
107
+
"arg": "prevTabIndex",
108
+
"type": "Number",
109
+
"description": "Tab that is currently active (0-based index). Will be -1 if no current active tab"
110
+
},
111
+
{
112
+
"arg": "bvEvt",
113
+
"type": "BvEvent",
114
+
"description": "BvEvent object. Call bvEvt.preventDefault() to cancel"
115
+
}
116
+
]
117
+
},
96
118
{
97
119
"event": "changed",
98
120
"description": "Emitted when a tab is added, removed, or tabs are re-ordered",
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import looseEqual from '../../utils/loose-equal'
4
4
import observeDom from '../../utils/observe-dom'
5
5
import stableSort from '../../utils/stable-sort'
6
6
import { arrayIncludes, concat } from '../../utils/array'
7
+
import { BvEvent } from '../../utils/bv-event.class'
7
8
import { requestAF, selectAll } from '../../utils/dom'
8
9
import { isEvent } from '../../utils/inspect'
9
10
import { omit } from '../../utils/object'
@@ -262,7 +263,7 @@ export const BTabs = /*#__PURE__*/ Vue.extend({
262
263
old = parseInt(old, 10) || 0
263
264
const tabs = this.tabs
264
265
if (tabs[val] && !tabs[val].disabled) {
265
-
this.currentTab = val
266
+
this.activateTab(tabs[val])
266
267
} else {
267
268
// Try next or prev tabs
268
269
if (val < old) {
@@ -481,14 +482,22 @@ export const BTabs = /*#__PURE__*/ Vue.extend({
481
482
let result = false
482
483
if (tab) {
483
484
const index = this.tabs.indexOf(tab)
484
-
if (!tab.disabled && index > -1) {
485
-
result = true
486
-
this.currentTab = index
485
+
if (!tab.disabled && index > -1 && index !== this.currentTab) {
486
+
const tabEvt = new BvEvent('activate-tab', {
487
+
cancelable: true,
488
+
vueTarget: this,
489
+
componentId: this.safeId()
490
+
})
491
+
this.$emit(tabEvt.type, index, this.currentTab, tabEvt)
492
+
if (!tabEvt.defaultPrevented) {
493
+
result = true
494
+
this.currentTab = index
495
+
}
487
496
}
488
497
}
489
-
if (!result) {
490
-
// Couldn't set tab, so ensure v-model is set to `this.currentTab`
491
-
/* istanbul ignore next: should rarely happen */
498
+
// Couldn't set tab, so ensure v-model is set to `this.currentTab`
499
+
/* istanbul ignore next: should rarely happen */
500
+
if (!result && this.currentTab !== this.value) {
492
501
this.$emit('input', this.currentTab)
493
502
}
494
503
return result
@@ -500,11 +509,9 @@ export const BTabs = /*#__PURE__*/ Vue.extend({
500
509
// Find first non-disabled tab that isn't the one being deactivated
501
510
// If no tabs are available, then don't deactivate current tab
502
511
return this.activateTab(this.tabs.filter(t => t !== tab).find(notDisabled))
503
-
} else {
504
-
// No tab specified
505
-
/* istanbul ignore next: should never happen */
506
-
return false
507
512
}
513
+
/* istanbul ignore next: should never/rarely happen */
514
+
return false
508
515
},
509
516
// Focus a tab button given it's <b-tab> instance
510
517
focusButton(tab) {
Original file line number Diff line number Diff line change
@@ -311,6 +311,72 @@ describe('tabs', () => {
311
311
wrapper.destroy()
312
312
})
313
313
314
+
it('`activate-tab` event works', async () => {
315
+
const App = Vue.extend({
316
+
methods: {
317
+
preventTab(next, prev, bvEvt) {
318
+
// Prevent 3rd tab (index === 2) from activating
319
+
if (next === 2) {
320
+
bvEvt.preventDefault()
321
+
}
322
+
}
323
+
},
324
+
render(h) {
325
+
return h(BTabs, { props: { value: 0 }, on: { 'activate-tab': this.preventTab } }, [
326
+
h(BTab, { props: {} }, 'tab 0'),
327
+
h(BTab, { props: {} }, 'tab 1'),
328
+
h(BTab, { props: {} }, 'tab 2')
329
+
])
330
+
}
331
+
})
332
+
const wrapper = mount(App)
333
+
expect(wrapper).toBeDefined()
334
+
335
+
await waitNT(wrapper.vm)
336
+
await waitRAF()
337
+
const tabs = wrapper.find(BTabs)
338
+
expect(tabs).toBeDefined()
339
+
expect(tabs.findAll(BTab).length).toBe(3)
340
+
341
+
// Expect 1st tab (index 0) to be active
342
+
expect(tabs.vm.currentTab).toBe(0)
343
+
expect(tabs.vm.tabs[0].localActive).toBe(true)
344
+
expect(tabs.emitted('input')).not.toBeDefined()
345
+
expect(tabs.emitted('activate-tab')).not.toBeDefined()
346
+
347
+
// Set 2nd BTab to be active
348
+
tabs.setProps({ value: 1 })
349
+
await waitNT(wrapper.vm)
350
+
await waitRAF()
351
+
expect(tabs.vm.currentTab).toBe(1)
352
+
expect(tabs.emitted('input')).toBeDefined()
353
+
expect(tabs.emitted('input').length).toBe(1)
354
+
expect(tabs.emitted('input')[0][0]).toBe(1)
355
+
expect(tabs.emitted('activate-tab')).toBeDefined()
356
+
expect(tabs.emitted('activate-tab').length).toBe(1)
357
+
expect(tabs.emitted('activate-tab')[0][0]).toBe(1)
358
+
expect(tabs.emitted('activate-tab')[0][1]).toBe(0)
359
+
expect(tabs.emitted('activate-tab')[0][2]).toBeDefined()
360
+
expect(tabs.emitted('activate-tab')[0][2].vueTarget).toBe(tabs.vm)
361
+
362
+
// Attempt to set 3rd BTab to be active
363
+
tabs.setProps({ value: 2 })
364
+
await waitNT(wrapper.vm)
365
+
await waitRAF()
366
+
expect(tabs.vm.currentTab).toBe(1)
367
+
expect(tabs.emitted('input')).toBeDefined()
368
+
expect(tabs.emitted('input').length).toBe(2)
369
+
expect(tabs.emitted('input')[1][0]).toBe(1)
370
+
expect(tabs.emitted('activate-tab').length).toBe(2)
371
+
expect(tabs.emitted('activate-tab')[1][0]).toBe(2)
372
+
expect(tabs.emitted('activate-tab')[1][1]).toBe(1)
373
+
expect(tabs.emitted('activate-tab')[1][2]).toBeDefined()
374
+
expect(tabs.emitted('activate-tab')[1][2].vueTarget).toBe(tabs.vm)
375
+
expect(tabs.emitted('activate-tab')[1][2].defaultPrevented).toBe(true)
376
+
377
+
wrapper.destroy()
378
+
})
379
+
314
380
it('clicking on tab activates the tab, and tab emits click event', async () => {
315
381
const App = Vue.extend({
316
382
render(h) {
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4