}
}
+ static selectMenuItem(parent, event) {
+ const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, parent).filter(isVisible)
+
+ if (!items.length) {
+ return
+ }
+
+ let index = items.indexOf(event.target)
+
+ // Up
+ if (event.key === ARROW_UP_KEY && index > 0) {
+ index--
+ }
+
+ // Down
+ if (event.key === ARROW_DOWN_KEY && index < items.length - 1) {
+ index++
+ }
+
+ // index is -1 if the first keydown is an ArrowUp
+ index = index === -1 ? 0 : index
+
+ items[index].focus()
+ }
+
static getParentFromElement(element) {
return getElementFromSelector(element) || element.parentNode
}
return
}
+ const isActive = this.classList.contains(CLASS_NAME_SHOW)
+
+ if (!isActive && event.key === ESCAPE_KEY) {
+ return
+ }
+
event.preventDefault()
event.stopPropagation()
return
}
- const parent = Dropdown.getParentFromElement(this)
- const isActive = this.classList.contains(CLASS_NAME_SHOW)
+ const getToggleButton = () => this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
if (event.key === ESCAPE_KEY) {
- const button = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
- button.focus()
+ getToggleButton().focus()
Dropdown.clearMenus()
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()
+ getToggleButton().click()
return
}
return
}
- const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, parent).filter(isVisible)
-
- if (!items.length) {
- return
- }
-
- let index = items.indexOf(event.target)
-
- // Up
- if (event.key === ARROW_UP_KEY && index > 0) {
- index--
- }
-
- // Down
- if (event.key === ARROW_DOWN_KEY && index < items.length - 1) {
- index++
- }
-
- // index is -1 if the first keydown is an ArrowUp
- index = index === -1 ? 0 : index
-
- items[index].focus()
+ Dropdown.selectMenuItem(Dropdown.getParentFromElement(this), event)
}
}
done()
}, 20)
})
+
+ it('should propagate escape key events if dropdown is closed', done => {
+ fixtureEl.innerHTML = [
+ '<div class="parent">',
+ ' <div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Some Item</a>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ]
+
+ const parent = fixtureEl.querySelector('.parent')
+ const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+
+ const parentKeyHandler = jasmine.createSpy('parentKeyHandler')
+
+ parent.addEventListener('keydown', parentKeyHandler)
+ parent.addEventListener('keyup', () => {
+ expect(parentKeyHandler).toHaveBeenCalled()
+ done()
+ })
+
+ const keydownEscape = createEvent('keydown', { bubbles: true })
+ keydownEscape.key = 'Escape'
+ const keyupEscape = createEvent('keyup', { bubbles: true })
+ keyupEscape.key = 'Escape'
+
+ toggle.focus()
+ toggle.dispatchEvent(keydownEscape)
+ toggle.dispatchEvent(keyupEscape)
+ })
})
describe('jQueryInterface', () => {