diff --git a/js/src/dom/eventHandler.js b/js/src/dom/eventHandler.js index 819f489ea1d63090f2a24e7ccb77e4acbc683e86..c73bcd2ab844ff59839347d782805694fa8f4d39 100644 --- a/js/src/dom/eventHandler.js +++ b/js/src/dom/eventHandler.js @@ -334,7 +334,7 @@ const EventHandler = (() => { let defaultPrevented = false if (inNamespace && typeof $ !== 'undefined') { - jQueryEvent = new $.Event(event, args) + jQueryEvent = $.Event(event, args) $(element).trigger(jQueryEvent) bubbles = !jQueryEvent.isPropagationStopped() @@ -342,8 +342,7 @@ const EventHandler = (() => { defaultPrevented = jQueryEvent.isDefaultPrevented() } - let evt = null - + let evt = null if (isNative) { evt = document.createEvent('HTMLEvents') evt.initEvent(typeEvent, bubbles, true) diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js index b8136dda13fdd7899a1de3c38f406677e5f5aeef..215837bf67927eeea20f692b809c8e2c79d8a804 100644 --- a/js/src/dom/manipulator.js +++ b/js/src/dom/manipulator.js @@ -38,6 +38,18 @@ const Manipulator = { } element.removeAttribute(`data-${key.replace(/[A-Z]/g, (chr) => `-${chr.toLowerCase()}`)}`) + }, + + toggleClass(element, className) { + if (typeof element === 'undefined' || element === null) { + return + } + + if (element.classList.contains(className)) { + element.classList.remove(className) + } else { + element.classList.add(className) + } } } diff --git a/js/src/dropdown.js b/js/src/dropdown.js index d336a46d9176fb33e4b660836f8291a9faab75c9..85d8738e0d497c415ee7a6c86efd53435d722731 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -5,8 +5,11 @@ * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import Data from './dom/data' +import EventHandler from './dom/eventHandler' +import Manipulator from './dom/manipulator' import Popper from 'popper.js' +import SelectorEngine from './dom/selectorEngine' import Util from './util' /** @@ -20,7 +23,6 @@ const VERSION = '4.3.1' const DATA_KEY = 'bs.dropdown' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key @@ -120,12 +122,12 @@ class Dropdown { // Public toggle() { - if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) { + if (this._element.disabled || this._element.classList.contains(ClassName.DISABLED)) { return } const parent = Dropdown._getParentFromElement(this._element) - const isActive = $(this._menu).hasClass(ClassName.SHOW) + const isActive = this._menu.classList.contains(ClassName.SHOW) Dropdown._clearMenus() @@ -136,11 +138,9 @@ class Dropdown { const relatedTarget = { relatedTarget: this._element } - const showEvent = $.Event(Event.SHOW, relatedTarget) + const showEvent = EventHandler.trigger(parent, Event.SHOW, relatedTarget) - $(parent).trigger(showEvent) - - if (showEvent.isDefaultPrevented()) { + if (showEvent.defaultPrevented) { return } @@ -171,7 +171,7 @@ class Dropdown { // to allow the menu to "escape" the scroll parent's boundaries // https://github.com/twbs/bootstrap/issues/24251 if (this._config.boundary !== 'scrollParent') { - $(parent).addClass(ClassName.POSITION_STATIC) + parent.classList.add(ClassName.POSITION_STATIC) } this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()) } @@ -181,68 +181,64 @@ class Dropdown { // only needed because of broken event delegation on iOS // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement && - $(parent).closest(Selector.NAVBAR_NAV).length === 0) { - $(document.body).children().on('mouseover', null, $.noop) + !Util.makeArray(SelectorEngine.closest(parent, Selector.NAVBAR_NAV)).length) { + Util.makeArray(document.body.children) + .forEach((elem) => EventHandler.on(elem, 'mouseover', null, Util.noop())) } this._element.focus() this._element.setAttribute('aria-expanded', true) - $(this._menu).toggleClass(ClassName.SHOW) - $(parent) - .toggleClass(ClassName.SHOW) - .trigger($.Event(Event.SHOWN, relatedTarget)) + Manipulator.toggleClass(this._menu, ClassName.SHOW) + Manipulator.toggleClass(parent, ClassName.SHOW) + EventHandler.trigger(parent, Event.SHOWN, relatedTarget) } show() { - if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || $(this._menu).hasClass(ClassName.SHOW)) { + if (this._element.disabled || this._element.classList.contains(ClassName.DISABLED) || this._menu.classList.contains(ClassName.SHOW)) { return } + const parent = Dropdown._getParentFromElement(this._element) const relatedTarget = { relatedTarget: this._element } - const showEvent = $.Event(Event.SHOW, relatedTarget) - const parent = Dropdown._getParentFromElement(this._element) - $(parent).trigger(showEvent) + const showEvent = EventHandler.trigger(parent, Event.SHOW, relatedTarget) - if (showEvent.isDefaultPrevented()) { + if (showEvent.defaultPrevented) { return } - $(this._menu).toggleClass(ClassName.SHOW) - $(parent) - .toggleClass(ClassName.SHOW) - .trigger($.Event(Event.SHOWN, relatedTarget)) + Manipulator.toggleClass(this._menu, ClassName.SHOW) + Manipulator.toggleClass(parent, ClassName.SHOW) + EventHandler.trigger(parent, Event.SHOWN, relatedTarget) } hide() { - if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || !$(this._menu).hasClass(ClassName.SHOW)) { + if (this._element.disabled || this._element.classList.contains(ClassName.DISABLED) || !this._menu.classList.contains(ClassName.SHOW)) { return } + const parent = Dropdown._getParentFromElement(this._element) const relatedTarget = { relatedTarget: this._element } - const hideEvent = $.Event(Event.HIDE, relatedTarget) - const parent = Dropdown._getParentFromElement(this._element) - $(parent).trigger(hideEvent) + const hideEvent = EventHandler.trigger(parent, Event.HIDE, relatedTarget) - if (hideEvent.isDefaultPrevented()) { + if (hideEvent.defaultPrevented) { return } - $(this._menu).toggleClass(ClassName.SHOW) - $(parent) - .toggleClass(ClassName.SHOW) - .trigger($.Event(Event.HIDDEN, relatedTarget)) + Manipulator.toggleClass(this._menu, ClassName.SHOW) + Manipulator.toggleClass(parent, ClassName.SHOW) + EventHandler.trigger(parent, Event.SHOWN, relatedTarget) } dispose() { - $.removeData(this._element, DATA_KEY) - $(this._element).off(EVENT_KEY) + Data.removeData(this._element, DATA_KEY) + EventHandler.off(this._element, EVENT_KEY) this._element = null this._menu = null if (this._popper !== null) { @@ -261,7 +257,7 @@ class Dropdown { // Private _addEventListeners() { - $(this._element).on(Event.CLICK, (event) => { + EventHandler.on(this._element, Event.CLICK, (event) => { event.preventDefault() event.stopPropagation() this.toggle() @@ -271,7 +267,7 @@ class Dropdown { _getConfig(config) { config = { ...this.constructor.Default, - ...$(this._element).data(), + ...Util.getDataAttributes(this._element), ...config } @@ -296,27 +292,27 @@ class Dropdown { } _getPlacement() { - const $parentDropdown = $(this._element.parentNode) - let placement = AttachmentMap.BOTTOM + const parentDropdown = this._element.parentNode + let placement = AttachmentMap.BOTTOM // Handle dropup - if ($parentDropdown.hasClass(ClassName.DROPUP)) { + if (parentDropdown.classList.contains(ClassName.DROPUP)) { placement = AttachmentMap.TOP - if ($(this._menu).hasClass(ClassName.MENURIGHT)) { + if (this._menu.classList.contains(ClassName.MENURIGHT)) { placement = AttachmentMap.TOPEND } - } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) { + } else if (parentDropdown.classList.contains(ClassName.DROPRIGHT)) { placement = AttachmentMap.RIGHT - } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) { + } else if (parentDropdown.classList.contains(ClassName.DROPLEFT)) { placement = AttachmentMap.LEFT - } else if ($(this._menu).hasClass(ClassName.MENURIGHT)) { + } else if (this._menu.classList.contains(ClassName.MENURIGHT)) { placement = AttachmentMap.BOTTOMEND } return placement } _detectNavbar() { - return $(this._element).closest('.navbar').length > 0 + return Util.makeArray(SelectorEngine.closest(this._element, '.navbar')).length > 0 } _getOffset() { @@ -364,22 +360,26 @@ class Dropdown { // Static - static _jQueryInterface(config) { - return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = typeof config === 'object' ? config : null + static _dropdownInterface(element, config) { + let data = Data.getData(element, DATA_KEY) + const _config = typeof config === 'object' ? config : null - if (!data) { - data = new Dropdown(this, _config) - $(this).data(DATA_KEY, data) - } + if (!data) { + data = new Dropdown(element, _config) + Data.setData(element, DATA_KEY, data) + } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } - data[config]() + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new Error(`No method named "${config}"`) } + data[config]() + } + } + + static _jQueryInterface(config) { + return this.each(function () { + Dropdown._dropdownInterface(this, config) }) } @@ -389,11 +389,10 @@ class Dropdown { return } - const toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE)) - + const toggles = Util.makeArray(SelectorEngine.find(Selector.DATA_TOGGLE)) for (let i = 0, len = toggles.length; i < len; i++) { - const parent = Dropdown._getParentFromElement(toggles[i]) - const context = $(toggles[i]).data(DATA_KEY) + const parent = Dropdown._getParentFromElement(toggles[i]) + const context = Data.getData(toggles[i], DATA_KEY) const relatedTarget = { relatedTarget: toggles[i] } @@ -407,34 +406,34 @@ class Dropdown { } const dropdownMenu = context._menu - if (!$(parent).hasClass(ClassName.SHOW)) { + if (!parent.classList.contains(ClassName.SHOW)) { continue } if (event && (event.type === 'click' && - /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && - $.contains(parent, event.target)) { + /input|textarea/i.test(event.target.tagName) || + event.type === 'keyup' && event.which === TAB_KEYCODE) && + parent.contains(event.target)) { continue } - const hideEvent = $.Event(Event.HIDE, relatedTarget) - $(parent).trigger(hideEvent) - if (hideEvent.isDefaultPrevented()) { + const hideEvent = EventHandler.trigger(parent, Event.HIDE, relatedTarget) + if (hideEvent.defaultPrevented) { continue } // If this is a touch-enabled device we remove the extra // empty mouseover listeners we added for iOS support if ('ontouchstart' in document.documentElement) { - $(document.body).children().off('mouseover', null, $.noop) + Util.makeArray(document.body.children) + .forEach((elem) => EventHandler.off(elem, 'mouseover', null, Util.noop())) } toggles[i].setAttribute('aria-expanded', 'false') - $(dropdownMenu).removeClass(ClassName.SHOW) - $(parent) - .removeClass(ClassName.SHOW) - .trigger($.Event(Event.HIDDEN, relatedTarget)) + dropdownMenu.classList.remove(ClassName.SHOW) + parent.classList.remove(ClassName.SHOW) + EventHandler.trigger(parent, Event.HIDDEN, relatedTarget) } } @@ -468,26 +467,25 @@ class Dropdown { event.preventDefault() event.stopPropagation() - if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { + if (this.disabled || this.classList.contains(ClassName.DISABLED)) { return } const parent = Dropdown._getParentFromElement(this) - const isActive = $(parent).hasClass(ClassName.SHOW) + const isActive = parent.classList.contains(ClassName.SHOW) if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { if (event.which === ESCAPE_KEYCODE) { - const toggle = parent.querySelector(Selector.DATA_TOGGLE) - $(toggle).trigger('focus') + EventHandler.trigger(SelectorEngine.findOne(Selector.DATA_TOGGLE, parent), 'focus') } - $(this).trigger('click') + EventHandler.trigger(this, 'click') return } const items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS)) - if (items.length === 0) { + if (!items.length) { return } @@ -515,31 +513,34 @@ class Dropdown { * ------------------------------------------------------------------------ */ -$(document) - .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) - .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) { - event.preventDefault() - event.stopPropagation() - Dropdown._jQueryInterface.call($(this), 'toggle') - }) - .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => { - e.stopPropagation() - }) +EventHandler.on(document, Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) +EventHandler.on(document, Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler) +EventHandler.on(document, Event.CLICK_DATA_API, Dropdown._clearMenus) +EventHandler.on(document, Event.KEYUP_DATA_API, Dropdown._clearMenus) +EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { + event.preventDefault() + event.stopPropagation() + Dropdown._dropdownInterface(this, 'toggle') +}) +EventHandler + .on(document, Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => e.stopPropagation()) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .dropdown to jQuery only if jQuery is present */ -$.fn[NAME] = Dropdown._jQueryInterface -$.fn[NAME].Constructor = Dropdown -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Dropdown._jQueryInterface +const $ = Util.jQuery +if (typeof $ !== 'undefined') { + const JQUERY_NO_CONFLICT = $.fn[NAME] + $.fn[NAME] = Dropdown._jQueryInterface + $.fn[NAME].Constructor = Dropdown + $.fn[NAME].noConflict = () => { + $.fn[NAME] = JQUERY_NO_CONFLICT + return Dropdown._jQueryInterface + } } - export default Dropdown diff --git a/js/tests/unit/dom/eventHandler.js b/js/tests/unit/dom/eventHandler.js index 998132911e1eb8ceb168df96ba148974ffecf497..f28261632834bf7d4364d57221b785f5f76feb24 100644 --- a/js/tests/unit/dom/eventHandler.js +++ b/js/tests/unit/dom/eventHandler.js @@ -336,6 +336,5 @@ $(function () { assert.ok(i === 5, 'listener removed again') document.body.removeChild(element) - }) }) diff --git a/js/tests/unit/dropdown.js b/js/tests/unit/dropdown.js index a94595ad201de5baeb11afcb09e72ada74f81c88..a39d272ef38e2693d5fbe708e0bcaee7fb6d9558 100644 --- a/js/tests/unit/dropdown.js +++ b/js/tests/unit/dropdown.js @@ -28,6 +28,7 @@ $(function () { QUnit.test('should throw explicit error on undefined method', function (assert) { assert.expect(1) var $el = $('<div/>') + $el.appendTo('#qunit-fixture') $el.bootstrapDropdown() try { $el.bootstrapDropdown('noMethod') @@ -39,6 +40,7 @@ $(function () { QUnit.test('should return jquery collection containing the element', function (assert) { assert.expect(2) var $el = $('<div/>') + $el.appendTo('#qunit-fixture') var $dropdown = $el.bootstrapDropdown() assert.ok($dropdown instanceof $, 'returns jquery collection') assert.strictEqual($dropdown[0], $el[0], 'collection contains element') @@ -129,13 +131,14 @@ $(function () { .appendTo('#qunit-fixture') .find('[data-toggle="dropdown"]') .bootstrapDropdown() - $dropdown - .parent('.dropdown') - .on('shown.bs.dropdown', function () { - assert.strictEqual($dropdown.attr('aria-expanded'), 'true', 'aria-expanded is set to string "true" on click') - done() - }) - $dropdown.trigger('click') + + var dropdownParent = $dropdown.parent('.dropdown')[0] + + EventHandler.on(dropdownParent, 'shown.bs.dropdown', function () { + assert.strictEqual($dropdown.attr('aria-expanded'), 'true', 'aria-expanded is set to string "true" on click') + done() + }) + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should set aria-expanded="false" on target when dropdown menu is hidden', function (assert) { @@ -157,15 +160,15 @@ $(function () { .find('[data-toggle="dropdown"]') .bootstrapDropdown() - $dropdown - .parent('.dropdown') - .on('hidden.bs.dropdown', function () { - assert.strictEqual($dropdown.attr('aria-expanded'), 'false', 'aria-expanded is set to string "false" on hide') - done() - }) + var dropdownParent = $dropdown.parent('.dropdown')[0] - $dropdown.trigger('click') - $(document.body).trigger('click') + EventHandler.one(dropdownParent, 'hidden.bs.dropdown', function () { + assert.strictEqual($dropdown.attr('aria-expanded'), 'false', 'aria-expanded is set to string "false" on hide') + done() + }) + + EventHandler.trigger($dropdown[0], 'click') + EventHandler.trigger(document.body, 'click') }) QUnit.test('should not open dropdown if target is disabled via class', function (assert) { @@ -197,23 +200,26 @@ $(function () { var done = assert.async() 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="divider"/>' + - '<a class="dropdown-item" href="#">Another link</a>' + - '</div>' + - '</div>' + + ' <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="divider"/>' + + ' <a class="dropdown-item" href="#">Another link</a>' + + ' </div>' + + ' </div>' + '</div>' - var $dropdown = $(dropdownHTML).find('[data-toggle="dropdown"]').bootstrapDropdown() + + $(dropdownHTML).appendTo('#qunit-fixture') + var $dropdown = $('#qunit-fixture').find('[data-toggle="dropdown"]').bootstrapDropdown() + $dropdown .parent('.dropdown') .on('shown.bs.dropdown', function () { assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') done() }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should remove "show" class if body is clicked', function (assert) { @@ -244,7 +250,8 @@ $(function () { assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class removed') done() }) - $dropdown.trigger('click') + + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should remove "show" class if tabbing outside of menu', function (assert) { @@ -269,14 +276,17 @@ $(function () { .parent('.dropdown') .on('shown.bs.dropdown', function () { assert.ok($dropdown.parent('.dropdown').hasClass('show'), '"show" class added on click') - var e = $.Event('keyup') - e.which = 9 // Tab - $(document.body).trigger(e) - }).on('hidden.bs.dropdown', function () { + + EventHandler.trigger(document.body, 'keyup', { + which: 9 // Tab + }) + }) + .on('hidden.bs.dropdown', function () { assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), '"show" class removed') done() }) - $dropdown.trigger('click') + + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should remove "show" class if body is clicked, with multiple dropdowns', function (assert) { @@ -310,7 +320,7 @@ $(function () { $(document.body).trigger('click') }).on('hidden.bs.dropdown', function () { assert.strictEqual($('#qunit-fixture .dropdown-menu.show').length, 0, '"show" class removed') - $last.trigger('click') + EventHandler.trigger($last[0], 'click') }) $last.parent('.btn-group') @@ -322,7 +332,7 @@ $(function () { assert.strictEqual($('#qunit-fixture .dropdown-menu.show').length, 0, '"show" class removed') done() }) - $first.trigger('click') + EventHandler.trigger($first[0], 'click') }) QUnit.test('should remove "show" class if body if tabbing outside of menu, with multiple dropdowns', function (assert) { @@ -353,26 +363,26 @@ $(function () { .on('shown.bs.dropdown', function () { assert.strictEqual($first.parents('.show').length, 1, '"show" class added on click') assert.strictEqual($('#qunit-fixture .dropdown-menu.show').length, 1, 'only one dropdown is shown') - var e = $.Event('keyup') - e.which = 9 // Tab - $(document.body).trigger(e) + EventHandler.trigger(document.body, 'keyup', { + which: 9 // Tab + }) }).on('hidden.bs.dropdown', function () { assert.strictEqual($('#qunit-fixture .dropdown-menu.show').length, 0, '"show" class removed') - $last.trigger('click') + EventHandler.trigger($last[0], 'click') }) $last.parent('.btn-group') .on('shown.bs.dropdown', function () { assert.strictEqual($last.parent('.show').length, 1, '"show" class added on click') assert.strictEqual($('#qunit-fixture .dropdown-menu.show').length, 1, 'only one dropdown is shown') - var e = $.Event('keyup') - e.which = 9 // Tab - $(document.body).trigger(e) + EventHandler.trigger(document.body, 'keyup', { + which: 9 // Tab + }) }).on('hidden.bs.dropdown', function () { assert.strictEqual($('#qunit-fixture .dropdown-menu.show').length, 0, '"show" class removed') done() }) - $first.trigger('click') + EventHandler.trigger($first[0], 'click') }) QUnit.test('should fire show and hide event', function (assert) { @@ -405,8 +415,8 @@ $(function () { done() }) - $dropdown.trigger('click') - $(document.body).trigger('click') + EventHandler.trigger($dropdown[0], 'click') + EventHandler.trigger(document.body, 'click') }) QUnit.test('should fire shown and hidden event', function (assert) { @@ -439,8 +449,8 @@ $(function () { done() }) - $dropdown.trigger('click') - $(document.body).trigger('click') + EventHandler.trigger($dropdown[0], 'click') + EventHandler.trigger(document.body, 'click') }) QUnit.test('should fire shown and hidden event with a relatedTarget', function (assert) { @@ -472,7 +482,7 @@ $(function () { $(document.body).trigger('click') }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should fire hide and hidden event with a clickEvent', function (assert) { @@ -586,7 +596,7 @@ $(function () { done() }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should skip disabled element when using keyboard navigation', function (assert) { @@ -621,7 +631,7 @@ $(function () { assert.ok(!$(document.activeElement).is(':disabled'), ':disabled is not focused') done() }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should focus next/previous element when using keyboard navigation', function (assert) { @@ -645,23 +655,24 @@ $(function () { .parent('.dropdown') .on('shown.bs.dropdown', function () { assert.ok(true, 'shown was fired') - $dropdown.trigger($.Event('keydown', { + + EventHandler.trigger($dropdown[0], 'keydown', { which: 40 - })) + }) assert.ok($(document.activeElement).is($('#item1')), 'item1 is focused') - $(document.activeElement).trigger($.Event('keydown', { + EventHandler.trigger(document.activeElement, 'keydown', { which: 40 - })) + }) assert.ok($(document.activeElement).is($('#item2')), 'item2 is focused') - $(document.activeElement).trigger($.Event('keydown', { + EventHandler.trigger(document.activeElement, 'keydown', { which: 38 - })) + }) assert.ok($(document.activeElement).is($('#item1')), 'item1 is focused') done() }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should not close the dropdown if the user clicks on a text field', function (assert) { @@ -688,9 +699,9 @@ $(function () { .parent('.dropdown') .on('shown.bs.dropdown', function () { assert.ok($dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is shown') - $textfield.trigger($.Event('click')) + EventHandler.trigger($textfield[0], 'click') }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should not close the dropdown if the user clicks on a textarea', function (assert) { @@ -717,9 +728,9 @@ $(function () { .parent('.dropdown') .on('shown.bs.dropdown', function () { assert.ok($dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is shown') - $textarea.trigger($.Event('click')) + EventHandler.trigger($textarea[0], 'click') }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('Dropdown should not use Popper.js in navbar', function (assert) { @@ -748,7 +759,7 @@ $(function () { assert.ok(typeof $dropdownMenu.attr('style') === 'undefined', 'No inline style applied by Popper.js') done() }) - $triggerDropdown.trigger($.Event('click')) + EventHandler.trigger($triggerDropdown[0], 'click') }) QUnit.test('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', function (assert) { @@ -810,14 +821,16 @@ $(function () { assert.ok($(document.activeElement)[0] === $textarea[0], 'textarea still focused') // Key escape - $input.trigger('focus').trigger($.Event('keydown', { + $input.trigger('focus') + EventHandler.trigger($input[0], 'keydown', { which: 27 - })) + }) + assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is not shown') done() }) - $dropdown.trigger('click') + EventHandler.trigger($dropdown[0], 'click') }) QUnit.test('should ignore space key events for <input>s within dropdown, and accept up, down and escape', function (assert) { @@ -857,35 +870,40 @@ $(function () { assert.ok($(document.activeElement).is($input), 'input is still focused') // Key escape - $input.trigger('focus').trigger($.Event('keydown', { + $input.trigger('focus') + EventHandler.trigger($input[0], 'keydown', { which: 27 - })) + }) assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is not shown') $dropdown .parent('.dropdown') .one('shown.bs.dropdown', function () { + assert.ok(true, 'shown was fired') + // Key down - $input.trigger('focus').trigger($.Event('keydown', { + $input.trigger('focus') + EventHandler.trigger($input[0], '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', { + $input.trigger('focus') + EventHandler.trigger($input[0], 'keydown', { which: 38 - })) + }) assert.ok(document.activeElement === $('#item1')[0], 'item1 is focused') done() }).bootstrapDropdown('toggle') - $input.trigger('click') + EventHandler.trigger($input[0], 'click') }) - $input.trigger('click') + EventHandler.trigger($input[0], 'click') }) - $input.trigger('click') + EventHandler.trigger($input[0], 'click') }) QUnit.test('should ignore space key events for <textarea>s within dropdown, and accept up, down and escape', function (assert) { @@ -925,35 +943,40 @@ $(function () { assert.ok($(document.activeElement).is($textarea), 'textarea is still focused') // Key escape - $textarea.trigger('focus').trigger($.Event('keydown', { + $textarea.trigger('focus') + EventHandler.trigger($textarea[0], 'keydown', { which: 27 - })) + }) assert.ok(!$dropdown.parent('.dropdown').hasClass('show'), 'dropdown menu is not shown') $dropdown .parent('.dropdown') .one('shown.bs.dropdown', function () { + assert.ok(true, 'shown was fired') + // Key down - $textarea.trigger('focus').trigger($.Event('keydown', { + $textarea.trigger('focus') + EventHandler.trigger($textarea[0], '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', { + $textarea.trigger('focus') + EventHandler.trigger($textarea[0], 'keydown', { which: 38 - })) + }) assert.ok(document.activeElement === $('#item1')[0], 'item1 is focused') done() }).bootstrapDropdown('toggle') - $textarea.trigger('click') + EventHandler.trigger($textarea[0], 'click') }) - $textarea.trigger('click') + EventHandler.trigger($textarea[0], 'click') }) - $textarea.trigger('click') + EventHandler.trigger($textarea[0], 'click') }) QUnit.test('should not use Popper.js if display set to static', function (assert) { diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html index d1bb0ed35080c63bb5f375ffb56034cf2e82e67d..2fbfd50dd50a779be87d94582fd705634b90d682 100644 --- a/js/tests/visual/dropdown.html +++ b/js/tests/visual/dropdown.html @@ -61,6 +61,12 @@ <div class="row"> <div class="col-sm-12 mt-4"> + <div class="dropdown"> + <button type="button" class="btn btn-secondary" data-toggle="dropdown" aria-expanded="false">Dropdown</button> + <div class="dropdown-menu"> + <input id="textField" type="text"> + </div> + </div> <div class="btn-group dropup"> <button type="button" class="btn btn-secondary">Dropup split</button> <button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> @@ -206,6 +212,9 @@ <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script> <script src="../../../node_modules/popper.js/dist/umd/popper.min.js"></script> <script src="../../dist/dom/eventHandler.js"></script> + <script src="../../dist/dom/data.js"></script> + <script src="../../dist/dom/selectorEngine.js"></script> + <script src="../../dist/dom/manipulator.js"></script> <script src="../../dist/util.js"></script> <script src="../../dist/dropdown.js"></script> <script src="../../dist/collapse.js"></script>