diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 69b99e628c..3667fa1113 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -468,6 +468,12 @@ class Dropdown extends BaseComponent {
return
}
+ if (!isActive && (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY)) {
+ const button = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
+ button.click()
+ return
+ }
+
if (!isActive || event.key === SPACE_KEY) {
Dropdown.clearMenus()
return
diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js
index bb137efed8..47775678f8 100644
--- a/js/tests/unit/dropdown.spec.js
+++ b/js/tests/unit/dropdown.spec.js
@@ -1626,4 +1626,52 @@ describe('Dropdown', () => {
expect(Dropdown.getInstance(div)).toEqual(null)
})
})
+
+ it('should open dropdown when pressing keydown or keyup', done => {
+ fixtureEl.innerHTML = [
+ '
',
+ ' ',
+ ' ',
+ '
'
+ ].join('')
+
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = fixtureEl.querySelector('.dropdown')
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
+
+ const keyup = createEvent('keyup')
+ keyup.key = 'ArrowUp'
+
+ const handleArrowDown = () => {
+ expect(triggerDropdown.classList.contains('show')).toEqual(true)
+ expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
+ setTimeout(() => {
+ dropdown.hide()
+ keydown.key = 'ArrowUp'
+ triggerDropdown.dispatchEvent(keyup)
+ }, 20)
+ }
+
+ const handleArrowUp = () => {
+ expect(triggerDropdown.classList.contains('show')).toEqual(true)
+ expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
+ done()
+ }
+
+ dropdown.addEventListener('shown.bs.dropdown', event => {
+ if (event.target.key === 'ArrowDown') {
+ handleArrowDown()
+ } else {
+ handleArrowUp()
+ }
+ })
+
+ triggerDropdown.dispatchEvent(keydown)
+ })
})