0
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-01-18 10:52:19 +01:00

Dropdown — Emit events on the .dropdown-toggle button (#32625)

* Emit events on the dropdown button

Emit the events on `.dropdown-toggle` button and then bubble up

* Add migration note for events

* Update the docs for events

* Add unit test to check the event bubbling

Co-authored-by: XhmikosR <xhmikosr@gmail.com>
This commit is contained in:
Rohit Sharma 2021-02-04 01:21:19 +05:30 committed by GitHub
parent 02d103be91
commit 3770b7b9e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 112 deletions

View File

@ -194,7 +194,7 @@ class Dropdown extends BaseComponent {
this._menu.classList.toggle(CLASS_NAME_SHOW)
this._element.classList.toggle(CLASS_NAME_SHOW)
EventHandler.trigger(parent, EVENT_SHOWN, relatedTarget)
EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)
}
hide() {
@ -202,12 +202,11 @@ class Dropdown extends BaseComponent {
return
}
const parent = Dropdown.getParentFromElement(this._element)
const relatedTarget = {
relatedTarget: this._element
}
const hideEvent = EventHandler.trigger(parent, EVENT_HIDE, relatedTarget)
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)
if (hideEvent.defaultPrevented) {
return
@ -219,7 +218,7 @@ class Dropdown extends BaseComponent {
this._menu.classList.toggle(CLASS_NAME_SHOW)
this._element.classList.toggle(CLASS_NAME_SHOW)
EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)
}
dispose() {
@ -383,7 +382,6 @@ class Dropdown extends BaseComponent {
const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
for (let i = 0, len = toggles.length; i < len; i++) {
const parent = Dropdown.getParentFromElement(toggles[i])
const context = Data.getData(toggles[i], DATA_KEY)
const relatedTarget = {
relatedTarget: toggles[i]
@ -409,7 +407,7 @@ class Dropdown extends BaseComponent {
continue
}
const hideEvent = EventHandler.trigger(parent, EVENT_HIDE, relatedTarget)
const hideEvent = EventHandler.trigger(toggles[i], EVENT_HIDE, relatedTarget)
if (hideEvent.defaultPrevented) {
continue
}
@ -429,7 +427,7 @@ class Dropdown extends BaseComponent {
dropdownMenu.classList.remove(CLASS_NAME_SHOW)
toggles[i].classList.remove(CLASS_NAME_SHOW)
EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
EventHandler.trigger(toggles[i], EVENT_HIDDEN, relatedTarget)
}
}

View File

@ -137,10 +137,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
@ -198,14 +197,13 @@ describe('Dropdown', () => {
const defaultValueOnTouchStart = document.documentElement.ontouchstart
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
document.documentElement.ontouchstart = () => {}
spyOn(EventHandler, 'on')
spyOn(EventHandler, 'off')
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
expect(EventHandler.on).toHaveBeenCalled()
@ -213,7 +211,7 @@ describe('Dropdown', () => {
dropdown.toggle()
})
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(false)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
expect(EventHandler.off).toHaveBeenCalled()
@ -236,10 +234,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
@ -351,12 +348,11 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown, {
reference: 'parent'
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
@ -376,12 +372,11 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown, {
reference: fixtureEl
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
@ -401,12 +396,11 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown, {
reference: { 0: fixtureEl, jquery: 'jQuery' }
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
@ -478,10 +472,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -504,10 +497,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -530,10 +522,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -556,14 +547,13 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('show.bs.dropdown', e => {
btnDropdown.addEventListener('show.bs.dropdown', e => {
e.preventDefault()
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -588,10 +578,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
done()
})
@ -610,10 +599,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -636,10 +624,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -662,10 +649,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -688,14 +674,13 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('show.bs.dropdown', e => {
btnDropdown.addEventListener('show.bs.dropdown', e => {
e.preventDefault()
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
throw new Error('should not throw shown.bs.dropdown event')
})
@ -720,11 +705,10 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
expect(dropdownMenu.classList.contains('show')).toEqual(false)
done()
})
@ -743,15 +727,14 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
spyOn(dropdown._popper, 'destroy')
dropdown.hide()
})
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
expect(dropdown._popper.destroy).toHaveBeenCalled()
done()
})
@ -770,11 +753,10 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
throw new Error('should not throw hidden.bs.dropdown event')
})
@ -797,11 +779,10 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
throw new Error('should not throw hidden.bs.dropdown event')
})
@ -824,10 +805,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
throw new Error('should not throw hidden.bs.dropdown event')
})
@ -850,15 +830,14 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('hide.bs.dropdown', e => {
btnDropdown.addEventListener('hide.bs.dropdown', e => {
e.preventDefault()
})
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
throw new Error('should not throw hidden.bs.dropdown event')
})
@ -983,15 +962,14 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
let showEventTriggered = false
let hideEventTriggered = false
dropdownEl.addEventListener('show.bs.dropdown', () => {
btnDropdown.addEventListener('show.bs.dropdown', () => {
showEventTriggered = true
})
dropdownEl.addEventListener('shown.bs.dropdown', e => {
btnDropdown.addEventListener('shown.bs.dropdown', e => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
expect(showEventTriggered).toEqual(true)
@ -999,11 +977,11 @@ describe('Dropdown', () => {
document.body.click()
})
dropdownEl.addEventListener('hide.bs.dropdown', () => {
btnDropdown.addEventListener('hide.bs.dropdown', () => {
hideEventTriggered = true
})
dropdownEl.addEventListener('hidden.bs.dropdown', e => {
btnDropdown.addEventListener('hidden.bs.dropdown', e => {
expect(btnDropdown.classList.contains('show')).toEqual(false)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
expect(hideEventTriggered).toEqual(true)
@ -1027,10 +1005,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(dropdownMenu.getAttribute('style')).toEqual(null, 'no inline style applied by Popper')
done()
})
@ -1049,10 +1026,9 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
// Popper adds this attribute when we use it
expect(dropdownMenu.getAttribute('x-placement')).toEqual(null)
done()
@ -1072,9 +1048,8 @@ describe('Dropdown', () => {
].join('')
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownEl = fixtureEl.querySelector('.dropdown')
dropdownEl.addEventListener('shown.bs.dropdown', () => {
btnDropdown.addEventListener('shown.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(true)
const keyup = createEvent('keyup')
@ -1083,7 +1058,7 @@ describe('Dropdown', () => {
document.dispatchEvent(keyup)
})
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
expect(btnDropdown.classList.contains('show')).toEqual(false)
done()
})
@ -1114,34 +1089,31 @@ describe('Dropdown', () => {
expect(triggerDropdownList.length).toEqual(2)
const first = triggerDropdownList[0]
const last = triggerDropdownList[1]
const dropdownTestMenu = first.parentNode
const btnGroup = last.parentNode
const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
dropdownTestMenu.addEventListener('shown.bs.dropdown', () => {
expect(first.classList.contains('show')).toEqual(true)
triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
expect(triggerDropdownFirst.classList.contains('show')).toEqual(true)
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
document.body.click()
})
dropdownTestMenu.addEventListener('hidden.bs.dropdown', () => {
triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0)
last.click()
triggerDropdownLast.click()
})
btnGroup.addEventListener('shown.bs.dropdown', () => {
expect(last.classList.contains('show')).toEqual(true)
triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
expect(triggerDropdownLast.classList.contains('show')).toEqual(true)
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
document.body.click()
})
btnGroup.addEventListener('hidden.bs.dropdown', () => {
triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0)
done()
})
first.click()
triggerDropdownFirst.click()
})
it('should remove "show" class if body if tabbing outside of menu, with multiple dropdowns', done => {
@ -1165,13 +1137,10 @@ describe('Dropdown', () => {
expect(triggerDropdownList.length).toEqual(2)
const first = triggerDropdownList[0]
const last = triggerDropdownList[1]
const dropdownTestMenu = first.parentNode
const btnGroup = last.parentNode
const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
dropdownTestMenu.addEventListener('shown.bs.dropdown', () => {
expect(first.classList.contains('show')).toEqual(true, '"show" class added on click')
triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
expect(triggerDropdownFirst.classList.contains('show')).toEqual(true, '"show" class added on click')
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
const keyup = createEvent('keyup')
@ -1180,13 +1149,13 @@ describe('Dropdown', () => {
document.dispatchEvent(keyup)
})
dropdownTestMenu.addEventListener('hidden.bs.dropdown', () => {
triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0, '"show" class removed')
last.click()
triggerDropdownLast.click()
})
btnGroup.addEventListener('shown.bs.dropdown', () => {
expect(last.classList.contains('show')).toEqual(true, '"show" class added on click')
triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
expect(triggerDropdownLast.classList.contains('show')).toEqual(true, '"show" class added on click')
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
const keyup = createEvent('keyup')
@ -1195,12 +1164,12 @@ describe('Dropdown', () => {
document.dispatchEvent(keyup)
})
btnGroup.addEventListener('hidden.bs.dropdown', () => {
triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0, '"show" class removed')
done()
})
first.click()
triggerDropdownFirst.click()
})
it('should fire hide and hidden event without a clickEvent if event type is not click', done => {
@ -1214,18 +1183,17 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
dropdown.addEventListener('hide.bs.dropdown', e => {
triggerDropdown.addEventListener('hide.bs.dropdown', e => {
expect(e.clickEvent).toBeUndefined()
})
dropdown.addEventListener('hidden.bs.dropdown', e => {
triggerDropdown.addEventListener('hidden.bs.dropdown', e => {
expect(e.clickEvent).toBeUndefined()
done()
})
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
const keydown = createEvent('keydown')
keydown.key = 'Escape'
@ -1235,6 +1203,42 @@ describe('Dropdown', () => {
triggerDropdown.click()
})
it('should bubble up the events to the parent elements', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#subMenu">Sub menu</a>',
' </div>',
'</div>'
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownParent = fixtureEl.querySelector('.dropdown')
const dropdown = new Dropdown(triggerDropdown)
const showFunction = jasmine.createSpy('showFunction')
dropdownParent.addEventListener('show.bs.dropdown', showFunction)
const shownFunction = jasmine.createSpy('shownFunction')
dropdownParent.addEventListener('shown.bs.dropdown', () => {
shownFunction()
dropdown.hide()
})
const hideFunction = jasmine.createSpy('hideFunction')
dropdownParent.addEventListener('hide.bs.dropdown', hideFunction)
dropdownParent.addEventListener('hidden.bs.dropdown', () => {
expect(showFunction).toHaveBeenCalled()
expect(shownFunction).toHaveBeenCalled()
expect(hideFunction).toHaveBeenCalled()
done()
})
dropdown.show()
})
it('should ignore keyboard events within <input>s and <textarea>s', done => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
@ -1248,11 +1252,10 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
const input = fixtureEl.querySelector('input')
const textarea = fixtureEl.querySelector('textarea')
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
input.focus()
const keydown = createEvent('keydown')
@ -1284,9 +1287,8 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
const keydown = createEvent('keydown')
keydown.key = 'ArrowDown'
@ -1320,9 +1322,8 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
const keydown = createEvent('keydown')
keydown.key = 'ArrowDown'
@ -1350,11 +1351,10 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
const item1 = fixtureEl.querySelector('#item1')
const item2 = fixtureEl.querySelector('#item2')
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
const keydownArrowDown = createEvent('keydown')
keydownArrowDown.key = 'ArrowDown'
@ -1388,10 +1388,9 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
const item1 = fixtureEl.querySelector('#item1')
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
const keydown = createEvent('keydown')
keydown.key = 'ArrowUp'
@ -1415,7 +1414,6 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
const input = fixtureEl.querySelector('input')
input.addEventListener('click', () => {
@ -1423,7 +1421,7 @@ describe('Dropdown', () => {
done()
})
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
input.dispatchEvent(createEvent('click'))
})
@ -1442,7 +1440,6 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
const textarea = fixtureEl.querySelector('textarea')
textarea.addEventListener('click', () => {
@ -1450,7 +1447,7 @@ describe('Dropdown', () => {
done()
})
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
textarea.dispatchEvent(createEvent('click'))
})
@ -1471,7 +1468,6 @@ describe('Dropdown', () => {
].join('')
const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = fixtureEl.querySelector('.dropdown')
const input = fixtureEl.querySelector('input')
const textarea = fixtureEl.querySelector('textarea')
@ -1487,7 +1483,7 @@ describe('Dropdown', () => {
const keydownEscape = createEvent('keydown')
keydownEscape.key = 'Escape'
dropdown.addEventListener('shown.bs.dropdown', () => {
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
// Key Space
input.focus()
input.dispatchEvent(keydownSpace)

View File

@ -968,8 +968,7 @@ Note when `boundary` is set to any value other than `'scrollParent'`, the style
### Events
All dropdown events are fired at the `.dropdown-menu`'s parent element and have a `relatedTarget` property, whose value is the toggling anchor element.
`hide.bs.dropdown` and `hidden.bs.dropdown` events have a `clickEvent` property (only when the original Event type is `click`) that contains an Event Object for the click event.
All dropdown events are fired at the toggling element and then bubbled up. So you can also add event listeners on the `.dropdown-menu`'s parent element. `hide.bs.dropdown` and `hidden.bs.dropdown` events have a `clickEvent` property (only when the original Event type is `click`) that contains an Event Object for the click event.
<table class="table">
<thead>

View File

@ -22,6 +22,7 @@ toc: true
- Restored `offset` option for Dropdown, Popover and Tooltip plugins.
- The default value for the `fallbackPlacements` is changed to `['top', 'right', 'bottom', 'left']` for better placement of popper elements.
- All the events for the dropdown are now triggered on the dropdown toggle button and then bubbled up to the parent element.
## v5.0.0-beta1