Skip to content

Commit

Permalink
Allow data-toggle="dropdown" and form click events to bubble (Backpor…
Browse files Browse the repository at this point in the history
…t of #33442)

* remove stopPropagation from button click event

* test for delegated click events

* ensure button children can open menu

* test to ensure clicking button opens the menu

* check current element and parents

* allow dropdown form click events to bubble
  • Loading branch information
GeoSot committed Oct 11, 2021
1 parent 8133c3e commit 4d9bf13
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 14 deletions.
34 changes: 20 additions & 14 deletions js/src/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ const CLASS_NAME_MENURIGHT = 'dropdown-menu-right'
const CLASS_NAME_POSITION_STATIC = 'position-static'

const SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]'
const SELECTOR_FORM_CHILD = '.dropdown form'
const SELECTOR_MENU = '.dropdown-menu'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
Expand Down Expand Up @@ -244,7 +243,6 @@ class Dropdown {
_addEventListeners() {
$(this._element).on(EVENT_CLICK, event => {
event.preventDefault()
event.stopPropagation()
this.toggle()
})
}
Expand Down Expand Up @@ -370,9 +368,14 @@ class Dropdown {
}

static _clearMenus(event) {
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
return
if (event) {
if (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE) {
return
}

if (/input|select|textarea|form/i.test(event.target.tagName)) {
return
}
}

const toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE))
Expand All @@ -397,10 +400,17 @@ class Dropdown {
continue
}

if (event && (event.type === 'click' &&
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
$.contains(parent, event.target)) {
continue
if (event) {
// todo `composedPath` not supported on IE
// Don't close the menu if the clicked element or one of its parents is the dropdown button
if ([context._element].some(element => event.composedPath().includes(element))) {
continue
}

// Tab navigation through the dropdown menu shouldn't close the menu
if (event.type === 'keyup' && event.which === TAB_KEYCODE && dropdownMenu.contains(event.target)) {
continue
}
}

const hideEvent = $.Event(EVENT_HIDE, relatedTarget)
Expand Down Expand Up @@ -515,11 +525,7 @@ $(document)
.on(`${EVENT_CLICK_DATA_API} ${EVENT_KEYUP_DATA_API}`, Dropdown._clearMenus)
.on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault()
event.stopPropagation()
Dropdown._jQueryInterface.call($(this), 'toggle')
})
.on(EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => {
e.stopPropagation()
Dropdown._jQueryInterface.call($(this))
})

/**
Expand Down
56 changes: 56 additions & 0 deletions js/tests/unit/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -1658,4 +1658,60 @@ $(function () {

$dropdown.trigger('click')
})

QUnit.test('should allow `data-bs-toggle="dropdown"` click events to bubble up', function (assert) {
assert.expect(2)
var fixtureHtml = [
'<div class="dropdown">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#">Secondary link</a>',
' </div>',
'</div>'
].join('')

$(fixtureHtml).appendTo('#qunit-fixture')
var btnDropdown = $('[data-bs-toggle="dropdown"]')
var clickListener = sinon.spy('clickListener')
var delegatedClickListener = sinon.spy('delegatedClickListener')

btnDropdown.addEventListener('click', clickListener)
document.addEventListener('click', delegatedClickListener)

btnDropdown.click()

sinon.assert.calledOnce(clickListener)
sinon.assert.calledOnce(delegatedClickListener)
})

QUnit.test('should open the dropdown when clicking the child element inside `data-bs-toggle="dropdown"`', function (assert) {
assert.expect(3)
var done = assert.async()
var fixtureHtml = [
'<div class="container">',
' <div class="dropdown">',
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"><span id="childElement">Dropdown</span></button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#subMenu">Sub menu</a>',
' </div>',
' </div>',
'</div>'
].join('')

$(fixtureHtml).appendTo('#qunit-fixture')

var btnDropdown = $('[data-bs-toggle="dropdown"]')
var childElement = $('#childElement')

btnDropdown.addEventListener('shown.bs.dropdown', function () {
setTimeout(function () {
assert.true(btnDropdown.classList.contains('show'))
assert.true(btnDropdown.getAttribute('aria-expanded'))
done()
})
})


childElement.click()
})
})

0 comments on commit 4d9bf13

Please sign in to comment.