Compare commits

...
Sign in to create a new pull request.

45 commits

Author SHA1 Message Date
XhmikosR
897f4db11a
Update tab.js 2020-11-25 15:02:12 +02:00
Patrick H. Lauke
c11118ba23
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-07-13 22:23:12 +01:00
Brian Juul Andersen
4fdae4b1a8 Update collapse.md (#29025)
Corrected minor spelling error.
2019-07-13 22:20:44 +01:00
Mark Otto
a13fba81f2 dist v5 2019-07-13 22:20:44 +01:00
Mark Otto
79d538703c v5: Forms update (#28450)
* Initial spike of consolidated form checks

* Stub out forms rearrangement

- Prepping to drop non-custom file and range inputs
- Prepping to merge custom and native checks and radios (with switches)
- Prepping to merge custom select with form select
- Moving docs arround so forms has it's own area given volume of CSS

* Move input group Sass file to forms subdir

* Start to split and move the docs around

* Simpler imports

* Copyediting

* delete overview file

* Remove commented out code

* remove the custom-forms import

* rewrite flex-check as form-check, replace all custom properties

* Remove old forms doc

* stub out new subpage link section

* update migration guide

* Update nav, forms overview in page nav, and descriptions

* fix check bg position

* fix margin-top calculation

* rename .custom-select to .form-select

* Update validation styles for new checks

* add some vertical margin, fix inline checks

* fix docs examples

* better way to do this contents stuff, redo the toc while i'm at it

* page restyle for docs while here

* un-callout that, edit text

* redo padding on toc

* fix toc

* start to cleanup checks docs

* Rewrite Markdown tables into HTML

* Redesign tables, redo their docs

* Replace Open Iconic icons with custom Bootstrap icons

* Redesign the docs navbar, add a subheader, redo the sidebar

* Redesign docs homepage a bit

* Simplify table style overrides for docs tables

* Simplify docs typography for page titles and reading line length

* Stub out icons page

* Part of sidebar update, remove migration from nav.yml

* Move toc CSS to separate partial

* Change appearance of overview page

* fix sidebar arrow direction

* Add footer to docs layout

* Update descriptions

* Drop the .form-group class for margin utilities

* Remove lingering form-group-margin-bottom var

* improve footer spacing

* add headings to range page

* uncomment form range css

* Rename .custom-range to .form-range

* Drop unused docs var

* Uncomment the comment

* Remove unused variable

* Fix radio image sizing

* Reboot update: reset horizontal ul and ol padding

* de-dupe IDs

* tweak toc styles

* nvm, fix dropdown versions stuff

* remove sidebar nav toggle for now

* broken html

* fix more broken html, move css

* scss linting

* comment out broken helper docs

* scope styles

* scope styles

* Fixes #25540 and fixes #26407 for v5 only

* Update sidebar once more

* Match new sidenav order

* fix syntax error

* Rename custom-file to form-file, update paths, update migration docs for previous changes in #28696

* rename back

* fix size and alignment

* rename that back too
2019-07-13 22:20:44 +01:00
Shohei Yoshida
cc5353da66 Drop support for .form-control-plaintext inside .input-group (#28972)
* Revert "Fix readonly-plain-text with button addon (#25871)"

This reverts commit ff40e00323.

* Add migration comment
2019-07-13 22:20:43 +01:00
Martijn Cuppens
bfe5438af2 Remove attribute selectors (#28988) 2019-07-13 22:20:43 +01:00
Johann-S
e81f20cb0e separate file for our polyfills to have lighter plugins 2019-07-13 22:20:43 +01:00
XhmikosR
349eeeb532 Fix typo. (#29008) 2019-07-13 22:20:43 +01:00
Mark Otto
76f87e2bef Fixes #28994 2019-07-13 22:20:42 +01:00
patrickhlauke
a38a3bd904 Fix visual unit test
fix the borked examples of dynamic tab panels, and remove the now unsupported tabs with dropdowns
2019-07-13 22:20:42 +01:00
patrickhlauke
430a1204aa Add extensive keyboard handling unit tests
very verbose, but fully tests that cursor keys are handled correctly and have the desired final result (though stops short of checking the actual tab panels themselves)
2019-07-13 22:02:04 +01:00
patrickhlauke
fabe1f0e24 Remove redundant sanity checks
unnecessary, as these checks should never fail (since the handler would not be called if there weren't any tabs, and the `index > 0` clause in the preceding `if` ensures that `index` will never be negative
2019-07-13 21:52:21 +01:00
patrickhlauke
1687b77a48 Fix one of the existing unit tests
the markup fragment does not reflect proper code structure (surprising that it still worked...)
2019-07-13 16:52:28 +01:00
patrickhlauke
eb299c90e5 Unit test for tablist initialization
- check that `tabindex` and `aria-selected` are properly set up with only one `aria-selected="true"` and `tabindex="0"`
- check that as fallback the first tab control is set to `aria-selected="true"`
2019-07-13 16:48:26 +01:00
patrickhlauke
65b32a0107 Merge branch 'patrickhlauke-tabs-kbd-automatic-activation' of https://github.com/twbs/bootstrap into patrickhlauke-tabs-kbd-automatic-activation 2019-07-13 10:56:38 +01:00
Patrick H. Lauke
eace005286
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-07-09 17:03:13 +01:00
Patrick H. Lauke
10b293f23e
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-07-09 00:12:32 +01:00
Johann-S
ea0e99dfc5 add transitioning state to avoid displaying two tabs in the same time 2019-07-08 14:45:04 +02:00
patrickhlauke
d5616fe0ce Add missing aria-orientation to list group tablists
For correct keyboard handling (cursor up/down) when interacting with these
2019-07-08 14:20:19 +02:00
patrickhlauke
3c8170044a Tweak
Stick with the naive `tabs[index].click()` as it works correctly, and trying to trigger just `show()` of a tab instance resulted in weird side effects...

Tighten the outer `makeArrary`/`forEach` loop (assuming when there's an empty array the forEach just bails, so no need to check the length?)
2019-07-08 14:20:19 +02:00
patrickhlauke
7c79b8a80f Replace for loop with forEach 2019-07-08 14:20:19 +02:00
patrickhlauke
66d0d7b05e Make note about dropdowns in tabs an actual callout 2019-07-08 14:20:19 +02:00
patrickhlauke
35179a4aee Remove dropdown-in-tabs related unit tests
Since that logic has now been expunged
2019-07-08 14:20:19 +02:00
patrickhlauke
a8cf124b82 Initial code tweaks based on comments 2019-07-08 14:20:19 +02:00
patrickhlauke
31dd87c600 Add explicit note about aria-orientation, add all missing role="presentation"
`role="presentation"` is needed as otherwise AT (like NVDA) will get confused/won't be able to "count" the total number of tabs properly
2019-07-08 14:20:19 +02:00
patrickhlauke
a1e7738632 Remove dropdown-in-tabs related code
- there's no ARIA pattern that would allow dropdowns in tablists; it cannot be expressed accessibly to AT users. it also has serious usability drawbacks. we're already saying in the docs it should not be
used ... this goes a step further and removes the handling for it
2019-07-08 14:20:19 +02:00
patrickhlauke
da9f93da1c WIP add initial keyboard handling and initialisation
- initialisation happens very inelegantly - could do with some advice on how to move this to a function (should it be public, private, static?)
- keyboard handling works, but seems to get confused when switching too quickly between tabs sometimes
- activating tabs currently done via click() - there must be a nicer way to do this...how? fire _activate? which parameters to pass?
2019-07-08 14:20:19 +02:00
patrickhlauke
2e5a010901 Add initial tabindex handling for tabs
set `tabindex` as well as `aria-selected` on tabs
2019-07-08 14:20:18 +02:00
Patrick H. Lauke
007c75837d
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-07-06 11:46:30 +01:00
Patrick H. Lauke
8be4c1364f
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-06-25 15:21:49 +01:00
Patrick H. Lauke
3b64256ac5
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-06-25 09:47:11 +01:00
Patrick H. Lauke
ef0c5c04e3
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-06-24 21:14:06 +01:00
patrickhlauke
8de6558408 Add missing aria-orientation to list group tablists
For correct keyboard handling (cursor up/down) when interacting with these
2019-06-19 14:16:33 +01:00
Patrick H. Lauke
d91293d966
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-06-19 12:58:23 +01:00
patrickhlauke
95ebcedccd Tweak
Stick with the naive `tabs[index].click()` as it works correctly, and trying to trigger just `show()` of a tab instance resulted in weird side effects...

Tighten the outer `makeArrary`/`forEach` loop (assuming when there's an empty array the forEach just bails, so no need to check the length?)
2019-06-19 12:47:13 +01:00
patrickhlauke
cbac96d5b0 Replace for loop with forEach 2019-06-19 11:01:25 +01:00
patrickhlauke
701977bf4e Make note about dropdowns in tabs an actual callout 2019-06-19 10:54:19 +01:00
patrickhlauke
d722edcb29 Remove dropdown-in-tabs related unit tests
Since that logic has now been expunged
2019-06-19 10:48:56 +01:00
patrickhlauke
53e954c834 Initial code tweaks based on comments 2019-06-19 10:22:34 +01:00
Patrick H. Lauke
578444b95e
Merge branch 'master' into patrickhlauke-tabs-kbd-automatic-activation 2019-06-18 21:22:35 +01:00
patrickhlauke
78737b239f Add explicit note about aria-orientation, add all missing role="presentation"
`role="presentation"` is needed as otherwise AT (like NVDA) will get confused/won't be able to "count" the total number of tabs properly
2019-06-18 20:32:52 +01:00
patrickhlauke
33df950b27 Remove dropdown-in-tabs related code
- there's no ARIA pattern that would allow dropdowns in tablists; it cannot be expressed accessibly to AT users. it also has serious usability drawbacks. we're already saying in the docs it should not be
used ... this goes a step further and removes the handling for it
2019-06-18 20:19:53 +01:00
patrickhlauke
7c90656c13 WIP add initial keyboard handling and initialisation
- initialisation happens very inelegantly - could do with some advice on how to move this to a function (should it be public, private, static?)
- keyboard handling works, but seems to get confused when switching too quickly between tabs sometimes
- activating tabs currently done via click() - there must be a nicer way to do this...how? fire _activate? which parameters to pass?
2019-06-18 19:59:05 +01:00
patrickhlauke
f4d85517b9 Add initial tabindex handling for tabs
set `tabindex` as well as `aria-selected` on tabs
2019-06-17 23:45:07 +01:00
5 changed files with 357 additions and 188 deletions

View file

@ -29,17 +29,22 @@ const VERSION = '4.3.1'
const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
const Event = {
HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`,
SHOW: `show${EVENT_KEY}`,
SHOWN: `shown${EVENT_KEY}`,
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`,
KEYDOWN_DATA_API: `keydown${EVENT_KEY}${DATA_API_KEY}`,
LOAD_DATA_API: `load${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DROPDOWN_MENU: 'dropdown-menu',
ACTIVE: 'active',
DISABLED: 'disabled',
FADE: 'fade',
@ -47,15 +52,16 @@ const ClassName = {
}
const Selector = {
DROPDOWN: '.dropdown',
NAV_LIST_GROUP: '.nav, .list-group',
ACTIVE: '.active',
ACTIVE_UL: ':scope > li > .active',
DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE: '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD: ':scope > .dropdown-menu .active'
TABLIST: '[role="tablist"]'
}
const ORIENTATION_VERTICAL = 'vertical'
const ORIENTATION_HORIZONTAL = 'horizontal'
/**
* ------------------------------------------------------------------------
* Class Definition
@ -97,6 +103,7 @@ class Tab {
}
let hideEvent = null
this.isTransitioning = true
if (previous) {
hideEvent = EventHandler.trigger(previous, Event.HIDE, {
@ -129,6 +136,7 @@ class Tab {
EventHandler.trigger(this._element, Event.SHOWN, {
relatedTarget: previous
})
this.isTransitioning = false
}
if (target) {
@ -175,20 +183,16 @@ class Tab {
if (active) {
active.classList.remove(ClassName.ACTIVE)
const dropdownChild = SelectorEngine.findOne(Selector.DROPDOWN_ACTIVE_CHILD, active.parentNode)
if (dropdownChild) {
dropdownChild.classList.remove(ClassName.ACTIVE)
}
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false)
active.setAttribute('tabindex', '-1')
}
}
element.classList.add(ClassName.ACTIVE)
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true)
element.setAttribute('tabindex', '0')
}
reflow(element)
@ -197,17 +201,6 @@ class Tab {
element.classList.add(ClassName.SHOW)
}
if (element.parentNode && element.parentNode.classList.contains(ClassName.DROPDOWN_MENU)) {
const dropdownElement = SelectorEngine.closest(element, Selector.DROPDOWN)
if (dropdownElement) {
makeArray(SelectorEngine.find(Selector.DROPDOWN_TOGGLE))
.forEach(dropdown => dropdown.classList.add(ClassName.ACTIVE))
}
element.setAttribute('aria-expanded', true)
}
if (callback) {
callback()
}
@ -229,6 +222,48 @@ class Tab {
})
}
static _dataApiKeydownHandler(event) {
const tablist = SelectorEngine.closest(event.target, Selector.TABLIST)
let tabListOrientation = tablist.getAttribute('aria-orientation')
if (tabListOrientation !== ORIENTATION_VERTICAL) {
tabListOrientation = ORIENTATION_HORIZONTAL
}
if ((tabListOrientation === ORIENTATION_HORIZONTAL && event.which !== ARROW_LEFT_KEYCODE && event.which !== ARROW_RIGHT_KEYCODE) ||
(tabListOrientation === ORIENTATION_VERTICAL && event.which !== ARROW_UP_KEYCODE && event.which !== ARROW_DOWN_KEYCODE)) {
return
}
event.preventDefault()
event.stopPropagation()
if (this.disabled || this.classList.contains(ClassName.DISABLED)) {
return
}
const tabs = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE, tablist))
let index = tabs.indexOf(event.target)
const tabInstance = Data.getData(tabs[index], DATA_KEY) || new Tab(tabs[index])
if (tabInstance.isTransitioning) {
return
}
// Left / Up
if ((event.which === ARROW_LEFT_KEYCODE || event.which === ARROW_UP_KEYCODE) && index > 0) {
index--
}
// Right / Down
if ((event.which === ARROW_RIGHT_KEYCODE || event.which === ARROW_DOWN_KEYCODE) && index < tabs.length - 1) {
index++
}
tabs[index].focus()
tabs[index].click()
}
static _getInstance(element) {
return Data.getData(element, DATA_KEY)
}
@ -240,6 +275,31 @@ class Tab {
* ------------------------------------------------------------------------
*/
EventHandler.on(window, Event.LOAD_DATA_API, () => {
makeArray(SelectorEngine.find(Selector.TABLIST))
.forEach(tablist => {
const tabs = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE, tablist))
let selectedTabFound = false
// iterate over each tab in the tablist, make sure they have correct tabindex/aria-selected
tabs.forEach(tab => {
if (tab.getAttribute('aria-selected') === 'true' && !selectedTabFound) {
tab.setAttribute('tabindex', '0')
selectedTabFound = true
} else {
tab.setAttribute('tabindex', '-1')
tab.setAttribute('aria-selected', 'false')
}
})
// if none of the tabs were explicitly marked as selected, pick first one
if (!selectedTabFound) {
tabs[0].setAttribute('tabindex', '0')
tabs[0].setAttribute('aria-selected', 'true')
}
})
})
EventHandler.on(document, Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Tab._dataApiKeydownHandler)
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()

View file

@ -180,35 +180,6 @@ $(function () {
.bootstrapTab('show')
})
QUnit.test('show and shown events should reference correct relatedTarget', function (assert) {
assert.expect(2)
var done = assert.async()
var dropHTML =
'<ul class="drop nav">' +
' <li class="dropdown"><a data-toggle="dropdown" href="#">1</a>' +
' <ul class="dropdown-menu nav">' +
' <li><a href="#a1-1" data-toggle="tab">1-1</a></li>' +
' <li><a href="#a1-2" data-toggle="tab">1-2</a></li>' +
' </ul>' +
' </li>' +
'</ul>'
$(dropHTML)
.find('ul > li:first-child a')
.bootstrapTab('show')
.end()
.find('ul > li:last-child a')
.on('show.bs.tab', function (e) {
assert.strictEqual(e.relatedTarget.hash, '#a1-1', 'references correct element as relatedTarget')
})
.on('shown.bs.tab', function (e) {
assert.strictEqual(e.relatedTarget.hash, '#a1-1', 'references correct element as relatedTarget')
done()
})
.bootstrapTab('show')
})
QUnit.test('should fire hide and hidden events', function (assert) {
assert.expect(2)
var done = assert.async()
@ -265,6 +236,30 @@ $(function () {
.bootstrapTab('show')
})
QUnit.test('show and shown events should reference correct relatedTarget', function (assert) {
assert.expect(2)
var done = assert.async()
var tabsHTML = '<ul class="nav">' +
'<li><a href="#home">Home</a></li>' +
'<li><a href="#profile">Profile</a></li>' +
'</ul>'
$(tabsHTML)
.find('li:first-child a')
.bootstrapTab('show')
.end()
.find('li:last-child a')
.on('show.bs.tab', function (e) {
assert.strictEqual(e.relatedTarget.hash, '#home', 'references correct element as relatedTarget')
})
.on('shown.bs.tab', function (e) {
assert.strictEqual(e.relatedTarget.hash, '#home', 'references correct element as relatedTarget')
done()
})
.bootstrapTab('show')
})
QUnit.test('hide and hidden events contain correct relatedTarget', function (assert) {
assert.expect(2)
var done = assert.async()
@ -291,9 +286,9 @@ $(function () {
QUnit.test('selected tab should have aria-selected', function (assert) {
assert.expect(8)
var tabsHTML = '<ul class="nav nav-tabs">' +
'<li><a class="nav-item active" href="#home" toggle="tab" aria-selected="true">Home</a></li>' +
'<li><a class="nav-item" href="#profile" toggle="tab" aria-selected="false">Profile</a></li>' +
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
'<li><a class="nav-item active" href="#home" data-toggle="tab" role="tab" aria-selected="true">Home</a></li>' +
'<li><a class="nav-item" href="#profile" data-toggle="tab" role="tab" aria-selected="false">Profile</a></li>' +
'</ul>'
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
@ -327,26 +322,6 @@ $(function () {
assert.ok($tabs.find('li:last-child a').hasClass('active'))
})
QUnit.test('selected tab should deactivate previous selected link in dropdown', function (assert) {
assert.expect(3)
var tabsHTML = '<ul class="nav nav-tabs">' +
'<li class="nav-item"><a class="nav-link" href="#home" data-toggle="tab">Home</a></li>' +
'<li class="nav-item"><a class="nav-link" href="#profile" data-toggle="tab">Profile</a></li>' +
'<li class="nav-item dropdown"><a class="nav-link dropdown-toggle active" data-toggle="dropdown" href="#">Dropdown</a>' +
'<div class="dropdown-menu">' +
'<a class="dropdown-item active" href="#dropdown1" id="dropdown1-tab" data-toggle="tab">@fat</a>' +
'<a class="dropdown-item" href="#dropdown2" id="dropdown2-tab" data-toggle="tab">@mdo</a>' +
'</div>' +
'</li>' +
'</ul>'
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
$tabs.find('li:first-child a')[0].click()
assert.ok($tabs.find('li:first-child a').hasClass('active'))
assert.notOk($tabs.find('li:last-child a').hasClass('active'))
assert.notOk($tabs.find('li:last-child .dropdown-menu a:first-child').hasClass('active'))
})
QUnit.test('Nested tabs', function (assert) {
assert.expect(2)
var done = assert.async()
@ -523,6 +498,213 @@ $(function () {
$('#secondNav')[0].click()
})
QUnit.test('should initialize tab list: only one tab control focusable and aria-selected', function (assert) {
assert.expect(5)
var done = assert.async()
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
'<li><a class="nav-item active" href="#home" data-toggle="tab" role="tab" aria-selected="true">Home</a></li>' +
'<li><a class="nav-item" href="#profile" data-toggle="tab" role="tab" aria-selected="true">Profile</a></li>' +
'<li><a class="nav-item" href="#contact" data-toggle="tab" role="tab" aria-selected="true">Contact</a></li>' +
'</ul>' /* purposely incorrect, with all set to aria-selected="true" */
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
window.dispatchEvent(new Event('load'))
setTimeout(function () {
assert.strictEqual($tabs.find('li:first-child a').attr('tabindex'), '0', 'first found aria-selected="true" control has been given tabindex="0"')
assert.strictEqual($tabs.find('li:nth-child(2) a').attr('tabindex'), '-1', 'second control has been given tabindex="-1"')
assert.strictEqual($tabs.find('li:nth-child(2) a').attr('aria-selected'), 'false', 'second control has been given aria-selected="false"')
assert.strictEqual($tabs.find('li:nth-child(3) a').attr('tabindex'), '-1', 'third control has been given tabindex="-1"')
assert.strictEqual($tabs.find('li:nth-child(3) a').attr('aria-selected'), 'false', 'third control has been given aria-selected="false"')
done()
}, 10)
})
QUnit.test('should initialize tab list: if no tab control has aria-selected="true", set it to the first one', function (assert) {
assert.expect(3)
var done = assert.async()
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
'<li><a class="nav-item active" href="#home" data-toggle="tab">Home</a></li>' +
'<li><a class="nav-item" href="#profile" data-toggle="tab">Profile</a></li>' +
'<li><a class="nav-item" href="#contact" data-toggle="tab">Contact</a></li>' +
'</ul>' /* purposely incorrect, left out aria-selected="true" on active one */
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
window.dispatchEvent(new Event('load'))
setTimeout(function () {
assert.strictEqual($tabs.find('li:first-child a').attr('aria-selected'), 'true', 'first control has been given aria-selected="true"')
assert.strictEqual($tabs.find('li:nth-child(2) a').attr('aria-selected'), 'false', 'second control has been given aria-selected="false"')
assert.strictEqual($tabs.find('li:nth-child(3) a').attr('aria-selected'), 'false', 'third control has been given aria-selected="false"')
done()
}, 10)
})
QUnit.test('should correctly switch tabs in response to left/right cursor keys (for horizontal/default tablist) and ignore up/down cursor keys', function (assert) {
assert.expect(27)
var done = assert.async()
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
'<li><a class="nav-item active" href="#home" data-toggle="tab" role="tab" tabindex="0" aria-selected="true">Home</a></li>' +
'<li><a class="nav-item" href="#profile" data-toggle="tab" role="tab" tabindex="-1" aria-selected="false">Profile</a></li>' +
'<li><a class="nav-item" href="#contact" data-toggle="tab" role="tab" tabindex="-1" aria-selected="false">Contact</a></li>' +
'</ul>' +
'<div class="tab-pane show active" id="home" role="tabpanel">...</div>' +
'<div class="tab-pane" id="profile" role="tabpanel">...</div>' +
'<div class="tab-pane" id="contact" role="tabpanel">...</div>'
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
var $firstTab = $tabs.find('li:first-child a')
var $secondTab = $tabs.find('li:nth-child(2) a')
var $thirdTab = $tabs.find('li:nth-child(3) a')
$firstTab[0].focus() /* set focus to first tab */
assert.ok(document.activeElement === $firstTab[0], 'first tab successfully focused')
/* right cursor key */
var keyDown = new Event('keydown')
keyDown.which = 39
$firstTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $firstTab[0], 'after 1st right cursor key, first tab not focused anymore')
assert.ok(!$firstTab.hasClass('active'), 'after 1st right cursor key, first tab not `.active` anymore')
assert.ok($firstTab.attr('aria-selected') === 'false', 'after 1st right cursor key, first tab has aria-selected="false"')
assert.ok($firstTab.attr('tabindex') === '-1', 'after 1st right cursor key, first tab has tabindex="-1"')
assert.ok(document.activeElement === $secondTab[0], 'after 1st right cursor key, second tab is focused')
assert.ok($secondTab.hasClass('active'), 'after 1st right cursor key, second tab is `.active`')
assert.ok($secondTab.attr('aria-selected') === 'true', 'after 1st right cursor key, second tab has aria-selected="true"')
assert.ok($secondTab.attr('tabindex') === '0', 'after 1st right cursor key, second tab has tabindex="0"')
/* right cursor key */
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $secondTab[0], 'after 2nd right cursor key, second tab not focused anymore')
assert.ok(!$secondTab.hasClass('active'), 'after 2nd right cursor key, second tab not `.active` anymore')
assert.ok($secondTab.attr('aria-selected') === 'false', 'after 2nd right cursor key, second tab has aria-selected="false"')
assert.ok($secondTab.attr('tabindex') === '-1', 'after 2nd right cursor key, second tab has tabindex="-1"')
assert.ok(document.activeElement === $thirdTab[0], 'after 2nd right cursor key, third tab is focused')
assert.ok($thirdTab.hasClass('active'), 'after 2nd right cursor key, third tab is `.active`')
assert.ok($thirdTab.attr('aria-selected') === 'true', 'after 2nd right cursor key, third tab has aria-selected="true"')
assert.ok($thirdTab.attr('tabindex') === '0', 'after 2nd right cursor key, third tab has tabindex="0"')
/* left cursor key */
keyDown.which = 37
$thirdTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $thirdTab[0], 'after left cursor key, third tab not focused anymore')
assert.ok(!$thirdTab.hasClass('active'), 'after left cursor key, third tab not `.active` anymore')
assert.ok($thirdTab.attr('aria-selected') === 'false', 'after left cursor key, third tab has aria-selected="false"')
assert.ok($thirdTab.attr('tabindex') === '-1', 'after left cursor key, third tab has tabindex="-1"')
assert.ok(document.activeElement === $secondTab[0], 'after left cursor key, second tab is focused')
assert.ok($secondTab.hasClass('active'), 'after left cursor key, second tab is `.active`')
assert.ok($secondTab.attr('aria-selected') === 'true', 'after left cursor key, second tab has aria-selected="true"')
assert.ok($secondTab.attr('tabindex') === '0', 'after left cursor key, second tab has tabindex="0"')
/* up cursor key */
keyDown.which = 38
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement === $secondTab[0], 'after up cursor key, second tab is still focused - no change')
/* down cursor key */
keyDown.which = 40
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement === $secondTab[0], 'after down cursor key, second tab is still focused - no change')
done()
})
QUnit.test('should correctly switch tabs in response to up/down cursor keys for vertical tablist and ignore left/right cursor keys', function (assert) {
assert.expect(27)
var done = assert.async()
var tabsHTML = '<ul class="nav nav-tabs" role="tablist" aria-orientation="vertical">' +
'<li><a class="nav-item active" href="#home" data-toggle="tab" role="tab" tabindex="0" aria-selected="true">Home</a></li>' +
'<li><a class="nav-item" href="#profile" data-toggle="tab" role="tab" tabindex="-1" aria-selected="false">Profile</a></li>' +
'<li><a class="nav-item" href="#contact" data-toggle="tab" role="tab" tabindex="-1" aria-selected="false">Contact</a></li>' +
'</ul>' +
'<div class="tab-pane show active" id="home" role="tabpanel">...</div>' +
'<div class="tab-pane" id="profile" role="tabpanel">...</div>' +
'<div class="tab-pane" id="contact" role="tabpanel">...</div>'
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
var $firstTab = $tabs.find('li:first-child a')
var $secondTab = $tabs.find('li:nth-child(2) a')
var $thirdTab = $tabs.find('li:nth-child(3) a')
$firstTab[0].focus() /* set focus to first tab */
assert.ok(document.activeElement === $firstTab[0], 'first tab successfully focused')
/* down cursor key */
var keyDown = new Event('keydown')
keyDown.which = 40
$firstTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $firstTab[0], 'after 1st down cursor key, first tab not focused anymore')
assert.ok(!$firstTab.hasClass('active'), 'after 1st down cursor key, first tab not `.active` anymore')
assert.ok($firstTab.attr('aria-selected') === 'false', 'after 1st down cursor key, first tab has aria-selected="false"')
assert.ok($firstTab.attr('tabindex') === '-1', 'after 1st down cursor key, first tab has tabindex="-1"')
assert.ok(document.activeElement === $secondTab[0], 'after 1st down cursor key, second tab is focused')
assert.ok($secondTab.hasClass('active'), 'after 1st down cursor key, second tab is `.active`')
assert.ok($secondTab.attr('aria-selected') === 'true', 'after 1st down cursor key, second tab has aria-selected="true"')
assert.ok($secondTab.attr('tabindex') === '0', 'after 1st down cursor key, second tab has tabindex="0"')
/* down cursor key */
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $secondTab[0], 'after 2nd down cursor key, second tab not focused anymore')
assert.ok(!$secondTab.hasClass('active'), 'after 2nd down cursor key, second tab not `.active` anymore')
assert.ok($secondTab.attr('aria-selected') === 'false', 'after 2nd down cursor key, second tab has aria-selected="false"')
assert.ok($secondTab.attr('tabindex') === '-1', 'after 2nd down cursor key, second tab has tabindex="-1"')
assert.ok(document.activeElement === $thirdTab[0], 'after 2nd down cursor key, third tab is focused')
assert.ok($thirdTab.hasClass('active'), 'after 2nd down cursor key, third tab is `.active`')
assert.ok($thirdTab.attr('aria-selected') === 'true', 'after 2nd down cursor key, third tab has aria-selected="true"')
assert.ok($thirdTab.attr('tabindex') === '0', 'after 2nd down cursor key, third tab has tabindex="0"')
/* up cursor key */
keyDown.which = 38
$thirdTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $thirdTab[0], 'after up cursor key, third tab not focused anymore')
assert.ok(!$thirdTab.hasClass('active'), 'after up cursor key, third tab not `.active` anymore')
assert.ok($thirdTab.attr('aria-selected') === 'false', 'after up cursor key, third tab has aria-selected="false"')
assert.ok($thirdTab.attr('tabindex') === '-1', 'after up cursor key, third tab has tabindex="-1"')
assert.ok(document.activeElement === $secondTab[0], 'after up cursor key, second tab is focused')
assert.ok($secondTab.hasClass('active'), 'after up cursor key, second tab is `.active`')
assert.ok($secondTab.attr('aria-selected') === 'true', 'after up cursor key, second tab has aria-selected="true"')
assert.ok($secondTab.attr('tabindex') === '0', 'after up cursor key, second tab has tabindex="0"')
/* left cursor key */
keyDown.which = 37
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement === $secondTab[0], 'after left cursor key, second tab is still focused - no change')
/* right cursor key */
keyDown.which = 39
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement === $secondTab[0], 'after right cursor key, second tab is still focused - no change')
done()
})
QUnit.test('should not switch tabs in response to cursor keys if tab panel still transitioning', function (assert) {
assert.expect(13)
var done = assert.async()
var tabsHTML = '<ul class="nav nav-tabs" role="tablist">' +
'<li><a class="nav-item active" href="#home" data-toggle="tab" role="tab" tabindex="0" aria-selected="true">Home</a></li>' +
'<li><a class="nav-item" href="#profile" data-toggle="tab" role="tab" tabindex="-1" aria-selected="false">Profile</a></li>' +
'<li><a class="nav-item" href="#contact" data-toggle="tab" role="tab" tabindex="-1" aria-selected="false">Contact</a></li>' +
'</ul>' +
'<div class="tab-pane fade show active" id="home" role="tabpanel">...</div>' +
'<div class="tab-pane fade" id="profile" role="tabpanel">...</div>' +
'<div class="tab-pane fade" id="contact" role="tabpanel">...</div>'
var $tabs = $(tabsHTML).appendTo('#qunit-fixture')
var $firstTab = $tabs.find('li:first-child a')
var $secondTab = $tabs.find('li:nth-child(2) a')
$firstTab[0].focus() /* set focus to first tab */
assert.ok(document.activeElement === $firstTab[0], 'first tab successfully focused')
/* right cursor key */
var keyDown = new Event('keydown')
keyDown.which = 39
$firstTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement !== $firstTab[0], 'after 1st right cursor key, first tab not focused anymore')
assert.ok(!$firstTab.hasClass('active'), 'after 1st right cursor key, first tab not `.active` anymore')
assert.ok($firstTab.attr('aria-selected') === 'false', 'after 1st right cursor key, first tab has aria-selected="false"')
assert.ok($firstTab.attr('tabindex') === '-1', 'after 1st right cursor key, first tab has tabindex="-1"')
assert.ok(document.activeElement === $secondTab[0], 'after 1st right cursor key, second tab is focused')
assert.ok($secondTab.hasClass('active'), 'after 1st right cursor key, second tab is `.active`')
assert.ok($secondTab.attr('aria-selected') === 'true', 'after 1st right cursor key, second tab has aria-selected="true"')
assert.ok($secondTab.attr('tabindex') === '0', 'after 1st right cursor key, second tab has tabindex="0"')
/* right cursor key - as this is fired so quickly, the tab panel is still transitioning */
$secondTab[0].dispatchEvent(keyDown)
assert.ok(document.activeElement === $secondTab[0], 'after 2nd right cursor key, while tab panel still transitioning, second tab is still focused')
assert.ok($secondTab.hasClass('active'), 'after 2nd right cursor key, while tab panel still transitioning, second tab is still `.active`')
assert.ok($secondTab.attr('aria-selected') === 'true', 'after 2nd right cursor key, while tab panel still transitioning, second tab still has aria-selected="true"')
assert.ok($secondTab.attr('tabindex') === '0', 'after 2nd right cursor key, while tab panel still transitioning, second tab still has tabindex="0"')
done()
})
QUnit.test('should return the version', function (assert) {
assert.expect(1)
assert.strictEqual(typeof Tab.VERSION, 'string')

View file

@ -24,16 +24,9 @@
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#profile" role="tab">Profile</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown">
<a class="dropdown-item" data-toggle="tab" href="#fat" role="tab">@fat</a>
<a class="dropdown-item" data-toggle="tab" href="#mdo" role="tab">@mdo</a>
</div>
</li>
</ul>
<div class="tab-content" role="tablist">
<div class="tab-content">
<div class="tab-pane active" id="home" role="tabpanel">
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
@ -42,14 +35,6 @@
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
</div>
<div class="tab-pane" id="fat" role="tabpanel">
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
</div>
<div class="tab-pane" id="mdo" role="tabpanel">
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
</div>
</div>
<h4>Tabs with fade</h4>
@ -61,16 +46,9 @@
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#profile2" role="tab">Profile</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown2" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown2">
<a class="dropdown-item" data-toggle="tab" href="#fat2" role="tab">@fat</a>
<a class="dropdown-item" data-toggle="tab" href="#mdo2" role="tab">@mdo</a>
</div>
</li>
</ul>
<div class="tab-content" role="tablist">
<div class="tab-content">
<div class="tab-pane fade show active" id="home2" role="tabpanel">
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
@ -79,14 +57,6 @@
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
</div>
<div class="tab-pane fade" id="fat2" role="tabpanel">
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
</div>
<div class="tab-pane fade" id="mdo2" role="tabpanel">
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
</div>
</div>
<h4>Tabs without fade (no initially active pane)</h4>
@ -98,16 +68,9 @@
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#profile3" role="tab">Profile</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown3" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown3">
<a class="dropdown-item" data-toggle="tab" href="#fat3" role="tab">@fat</a>
<a class="dropdown-item" data-toggle="tab" href="#mdo3" role="tab">@mdo</a>
</div>
</li>
</ul>
<div class="tab-content" role="tablist">
<div class="tab-content">
<div class="tab-pane" id="home3" role="tabpanel">
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
@ -116,14 +79,6 @@
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
</div>
<div class="tab-pane" id="fat3" role="tabpanel">
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
</div>
<div class="tab-pane" id="mdo3" role="tabpanel">
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
</div>
</div>
<h4>Tabs with fade (no initially active pane)</h4>
@ -135,13 +90,6 @@
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#profile4" role="tab">Profile</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown4" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown4">
<a class="dropdown-item" data-toggle="tab" href="#fat4" role="tab">@fat</a>
<a class="dropdown-item" data-toggle="tab" href="#mdo4" role="tab">@mdo</a>
</div>
</li>
</ul>
<div class="tab-content">
@ -153,53 +101,30 @@
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
<p>Food truck fixie locavore, accusamus mcsweeney's marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee. Qui photo booth letterpress, commodo enim craft beer mlkshk aliquip jean shorts ullamco ad vinyl cillum PBR. Homo nostrud organic, assumenda labore aesthetic magna delectus mollit. Keytar helvetica VHS salvia yr, vero magna velit sapiente labore stumptown. Vegan fanny pack odio cillum wes anderson 8-bit, sustainable jean shorts beard ut DIY ethical culpa terry richardson biodiesel. Art party scenester stumptown, tumblr butcher vero sint qui sapiente accusamus tattooed echo park.</p>
</div>
<div class="tab-pane fade" id="fat4" role="tabpanel">
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
</div>
<div class="tab-pane fade" id="mdo4" role="tabpanel">
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
</div>
</div>
<h4>Tabs with nav (with fade)</h4>
<nav class="nav nav-pills">
<a class="nav-link nav-item active" data-toggle="tab" href="#home5">Home</a>
<a class="nav-link nav-item" data-toggle="tab" href="#profile5">Profile</a>
<div class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="dropdown5" data-toggle="dropdown" aria-expanded="false">Dropdown</a>
<div class="dropdown-menu" aria-labelledby="dropdown5">
<a class="dropdown-item" data-toggle="tab" href="#fat5">@fat</a>
<a class="dropdown-item" data-toggle="tab" href="#mdo5">@mdo</a>
</div>
</div>
<a class="nav-link nav-item disabled" href="#">Disabled</a>
</nav>
<div class="nav nav-pills" role="tablist">
<a class="nav-link nav-item active" data-toggle="tab" role="tab" href="#home5">Home</a>
<a class="nav-link nav-item" data-toggle="tab" role="tab" href="#profile5">Profile</a>
<a class="nav-link nav-item disabled" data-toggle="tab" role="tab" href="#">Disabled</a>
</div>
<div class="tab-content" role="tabpanel">
<div role="tabpanel" class="tab-pane fade show active" id="home5">
<div class="tab-content">
<div class="tab-pane fade show active" id="home5" role="tabpanel">
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
</div>
<div role="tabpanel" class="tab-pane fade" id="profile5">
<div class="tab-pane fade" id="profile5" role="tabpanel">
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
<p>Raw denim you probably haven't heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica. Reprehenderit butcher retro keffiyeh dreamcatcher synth. Cosby sweater eu banh mi, qui irure terry richardson ex squid. Aliquip placeat salvia cillum iphone. Seitan aliquip quis cardigan american apparel, butcher voluptate nisi qui.</p>
</div>
<div class="tab-pane fade" id="fat5" role="tabpanel">
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
<p>Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney's organic lomo retro fanny pack lo-fi farm-to-table readymade. Messenger bag gentrify pitchfork tattooed craft beer, iphone skateboard locavore carles etsy salvia banksy hoodie helvetica. DIY synth PBR banksy irony. Leggings gentrify squid 8-bit cred pitchfork. Williamsburg banh mi whatever gluten-free, carles pitchfork biodiesel fixie etsy retro mlkshk vice blog. Scenester cred you probably haven't heard of them, vinyl craft beer blog stumptown. Pitchfork sustainable tofu synth chambray yr.</p>
</div>
<div class="tab-pane fade" id="mdo5" role="tabpanel">
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
<p>Trust fund seitan letterpress, keytar raw denim keffiyeh etsy art party before they sold out master cleanse gluten-free squid scenester freegan cosby sweater. Fanny pack portland seitan DIY, art party locavore wolf cliche high life echo park Austin. Cred vinyl keffiyeh DIY salvia PBR, banh mi before they sold out farm-to-table VHS viral locavore cosby sweater. Lomo wolf viral, mustache readymade thundercats keffiyeh craft beer marfa ethical. Wolf salvia freegan, sartorial keffiyeh echo park vegan.</p>
</div>
</div>
<h4>Tabs with list-group (with fade)</h4>
<h4>Tabs with list-group (vertical, with fade)</h4>
<div class="row">
<div class="col-4">
<div class="list-group" id="list-tab" role="tablist">
<div class="list-group" id="list-tab" role="tablist" aria-orientation="vertical">
<a class="list-group-item list-group-item-action active" id="list-home-list" data-toggle="tab" href="#list-home" role="tab" aria-controls="list-home">Home</a>
<a class="list-group-item list-group-item-action" id="list-profile-list" data-toggle="tab" href="#list-profile" role="tab" aria-controls="list-profile">Profile</a>
<a class="list-group-item list-group-item-action" id="list-messages-list" data-toggle="tab" href="#list-messages" role="tab" aria-controls="list-messages">Messages</a>

View file

@ -205,7 +205,7 @@ Use the tab JavaScript plugin—include it individually or through the compiled
<div class="bd-example" role="tabpanel">
<div class="row">
<div class="col-4">
<div class="list-group" id="list-tab" role="tablist">
<div class="list-group" id="list-tab" role="tablist" aria-orientation="vertical">
<a class="list-group-item list-group-item-action active" id="list-home-list" data-toggle="tab" href="#list-home" role="tab" aria-controls="list-home">Home</a>
<a class="list-group-item list-group-item-action" id="list-profile-list" data-toggle="tab" href="#list-profile" role="tab" aria-controls="list-profile">Profile</a>
<a class="list-group-item list-group-item-action" id="list-messages-list" data-toggle="tab" href="#list-messages" role="tab" aria-controls="list-messages">Messages</a>
@ -234,7 +234,7 @@ Use the tab JavaScript plugin—include it individually or through the compiled
{{< highlight html >}}
<div class="row">
<div class="col-4">
<div class="list-group" id="list-tab" role="tablist">
<div class="list-group" id="list-tab" role="tablist" aria-orientation="vertical">
<a class="list-group-item list-group-item-action active" id="list-home-list" data-toggle="list" href="#list-home" role="tab" aria-controls="home">Home</a>
<a class="list-group-item list-group-item-action" id="list-profile-list" data-toggle="list" href="#list-profile" role="tab" aria-controls="profile">Profile</a>
<a class="list-group-item list-group-item-action" id="list-messages-list" data-toggle="list" href="#list-messages" role="tab" aria-controls="messages">Messages</a>
@ -259,7 +259,7 @@ You can activate a list group navigation without writing any JavaScript by simpl
<div role="tabpanel">
{{< highlight html >}}
<!-- List group -->
<div class="list-group" id="myList" role="tablist">
<div class="list-group" id="myList" role="tablist" aria-orientation="vertical">
<a class="list-group-item list-group-item-action active" data-toggle="list" href="#home" role="tab">Home</a>
<a class="list-group-item list-group-item-action" data-toggle="list" href="#profile" role="tab">Profile</a>
<a class="list-group-item list-group-item-action" data-toggle="list" href="#messages" role="tab">Messages</a>
@ -316,7 +316,7 @@ To make tabs panel fade in, add `.fade` to each `.tab-pane`. The first tab pane
Activates a list item element and content container. Tab should have either a `data-target` or an `href` targeting a container node in the DOM.
{{< highlight html >}}
<div class="list-group" id="myList" role="tablist">
<div class="list-group" id="myList" role="tablist" aria-orientation="vertical">
<a class="list-group-item list-group-item-action active" data-toggle="list" href="#home" role="tab">Home</a>
<a class="list-group-item list-group-item-action" data-toggle="list" href="#profile" role="tab">Profile</a>
<a class="list-group-item list-group-item-action" data-toggle="list" href="#messages" role="tab">Messages</a>

View file

@ -307,17 +307,19 @@ Use the tab JavaScript plugin—include it individually or through the compiled
Dynamic tabbed interfaces, as described in the [<abbr title="Web Accessibility Initiative">WAI</abbr> <abbr title="Accessible Rich Internet Applications">ARIA</abbr> Authoring Practices](https://www.w3.org/TR/wai-aria-practices/#tabpanel), require `role="tablist"`, `role="tab"`, `role="tabpanel"`, and additional `aria-` attributes in order to convey their structure, functionality and current state to users of assistive technologies (such as screen readers).
Note that dynamic tabbed interfaces should <em>not</em> contain dropdown menus, as this causes both usability and accessibility issues. From a usability perspective, the fact that the currently displayed tab's trigger element is not immediately visible (as it's inside the closed dropdown menu) can cause confusion. From an accessibility point of view, there is currently no sensible way to map this sort of construct to a standard WAI ARIA pattern, meaning that it cannot be easily made understandable to users of assistive technologies.
{{< callout warning >}}
Dynamic tabbed interfaces <em>can't</em> contain dropdown menus, as this causes both usability and accessibility issues. From a usability perspective, the fact that the currently displayed tab's trigger element is not immediately visible (as it's inside the closed dropdown menu) can cause confusion. From an accessibility point of view, there is currently no sensible way to map this sort of construct to a standard WAI ARIA pattern, meaning that it cannot be easily made understandable to users of assistive technologies.
{{< /callout >}}
<div class="bd-example">
<ul class="nav nav-tabs mb-3" id="myTab" role="tablist">
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile" aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#contact" role="tab" aria-controls="contact" aria-selected="false">Contact</a>
</li>
</ul>
@ -336,13 +338,13 @@ Note that dynamic tabbed interfaces should <em>not</em> contain dropdown menus,
{{< highlight html >}}
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile" aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#contact" role="tab" aria-controls="contact" aria-selected="false">Contact</a>
</li>
</ul>
@ -395,13 +397,13 @@ The tabs plugin also works with pills.
<div class="bd-example">
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="pills-home-tab" data-toggle="pill" href="#pills-home" role="tab" aria-controls="pills-home" aria-selected="true">Home</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="pills-profile-tab" data-toggle="pill" href="#pills-profile" role="tab" aria-controls="pills-profile" aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="pills-contact-tab" data-toggle="pill" href="#pills-contact" role="tab" aria-controls="pills-contact" aria-selected="false">Contact</a>
</li>
</ul>
@ -420,13 +422,13 @@ The tabs plugin also works with pills.
{{< highlight html >}}
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="pills-home-tab" data-toggle="pill" href="#pills-home" role="tab" aria-controls="pills-home" aria-selected="true">Home</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="pills-profile-tab" data-toggle="pill" href="#pills-profile" role="tab" aria-controls="pills-profile" aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="pills-contact-tab" data-toggle="pill" href="#pills-contact" role="tab" aria-controls="pills-contact" aria-selected="false">Contact</a>
</li>
</ul>
@ -437,7 +439,7 @@ The tabs plugin also works with pills.
</div>
{{< /highlight >}}
And with vertical pills.
When making vertical tab panels, make sure to include `aria-orientation="vertical"` to switch to the appropriate keyboard behavior (switching tabs with the up and down cursor keys).
<div class="bd-example">
<div class="row">
@ -496,16 +498,16 @@ You can activate a tab or pill navigation without writing any JavaScript by simp
{{< highlight html >}}
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile" aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="messages-tab" data-toggle="tab" href="#messages" role="tab" aria-controls="messages" aria-selected="false">Messages</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="settings-tab" data-toggle="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false">Settings</a>
</li>
</ul>
@ -564,16 +566,16 @@ Activates a tab element and content container. Tab should have either a `data-ta
{{< highlight html >}}
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile" aria-selected="false">Profile</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="messages-tab" data-toggle="tab" href="#messages" role="tab" aria-controls="messages" aria-selected="false">Messages</a>
</li>
<li class="nav-item">
<li class="nav-item" role="presentation">
<a class="nav-link" id="settings-tab" data-toggle="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="false">Settings</a>
</li>
</ul>