]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Dropdown handle keydown on input and textarea.
authorPierre-Denis Vanduynslager <pierre.denis.vanduynslager@gmail.com>
Sat, 21 Jan 2017 05:17:34 +0000 (07:17 +0200)
committerXhmikosR <xhmikosr@gmail.com>
Fri, 27 Oct 2017 16:19:55 +0000 (19:19 +0300)
js/src/dropdown.js
js/tests/unit/dropdown.js

index e3331ac183f6c3e191ecc31695601c171c3d6c45..85899d6fcd55efe7fafe08067cb9227e0cfb8f25 100644 (file)
@@ -62,6 +62,11 @@ const Dropdown = (($) => {
   const Selector = {
     DATA_TOGGLE   : '[data-toggle="dropdown"]',
     FORM_CHILD    : '.dropdown form',
+<<<<<<< HEAD
+=======
+    ROLE_MENU     : '[role="menu"]',
+    ROLE_LISTBOX  : '[role="listbox"]',
+>>>>>>> Dropdown handle keydown on input and textarea
     MENU          : '.dropdown-menu',
     NAVBAR_NAV    : '.navbar-nav',
     VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)'
@@ -357,8 +362,17 @@ const Dropdown = (($) => {
     }
 
     static _dataApiKeydownHandler(event) {
-      if (!REGEXP_KEYDOWN.test(event.which) || /button/i.test(event.target.tagName) && event.which === SPACE_KEYCODE ||
-         /input|textarea/i.test(event.target.tagName)) {
+      // If not input/textarea:
+      //  - And not a key in REGEXP_KEYDOWN => not a dropdown command
+      // If input/textarea:
+      //  - If space key => not a dropdown command
+      //  - If key is other than excape
+      //    - If key is not up or down => not a dropdown command
+      //    - If trigger inside the menu => not a dropdown command
+      if (/input|textarea/i.test(event.target.tagName) ?
+        event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
+        (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
+          $(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
         return
       }
 
@@ -418,6 +432,7 @@ const Dropdown = (($) => {
 
   $(document)
     .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE,  Dropdown._dataApiKeydownHandler)
+<<<<<<< HEAD
     .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler)
     .on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus)
     .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
@@ -425,6 +440,13 @@ const Dropdown = (($) => {
       event.stopPropagation()
       Dropdown._jQueryInterface.call($(this), 'toggle')
     })
+=======
+    .on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU,    Dropdown._dataApiKeydownHandler)
+    .on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler)
+    .on(Event.KEYDOWN_DATA_API, Selector.MENU,    Dropdown._dataApiKeydownHandler)
+    .on(`${Event.CLICK_DATA_API} ${Event.FOCUSIN_DATA_API}`, Dropdown._clearMenus)
+    .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle)
+>>>>>>> Dropdown handle keydown on input and textarea
     .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => {
       e.stopPropagation()
     })
index 0b808cc4877a267a92144d0b0466b105361bdccf..d401db0b5e70b0ceb651fe95cd104e6cc9b3df01 100644 (file)
@@ -457,8 +457,8 @@ $(function () {
     $dropdown.trigger('click')
   })
 
-  QUnit.test('should ignore keyboard events within <input>s and <textarea>s', function (assert) {
-    assert.expect(3)
+  QUnit.test('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', function (assert) {
+    assert.expect(8)
     var done = assert.async()
 
     var dropdownHTML = '<div class="tabs">'
@@ -487,11 +487,27 @@ $(function () {
       .on('shown.bs.dropdown', function () {
         assert.ok(true, 'shown was fired')
 
-        $input.trigger('focus').trigger($.Event('keydown', { which: 38 }))
-        assert.ok($(document.activeElement).is($input), 'input still focused')
+        // Space key
+        $input.trigger('focus').trigger($.Event('keydown', { which: 32 }))
+        assert.ok($(document.activeElement)[0] === $input[0], 'input still focused')
+        $textarea.trigger('focus').trigger($.Event('keydown', { which: 32 }))
+        assert.ok($(document.activeElement)[0] === $textarea[0], 'textarea still focused')
 
+        // Key up
+        $input.trigger('focus').trigger($.Event('keydown', { which: 38 }))
+        assert.ok($(document.activeElement)[0] === $input[0], 'input still focused')
         $textarea.trigger('focus').trigger($.Event('keydown', { which: 38 }))
-        assert.ok($(document.activeElement).is($textarea), 'textarea still focused')
+        assert.ok($(document.activeElement)[0] === $textarea[0], 'textarea still focused')
+
+        // Key down
+        $input.trigger('focus').trigger($.Event('keydown', { which: 40 }))
+        assert.ok($(document.activeElement)[0] === $input[0], 'input still focused')
+        $textarea.trigger('focus').trigger($.Event('keydown', { which: 40 }))
+        assert.ok($(document.activeElement)[0] === $textarea[0], 'textarea still focused')
+
+        // Key escape
+        $input.trigger('focus').trigger($.Event('keydown', { which: 27 }))
+        assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is not shown')
 
         done()
       })
@@ -499,6 +515,126 @@ $(function () {
     $dropdown.trigger('click')
   })
 
+  QUnit.test('should ignore space key events for <input>s within dropdown, and accept up, down and escape', function (assert) {
+    assert.expect(6)
+    var done = assert.async()
+
+    var dropdownHTML = '<ul class="tabs">'
+        + '<li class="dropdown">'
+        + '<input type="text" id="input" data-toggle="dropdown">'
+        + '<ul class="dropdown-menu" role="menu">'
+        + '<li><a id="item1" href="#">Secondary link</a></li>'
+        + '<li><a id="item2" href="#">Something else here</a></li>'
+        + '<li class="divider"/>'
+        + '<li><a href="#">Another link</a></li>'
+        + '</ul>'
+        + '</li>'
+        + '</ul>'
+    var $dropdown = $(dropdownHTML)
+      .appendTo('#qunit-fixture')
+      .find('[data-toggle="dropdown"]')
+      .bootstrapDropdown()
+
+    var $input = $('#input')
+
+    $dropdown
+    .parent('.dropdown')
+    .one('shown.bs.dropdown', function () {
+      assert.ok(true, 'shown was fired')
+
+      // Key space
+      $input.trigger('focus').trigger($.Event('keydown', { which: 32 }))
+      assert.ok($dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is shown')
+      assert.ok($(document.activeElement).is($input), 'input is still focused')
+
+      // Key escape
+      $input.trigger('focus').trigger($.Event('keydown', { which: 27 }))
+      assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is not shown')
+
+      $dropdown
+      .parent('.dropdown')
+      .one('shown.bs.dropdown', function () {
+
+        // Key down
+        $input.trigger('focus').trigger($.Event('keydown', { which: 40 }))
+        assert.ok(document.activeElement === $('#item1')[0], 'item1 is focused')
+
+        $dropdown
+        .parent('.dropdown')
+        .one('shown.bs.dropdown', function () {
+
+          // Key up
+          $input.trigger('focus').trigger($.Event('keydown', { which: 38 }))
+          assert.ok(document.activeElement === $('#item1')[0], 'item1 is focused')
+          done()
+        }).bootstrapDropdown('toggle')
+        $input.trigger('click')
+      })
+      $input.trigger('click')
+    })
+    $input.trigger('click')
+  })
+
+  QUnit.test('should ignore space key events for <textarea>s within dropdown, and accept up, down and escape', function (assert) {
+    assert.expect(6)
+    var done = assert.async()
+
+    var dropdownHTML = '<ul class="tabs">'
+        + '<li class="dropdown">'
+        + '<textarea id="textarea" data-toggle="dropdown"></textarea>'
+        + '<ul class="dropdown-menu" role="menu">'
+        + '<li><a id="item1" href="#">Secondary link</a></li>'
+        + '<li><a id="item2" href="#">Something else here</a></li>'
+        + '<li class="divider"/>'
+        + '<li><a href="#">Another link</a></li>'
+        + '</ul>'
+        + '</li>'
+        + '</ul>'
+    var $dropdown = $(dropdownHTML)
+      .appendTo('#qunit-fixture')
+      .find('[data-toggle="dropdown"]')
+      .bootstrapDropdown()
+
+    var $textarea = $('#textarea')
+
+    $dropdown
+    .parent('.dropdown')
+    .one('shown.bs.dropdown', function () {
+      assert.ok(true, 'shown was fired')
+
+      // Key space
+      $textarea.trigger('focus').trigger($.Event('keydown', { which: 32 }))
+      assert.ok($dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is shown')
+      assert.ok($(document.activeElement).is($textarea), 'textarea is still focused')
+
+      // Key escape
+      $textarea.trigger('focus').trigger($.Event('keydown', { which: 27 }))
+      assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is not shown')
+
+      $dropdown
+      .parent('.dropdown')
+      .one('shown.bs.dropdown', function () {
+
+        // Key down
+        $textarea.trigger('focus').trigger($.Event('keydown', { which: 40 }))
+        assert.ok(document.activeElement === $('#item1')[0], 'item1 is focused')
+
+        $dropdown
+        .parent('.dropdown')
+        .one('shown.bs.dropdown', function () {
+
+          // Key up
+          $textarea.trigger('focus').trigger($.Event('keydown', { which: 38 }))
+          assert.ok(document.activeElement === $('#item1')[0], 'item1 is focused')
+          done()
+        }).bootstrapDropdown('toggle')
+        $textarea.trigger('click')
+      })
+      $textarea.trigger('click')
+    })
+    $textarea.trigger('click')
+  })
+
   QUnit.test('should skip disabled element when using keyboard navigation', function (assert) {
     assert.expect(2)
     var done = assert.async()