Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bundlewatch.config.json4
-rw-r--r--js/src/dropdown.js21
-rw-r--r--js/src/util/index.js4
-rw-r--r--js/tests/unit/dropdown.spec.js41
-rw-r--r--js/tests/unit/util/index.spec.js13
5 files changed, 59 insertions, 24 deletions
diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json
index 81badf254c..988accd7f9 100644
--- a/.bundlewatch.config.json
+++ b/.bundlewatch.config.json
@@ -34,7 +34,7 @@
},
{
"path": "./dist/js/bootstrap.bundle.js",
- "maxSize": "41.25 kB"
+ "maxSize": "41.5 kB"
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
@@ -50,7 +50,7 @@
},
{
"path": "./dist/js/bootstrap.js",
- "maxSize": "27.25 kB"
+ "maxSize": "27.5 kB"
},
{
"path": "./dist/js/bootstrap.min.js",
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index cab2d018bb..34beb65129 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -354,18 +354,16 @@ class Dropdown extends BaseComponent {
}
}
- _selectMenuItem(event) {
- if (![ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {
- return
- }
-
+ _selectMenuItem({ key, target }) {
const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
if (!items.length) {
return
}
- getNextActiveElement(items, event.target, event.key === ARROW_DOWN_KEY, false).focus()
+ // if target isn't included in items (e.g. when expanding the dropdown)
+ // allow cycling to get the last item in case key equals ARROW_UP_KEY
+ getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()
}
// Static
@@ -480,17 +478,18 @@ class Dropdown extends BaseComponent {
return
}
- if (!isActive && (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY)) {
- getToggleButton().click()
+ if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
+ if (!isActive) {
+ getToggleButton().click()
+ }
+
+ Dropdown.getInstance(getToggleButton())._selectMenuItem(event)
return
}
if (!isActive || event.key === SPACE_KEY) {
Dropdown.clearMenus()
- return
}
-
- Dropdown.getInstance(getToggleButton())._selectMenuItem(event)
}
}
diff --git a/js/src/util/index.js b/js/src/util/index.js
index 77bdc072fc..4d077b21f9 100644
--- a/js/src/util/index.js
+++ b/js/src/util/index.js
@@ -264,9 +264,9 @@ const execute = callback => {
const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
let index = list.indexOf(activeElement)
- // if the element does not exist in the list initialize it as the first element
+ // if the element does not exist in the list return an element depending on the direction and if cycle is allowed
if (index === -1) {
- return list[0]
+ return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]
}
const listLength = list.length
diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js
index 5275f1a556..390cddfbfa 100644
--- a/js/tests/unit/dropdown.spec.js
+++ b/js/tests/unit/dropdown.spec.js
@@ -1561,7 +1561,7 @@ describe('Dropdown', () => {
triggerDropdown.click()
})
- it('should focus on the first element when using ArrowUp for the first time', done => {
+ it('should open the dropdown and focus on the last item when using ArrowUp for the first time', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
@@ -1573,19 +1573,44 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const item1 = fixtureEl.querySelector('#item1')
+ const lastItem = fixtureEl.querySelector('#item2')
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowUp'
+ setTimeout(() => {
+ expect(document.activeElement).toEqual(lastItem, 'item2 is focused')
+ done()
+ })
+ })
- document.activeElement.dispatchEvent(keydown)
- expect(document.activeElement).toEqual(item1, 'item1 is focused')
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowUp'
+ triggerDropdown.dispatchEvent(keydown)
+ })
- done()
+ it('should open the dropdown and focus on the first item when using ArrowDown for the first time', done => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a id="item1" class="dropdown-item" href="#">A link</a>',
+ ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const firstItem = fixtureEl.querySelector('#item1')
+
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ setTimeout(() => {
+ expect(document.activeElement).toEqual(firstItem, 'item1 is focused')
+ done()
+ })
})
- triggerDropdown.click()
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
+ triggerDropdown.dispatchEvent(keydown)
})
it('should not close the dropdown if the user clicks on a text field within dropdown-menu', done => {
diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js
index 774945d1f9..737ecacfde 100644
--- a/js/tests/unit/util/index.spec.js
+++ b/js/tests/unit/util/index.spec.js
@@ -661,11 +661,22 @@ describe('Util', () => {
})
describe('getNextActiveElement', () => {
- it('should return first element if active not exists or not given', () => {
+ it('should return first element if active not exists or not given and shouldGetNext is either true, or false with cycling being disabled', () => {
const array = ['a', 'b', 'c', 'd']
expect(Util.getNextActiveElement(array, '', true, true)).toEqual('a')
expect(Util.getNextActiveElement(array, 'g', true, true)).toEqual('a')
+ expect(Util.getNextActiveElement(array, '', true, false)).toEqual('a')
+ expect(Util.getNextActiveElement(array, 'g', true, false)).toEqual('a')
+ expect(Util.getNextActiveElement(array, '', false, false)).toEqual('a')
+ expect(Util.getNextActiveElement(array, 'g', false, false)).toEqual('a')
+ })
+
+ it('should return last element if active not exists or not given and shouldGetNext is false but cycling is enabled', () => {
+ const array = ['a', 'b', 'c', 'd']
+
+ expect(Util.getNextActiveElement(array, '', false, true)).toEqual('d')
+ expect(Util.getNextActiveElement(array, 'g', false, true)).toEqual('d')
})
it('should return next element or same if is last', () => {