]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Dropdown: close menu when focusing outside element (#21375)
authorPierre-Denis Vanduynslager <pierre.denis.vanduynslager@gmail.com>
Mon, 2 Jan 2017 22:44:27 +0000 (17:44 -0500)
committerMark Otto <markd.otto@gmail.com>
Mon, 2 Jan 2017 22:44:27 +0000 (14:44 -0800)
* Close dropdown menu when focusing an outside element
* Update unit test to new markup

js/src/dropdown.js
js/tests/unit/dropdown.js

index 97bba1c76399f78f743097ed9237232558b839e3..29c4efe047cc1d32e8e67952a6b273b1f8d907d5 100644 (file)
@@ -35,6 +35,7 @@ const Dropdown = (($) => {
     SHOWN            : `shown${EVENT_KEY}`,
     CLICK            : `click${EVENT_KEY}`,
     CLICK_DATA_API   : `click${EVENT_KEY}${DATA_API_KEY}`,
+    FOCUSIN_DATA_API : `focusin${EVENT_KEY}${DATA_API_KEY}`,
     KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`
   }
 
@@ -180,9 +181,9 @@ const Dropdown = (($) => {
           continue
         }
 
-        if (event && event.type === 'click' &&
-           /input|textarea/i.test(event.target.tagName) &&
-           $.contains(parent, event.target)) {
+        if (event && (event.type === 'click' &&
+            /input|textarea/i.test(event.target.tagName) || event.type === 'focusin')
+            && $.contains(parent, event.target)) {
           continue
         }
 
@@ -275,7 +276,7 @@ const Dropdown = (($) => {
     .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE,  Dropdown._dataApiKeydownHandler)
     .on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU,    Dropdown._dataApiKeydownHandler)
     .on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler)
-    .on(Event.CLICK_DATA_API, Dropdown._clearMenus)
+    .on(`${Event.CLICK_DATA_API} ${Event.FOCUSIN_DATA_API}`, Dropdown._clearMenus)
     .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle)
     .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
       e.stopPropagation()
index 53455c2a62df357f7aeb920f42f03088c13850fe..9a34fc0c93375243c5ab8077290f028c21e5f681 100644 (file)
@@ -192,6 +192,30 @@ $(function () {
     assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class removed')
   })
 
+  QUnit.test('should remove "show" class if body is focused', function (assert) {
+    assert.expect(2)
+    var dropdownHTML = '<div class="tabs">'
+        + '<div class="dropdown">'
+        + '<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>'
+        + '<div class="dropdown-menu">'
+        + '<a class="dropdown-item" href="#">Secondary link</a>'
+        + '<a class="dropdown-item" href="#">Something else here</a>'
+        + '<div class="dropdown-divider"/>'
+        + '<a class="dropdown-item" href="#">Another link</a>'
+        + '</div>'
+        + '</div>'
+        + '</div>'
+    var $dropdown = $(dropdownHTML)
+       .appendTo('#qunit-fixture')
+       .find('[data-toggle="dropdown"]')
+       .bootstrapDropdown()
+       .trigger('click')
+
+    assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click')
+    $(document.body).trigger('focusin')
+    assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class removed')
+  })
+
   QUnit.test('should remove "show" class if body is clicked, with multiple dropdowns', function (assert) {
     assert.expect(7)
     var dropdownHTML = '<ul class="nav">'
@@ -229,6 +253,42 @@ $(function () {
     assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed')
   })
 
+  QUnit.test('should remove "show" class if body is focused, with multiple dropdowns', function (assert) {
+    assert.expect(7)
+    var dropdownHTML = '<div class="nav">'
+        + '<div class="dropdown" id="testmenu">'
+        + '<a class="dropdown-toggle" data-toggle="dropdown" href="#testmenu">Test menu <span class="caret"/></a>'
+        + '<div class="dropdown-menu">'
+        + '<a class="dropdown-item" href="#sub1">Submenu 1</a>'
+        + '</div>'
+        + '</div>'
+        + '</div>'
+        + '<div class="btn-group">'
+        + '<button class="btn">Actions</button>'
+        + '<button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"/></button>'
+        + '<div class="dropdown-menu">'
+        + '<a class="dropdown-item" href="#">Action 1</a>'
+        + '</div>'
+        + '</div>'
+    var $dropdowns = $(dropdownHTML).appendTo('#qunit-fixture').find('[data-toggle="dropdown"]')
+    var $first = $dropdowns.first()
+    var $last = $dropdowns.last()
+
+    assert.strictEqual($dropdowns.length, 2, 'two dropdowns')
+
+    $first.trigger('click')
+    assert.strictEqual($first.parents('.show').length, 1, '"show" class added on click')
+    assert.strictEqual($('#qunit-fixture .show').length, 1, 'only one dropdown is show')
+    $(document.body).trigger('focusin')
+    assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed')
+
+    $last.trigger('click')
+    assert.strictEqual($last.parent('.show').length, 1, '"show" class added on click')
+    assert.strictEqual($('#qunit-fixture .show').length, 1, 'only one dropdown is show')
+    $(document.body).trigger('focusin')
+    assert.strictEqual($('#qunit-fixture .show').length, 0, '"show" class removed')
+  })
+
   QUnit.test('should fire show and hide event', function (assert) {
     assert.expect(2)
     var dropdownHTML = '<ul class="tabs">'