mirror of
https://github.com/twbs/bootstrap.git
synced 2025-08-08 06:36:33 +02:00
Support Home
and End
keys in tabs (#38498)
* Support `Home` and `End` keys in tabs * Update tab.js * simplify tests * Update navs-tabs.md * Update .bundlewatch.config.json --------- Co-authored-by: Patrick H. Lauke <redux@splintered.co.uk> Co-authored-by: XhmikosR <xhmikosr@gmail.com> Co-authored-by: Mark Otto <markdotto@gmail.com>
This commit is contained in:
@@ -30,6 +30,8 @@ const ARROW_LEFT_KEY = 'ArrowLeft'
|
||||
const ARROW_RIGHT_KEY = 'ArrowRight'
|
||||
const ARROW_UP_KEY = 'ArrowUp'
|
||||
const ARROW_DOWN_KEY = 'ArrowDown'
|
||||
const HOME_KEY = 'Home'
|
||||
const END_KEY = 'End'
|
||||
|
||||
const CLASS_NAME_ACTIVE = 'active'
|
||||
const CLASS_NAME_FADE = 'fade'
|
||||
@@ -151,14 +153,22 @@ class Tab extends BaseComponent {
|
||||
}
|
||||
|
||||
_keydown(event) {
|
||||
if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key))) {
|
||||
if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {
|
||||
return
|
||||
}
|
||||
|
||||
event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page
|
||||
event.preventDefault()
|
||||
const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
|
||||
const nextActiveElement = getNextActiveElement(this._getChildren().filter(element => !isDisabled(element)), event.target, isNext, true)
|
||||
|
||||
const children = this._getChildren().filter(element => !isDisabled(element))
|
||||
let nextActiveElement
|
||||
|
||||
if ([HOME_KEY, END_KEY].includes(event.key)) {
|
||||
nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]
|
||||
} else {
|
||||
const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
|
||||
nextActiveElement = getNextActiveElement(children, event.target, isNext, true)
|
||||
}
|
||||
|
||||
if (nextActiveElement) {
|
||||
nextActiveElement.focus({ preventScroll: true })
|
||||
|
@@ -630,6 +630,58 @@ describe('Tab', () => {
|
||||
expect(spyPrevent).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('if keydown event is Home, handle it', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="nav">',
|
||||
' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const tabEl1 = fixtureEl.querySelector('#tab1')
|
||||
const tabEl3 = fixtureEl.querySelector('#tab3')
|
||||
|
||||
const tab3 = new Tab(tabEl3)
|
||||
tab3.show()
|
||||
|
||||
const spyShown = jasmine.createSpy()
|
||||
tabEl1.addEventListener('shown.bs.tab', spyShown)
|
||||
|
||||
const keydown = createEvent('keydown')
|
||||
keydown.key = 'Home'
|
||||
|
||||
tabEl3.dispatchEvent(keydown)
|
||||
|
||||
expect(spyShown).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('if keydown event is End, handle it', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="nav">',
|
||||
' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const tabEl1 = fixtureEl.querySelector('#tab1')
|
||||
const tabEl3 = fixtureEl.querySelector('#tab3')
|
||||
|
||||
const tab1 = new Tab(tabEl1)
|
||||
tab1.show()
|
||||
|
||||
const spyShown = jasmine.createSpy()
|
||||
tabEl3.addEventListener('shown.bs.tab', spyShown)
|
||||
|
||||
const keydown = createEvent('keydown')
|
||||
keydown.key = 'End'
|
||||
|
||||
tabEl1.dispatchEvent(keydown)
|
||||
|
||||
expect(spyShown).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('if keydown event is right arrow and next element is disabled', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="nav">',
|
||||
@@ -711,6 +763,66 @@ describe('Tab', () => {
|
||||
expect(spyFocus2).not.toHaveBeenCalled()
|
||||
expect(spyFocus1).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('if keydown event is Home and first element is disabled', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="nav">',
|
||||
' <span id="tab1" class="nav-link disabled" data-bs-toggle="tab" disabled></span>',
|
||||
' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const tabEl1 = fixtureEl.querySelector('#tab1')
|
||||
const tabEl2 = fixtureEl.querySelector('#tab2')
|
||||
const tabEl3 = fixtureEl.querySelector('#tab3')
|
||||
const tab3 = new Tab(tabEl3)
|
||||
|
||||
tab3.show()
|
||||
|
||||
const spyShown1 = jasmine.createSpy()
|
||||
const spyShown2 = jasmine.createSpy()
|
||||
tabEl1.addEventListener('shown.bs.tab', spyShown1)
|
||||
tabEl2.addEventListener('shown.bs.tab', spyShown2)
|
||||
|
||||
const keydown = createEvent('keydown')
|
||||
keydown.key = 'Home'
|
||||
|
||||
tabEl3.dispatchEvent(keydown)
|
||||
|
||||
expect(spyShown1).not.toHaveBeenCalled()
|
||||
expect(spyShown2).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('if keydown event is End and last element is disabled', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="nav">',
|
||||
' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
|
||||
' <span id="tab3" class="nav-link" data-bs-toggle="tab" disabled></span>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const tabEl1 = fixtureEl.querySelector('#tab1')
|
||||
const tabEl2 = fixtureEl.querySelector('#tab2')
|
||||
const tabEl3 = fixtureEl.querySelector('#tab3')
|
||||
const tab1 = new Tab(tabEl1)
|
||||
|
||||
tab1.show()
|
||||
|
||||
const spyShown2 = jasmine.createSpy()
|
||||
const spyShown3 = jasmine.createSpy()
|
||||
tabEl2.addEventListener('shown.bs.tab', spyShown2)
|
||||
tabEl3.addEventListener('shown.bs.tab', spyShown3)
|
||||
|
||||
const keydown = createEvent('keydown')
|
||||
keydown.key = 'End'
|
||||
|
||||
tabEl1.dispatchEvent(keydown)
|
||||
|
||||
expect(spyShown3).not.toHaveBeenCalled()
|
||||
expect(spyShown2).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('jQueryInterface', () => {
|
||||
|
Reference in New Issue
Block a user