]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
fix(dropdowns): Fix multiple dropdowns when they are inside the same tag (#37011)
authorLouis-Maxime Piton <louismaxime.piton@orange.com>
Fri, 2 Sep 2022 07:52:33 +0000 (09:52 +0200)
committerGitHub <noreply@github.com>
Fri, 2 Sep 2022 07:52:33 +0000 (10:52 +0300)
.bundlewatch.config.json
js/src/dropdown.js
js/tests/unit/dropdown.spec.js

index c89a19e471d558cd982ced55100c0a8339ad8f95..41cea2119d06272cb1ff9ccc4c2a8630bef84586 100644 (file)
@@ -42,7 +42,7 @@
     },
     {
       "path": "./dist/js/bootstrap.esm.js",
-      "maxSize": "27.75 kB"
+      "maxSize": "28.0 kB"
     },
     {
       "path": "./dist/js/bootstrap.esm.min.js",
index 601792953e0340fe452bdebe8140481b8653a841..424b187ffe871209db018678a49e62764ba0f636 100644 (file)
@@ -95,7 +95,8 @@ class Dropdown extends BaseComponent {
 
     this._popper = null
     this._parent = this._element.parentNode // dropdown wrapper
-    this._menu = SelectorEngine.findOne(SELECTOR_MENU, this._parent)
+    // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/
+    this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0]
     this._inNavbar = this._detectNavbar()
   }
 
@@ -405,7 +406,8 @@ class Dropdown extends BaseComponent {
 
     event.preventDefault()
 
-    const getToggleButton = SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode)
+    // todo: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.2/forms/input-group/
+    const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0]
     const instance = Dropdown.getOrCreateInstance(getToggleButton)
 
     if (isUpOrDownEvent) {
index 56ac4ff4942428b2d2c8e58745e720f9d7823f81..ea7ddace30f85486d43ca866d09b441f2f68ad46 100644 (file)
@@ -1458,6 +1458,67 @@ describe('Dropdown', () => {
       })
     })
 
+    it('should be able to identify clicked dropdown, even with multiple dropdowns in the same tag', () => {
+      fixtureEl.innerHTML = [
+        '<div class="dropdown">',
+        '  <button id="dropdown1" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+        '  <div id="menu1" class="dropdown-menu">',
+        '    <a class="dropdown-item" href="#">Dropdown item</a>',
+        '  </div>',
+        '  <button id="dropdown2" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+        '  <div id="menu2" class="dropdown-menu">',
+        '    <a class="dropdown-item" href="#">Dropdown item</a>',
+        '  </div>',
+        '</div>'
+      ].join('')
+
+      const dropdownToggle1 = fixtureEl.querySelector('#dropdown1')
+      const dropdownToggle2 = fixtureEl.querySelector('#dropdown2')
+      const dropdownMenu1 = fixtureEl.querySelector('#menu1')
+      const dropdownMenu2 = fixtureEl.querySelector('#menu2')
+      const spy = spyOn(Dropdown, 'getOrCreateInstance').and.callThrough()
+
+      dropdownToggle1.click()
+      expect(spy).toHaveBeenCalledWith(dropdownToggle1)
+
+      dropdownToggle2.click()
+      expect(spy).toHaveBeenCalledWith(dropdownToggle2)
+
+      dropdownMenu1.click()
+      expect(spy).toHaveBeenCalledWith(dropdownToggle1)
+
+      dropdownMenu2.click()
+      expect(spy).toHaveBeenCalledWith(dropdownToggle2)
+    })
+
+    it('should be able to show the proper menu, even with multiple dropdowns in the same tag', () => {
+      fixtureEl.innerHTML = [
+        '<div class="dropdown">',
+        '  <button id="dropdown1" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+        '  <div id="menu1" class="dropdown-menu">',
+        '    <a class="dropdown-item" href="#">Dropdown item</a>',
+        '  </div>',
+        '  <button id="dropdown2" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+        '  <div id="menu2" class="dropdown-menu">',
+        '    <a class="dropdown-item" href="#">Dropdown item</a>',
+        '  </div>',
+        '</div>'
+      ].join('')
+
+      const dropdownToggle1 = fixtureEl.querySelector('#dropdown1')
+      const dropdownToggle2 = fixtureEl.querySelector('#dropdown2')
+      const dropdownMenu1 = fixtureEl.querySelector('#menu1')
+      const dropdownMenu2 = fixtureEl.querySelector('#menu2')
+
+      dropdownToggle1.click()
+      expect(dropdownMenu1).toHaveClass('show')
+      expect(dropdownMenu2).not.toHaveClass('show')
+
+      dropdownToggle2.click()
+      expect(dropdownMenu1).not.toHaveClass('show')
+      expect(dropdownMenu2).toHaveClass('show')
+    })
+
     it('should fire hide and hidden event without a clickEvent if event type is not click', () => {
       return new Promise(resolve => {
         fixtureEl.innerHTML = [