diff --git a/js/src/carousel.js b/js/src/carousel.js index a5451538cde511603f74e8e96a1431eae5abb49f..93f896e535c1502804aa8f2dd1291fca7837af0f 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -33,8 +33,8 @@ const DATA_KEY = 'bs.carousel' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key -const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key +const ARROW_LEFT_KEY = 'ArrowLeft' +const ARROW_RIGHT_KEY = 'ArrowRight' const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch const SWIPE_THRESHOLD = 40 @@ -342,12 +342,12 @@ class Carousel { return } - switch (event.which) { - case ARROW_LEFT_KEYCODE: + switch (event.key) { + case ARROW_LEFT_KEY: event.preventDefault() this.prev() break - case ARROW_RIGHT_KEYCODE: + case ARROW_RIGHT_KEY: event.preventDefault() this.next() break diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index 7fdeaa4f8e9010648564ead59df78c79339de6cc..e43edd6ee2c84585ebb9962247b5efee3d5f12f6 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -17,7 +17,6 @@ import { defaultPreventedPreservedOnDispatch } from './polyfill' const $ = getjQuery() const namespaceRegex = /[^.]*(?=\..*)\.|.*/ const stripNameRegex = /\..*/ -const keyEventRegex = /^key/ const stripUidRegex = /::\d+$/ const eventRegistry = {} // Events storage let uidEvent = 1 @@ -94,11 +93,6 @@ function getEvent(element) { } function fixEvent(event, element) { - // Add which for key events - if (event.which === null && keyEventRegex.test(event.type)) { - event.which = event.charCode === null ? event.keyCode : event.charCode - } - event.delegateTarget = element } diff --git a/js/src/dropdown.js b/js/src/dropdown.js index aab1d6bd2a20feacd9cc229658bef43a938f8a03..b1aa6d8496a78c9136b4dd2859f02e6c041d0ef3 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -31,14 +31,14 @@ const DATA_KEY = 'bs.dropdown' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -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 -const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key -const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key -const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) +const ESCAPE_KEY = 'Escape' +const SPACE_KEY = 'Space' +const TAB_KEY = 'Tab' +const ARROW_UP_KEY = 'ArrowUp' +const ARROW_DOWN_KEY = 'ArrowDown' +const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button -const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) +const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`) const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` @@ -372,8 +372,8 @@ class Dropdown { } static clearMenus(event) { - if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || - (event.type === 'keyup' && event.which !== TAB_KEYCODE))) { + if (event && (event.button === RIGHT_MOUSE_BUTTON || + (event.type === 'keyup' && event.key !== TAB_KEY))) { return } @@ -401,7 +401,7 @@ class Dropdown { if (event && ((event.type === 'click' && /input|textarea/i.test(event.target.tagName)) || - (event.type === 'keyup' && event.which === TAB_KEYCODE)) && + (event.type === 'keyup' && event.key === TAB_KEY)) && dropdownMenu.contains(event.target)) { continue } @@ -443,10 +443,10 @@ class Dropdown { // - 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.key === SPACE_KEY || (event.key !== ESCAPE_KEY && + ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) || SelectorEngine.closest(event.target, SELECTOR_MENU))) : - !REGEXP_KEYDOWN.test(event.which)) { + !REGEXP_KEYDOWN.test(event.key)) { return } @@ -460,14 +460,14 @@ class Dropdown { const parent = Dropdown.getParentFromElement(this) const isActive = this.classList.contains(CLASS_NAME_SHOW) - if (event.which === ESCAPE_KEYCODE) { + if (event.key === ESCAPE_KEY) { const button = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] button.focus() Dropdown.clearMenus() return } - if (!isActive || event.which === SPACE_KEYCODE) { + if (!isActive || event.key === SPACE_KEY) { Dropdown.clearMenus() return } @@ -481,11 +481,11 @@ class Dropdown { let index = items.indexOf(event.target) || 0 - if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up + if (event.key === ARROW_UP_KEY && index > 0) { // Up index-- } - if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down + if (event.key === ARROW_DOWN_KEY && index < items.length - 1) { // Down index++ } diff --git a/js/src/modal.js b/js/src/modal.js index d5f1c8c9ae99042142c38ad3b5e177e2f761778b..0daa428a8dfeeb5c021cb4ecf46f0c713701aa9e 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -31,7 +31,7 @@ const VERSION = '4.3.1' const DATA_KEY = 'bs.modal' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key +const ESCAPE_KEY = 'Escape' const Default = { backdrop: true, @@ -299,10 +299,10 @@ class Modal { _setEscapeEvent() { if (this._isShown) { EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { - if (this._config.keyboard && event.which === ESCAPE_KEYCODE) { + if (this._config.keyboard && event.key === ESCAPE_KEY) { event.preventDefault() this.hide() - } else if (!this._config.keyboard && event.which === ESCAPE_KEYCODE) { + } else if (!this._config.keyboard && event.key === ESCAPE_KEY) { this._triggerBackdropTransition() } }) diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js index be32d19d9b36412495efa66057cbebc2a47c453a..46659a032aa9fc2ca1465156cd8f6c79d0155be0 100644 --- a/js/tests/unit/carousel.spec.js +++ b/js/tests/unit/carousel.spec.js @@ -70,10 +70,10 @@ describe('Carousel', () => { done() }) - const keyDown = createEvent('keydown') - keyDown.which = 39 + const keydown = createEvent('keydown') + keydown.key = 'ArrowRight' - carouselEl.dispatchEvent(keyDown) + carouselEl.dispatchEvent(keydown) }) it('should go to previous item if left arrow key is pressed', done => { @@ -100,10 +100,10 @@ describe('Carousel', () => { done() }) - const keyDown = createEvent('keydown') - keyDown.which = 37 + const keydown = createEvent('keydown') + keydown.key = 'ArrowLeft' - carouselEl.dispatchEvent(keyDown) + carouselEl.dispatchEvent(keydown) }) it('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', done => { @@ -130,10 +130,10 @@ describe('Carousel', () => { done() }) - const keyDown = createEvent('keydown') - keyDown.which = 40 + const keydown = createEvent('keydown') + keydown.key = 'ArrowDown' - carouselEl.dispatchEvent(keyDown) + carouselEl.dispatchEvent(keydown) }) it('should ignore keyboard events within <input>s and <textarea>s', () => { @@ -157,34 +157,34 @@ describe('Carousel', () => { keyboard: true }) - const spyKeyDown = spyOn(carousel, '_keydown').and.callThrough() + const spyKeydown = spyOn(carousel, '_keydown').and.callThrough() const spyPrev = spyOn(carousel, 'prev') const spyNext = spyOn(carousel, 'next') - const keyDown = createEvent('keydown', { bubbles: true, cancelable: true }) - keyDown.which = 39 - Object.defineProperty(keyDown, 'target', { + const keydown = createEvent('keydown', { bubbles: true, cancelable: true }) + keydown.key = 'ArrowRight' + Object.defineProperty(keydown, 'target', { value: input, writable: true, configurable: true }) - input.dispatchEvent(keyDown) + input.dispatchEvent(keydown) - expect(spyKeyDown).toHaveBeenCalled() + expect(spyKeydown).toHaveBeenCalled() expect(spyPrev).not.toHaveBeenCalled() expect(spyNext).not.toHaveBeenCalled() - spyKeyDown.calls.reset() + spyKeydown.calls.reset() spyPrev.calls.reset() spyNext.calls.reset() - Object.defineProperty(keyDown, 'target', { + Object.defineProperty(keydown, 'target', { value: textarea }) - textarea.dispatchEvent(keyDown) + textarea.dispatchEvent(keydown) - expect(spyKeyDown).toHaveBeenCalled() + expect(spyKeydown).toHaveBeenCalled() expect(spyPrev).not.toHaveBeenCalled() expect(spyNext).not.toHaveBeenCalled() }) diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index e8a7e66ba9ee5e0b29221ff660e205ad5ffd219c..b2820f72e05e2355b4b51225a27a6490fce944d7 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -1068,10 +1068,10 @@ describe('Dropdown', () => { dropdownEl.addEventListener('shown.bs.dropdown', () => { expect(btnDropdown.classList.contains('show')).toEqual(true) - const keyUp = createEvent('keyup') + const keyup = createEvent('keyup') - keyUp.which = 9 // Tab - document.dispatchEvent(keyUp) + keyup.key = 'Tab' + document.dispatchEvent(keyup) }) dropdownEl.addEventListener('hidden.bs.dropdown', () => { @@ -1165,10 +1165,10 @@ describe('Dropdown', () => { expect(first.classList.contains('show')).toEqual(true, '"show" class added on click') expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown') - const keyUp = createEvent('keyup') - keyUp.which = 9 // Tab + const keyup = createEvent('keyup') + keyup.key = 'Tab' - document.dispatchEvent(keyUp) + document.dispatchEvent(keyup) }) dropdownTestMenu.addEventListener('hidden.bs.dropdown', () => { @@ -1180,10 +1180,10 @@ describe('Dropdown', () => { expect(last.classList.contains('show')).toEqual(true, '"show" class added on click') expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown') - const keyUp = createEvent('keyup') - keyUp.which = 9 // Tab + const keyup = createEvent('keyup') + keyup.key = 'Tab' - document.dispatchEvent(keyUp) + document.dispatchEvent(keyup) }) btnGroup.addEventListener('hidden.bs.dropdown', () => { @@ -1217,10 +1217,10 @@ describe('Dropdown', () => { }) dropdown.addEventListener('shown.bs.dropdown', () => { - const keyDown = createEvent('keydown') + const keydown = createEvent('keydown') - keyDown.which = 27 - triggerDropdown.dispatchEvent(keyDown) + keydown.key = 'Escape' + triggerDropdown.dispatchEvent(keydown) }) triggerDropdown.click() @@ -1245,15 +1245,15 @@ describe('Dropdown', () => { dropdown.addEventListener('shown.bs.dropdown', () => { input.focus() - const keyDown = createEvent('keydown') + const keydown = createEvent('keydown') - keyDown.which = 38 - input.dispatchEvent(keyDown) + keydown.key = 'ArrowUp' + input.dispatchEvent(keydown) expect(document.activeElement).toEqual(input, 'input still focused') textarea.focus() - textarea.dispatchEvent(keyDown) + textarea.dispatchEvent(keydown) expect(document.activeElement).toEqual(textarea, 'textarea still focused') done() @@ -1278,11 +1278,11 @@ describe('Dropdown', () => { const dropdown = fixtureEl.querySelector('.dropdown') dropdown.addEventListener('shown.bs.dropdown', () => { - const keyDown = createEvent('keydown') - keyDown.which = 40 + const keydown = createEvent('keydown') + keydown.key = 'ArrowDown' - triggerDropdown.dispatchEvent(keyDown) - triggerDropdown.dispatchEvent(keyDown) + triggerDropdown.dispatchEvent(keydown) + triggerDropdown.dispatchEvent(keydown) expect(document.activeElement.classList.contains('disabled')).toEqual(false, '.disabled not focused') expect(document.activeElement.hasAttribute('disabled')).toEqual(false, ':disabled not focused') @@ -1314,10 +1314,10 @@ describe('Dropdown', () => { const dropdown = fixtureEl.querySelector('.dropdown') dropdown.addEventListener('shown.bs.dropdown', () => { - const keyDown = createEvent('keydown') - keyDown.which = 40 + const keydown = createEvent('keydown') + keydown.key = 'ArrowDown' - triggerDropdown.dispatchEvent(keyDown) + triggerDropdown.dispatchEvent(keydown) expect(document.activeElement.classList.contains('d-none')).toEqual(false, '.d-none not focused') expect(document.activeElement.style.display === 'none').toEqual(false, '"display: none" not focused') @@ -1346,19 +1346,19 @@ describe('Dropdown', () => { const item2 = fixtureEl.querySelector('#item2') dropdown.addEventListener('shown.bs.dropdown', () => { - const keyDown40 = createEvent('keydown') - keyDown40.which = 40 + const keydownArrowDown = createEvent('keydown') + keydownArrowDown.key = 'ArrowDown' - triggerDropdown.dispatchEvent(keyDown40) + triggerDropdown.dispatchEvent(keydownArrowDown) expect(document.activeElement).toEqual(item1, 'item1 is focused') - document.activeElement.dispatchEvent(keyDown40) + document.activeElement.dispatchEvent(keydownArrowDown) expect(document.activeElement).toEqual(item2, 'item2 is focused') - const keyDown38 = createEvent('keydown') - keyDown38.which = 38 + const keydownArrowUp = createEvent('keydown') + keydownArrowUp.key = 'ArrowUp' - document.activeElement.dispatchEvent(keyDown38) + document.activeElement.dispatchEvent(keydownArrowUp) expect(document.activeElement).toEqual(item1, 'item1 is focused') done() @@ -1438,59 +1438,55 @@ describe('Dropdown', () => { const input = fixtureEl.querySelector('input') const textarea = fixtureEl.querySelector('textarea') - // Space key - const keyDownSpace = createEvent('keydown') - keyDownSpace.which = 32 + const keydownSpace = createEvent('keydown') + keydownSpace.key = 'Space' - // Key up - const keyDownUp = createEvent('keydown') - keyDownSpace.which = 38 + const keydownArrowUp = createEvent('keydown') + keydownArrowUp.key = 'ArrowUp' - // Key down - const keyDown = createEvent('keydown') - keyDownSpace.which = 40 + const keydownArrowDown = createEvent('keydown') + keydownArrowDown.key = 'ArrowDown' - // Key escape - const keyDownEscape = createEvent('keydown') - keyDownEscape.which = 27 + const keydownEscape = createEvent('keydown') + keydownEscape.key = 'Escape' dropdown.addEventListener('shown.bs.dropdown', () => { - // Space key + // Key Space input.focus() - input.dispatchEvent(keyDownSpace) + input.dispatchEvent(keydownSpace) expect(document.activeElement).toEqual(input, 'input still focused') textarea.focus() - textarea.dispatchEvent(keyDownSpace) + textarea.dispatchEvent(keydownSpace) expect(document.activeElement).toEqual(textarea, 'textarea still focused') - // Key up + // Key ArrowUp input.focus() - input.dispatchEvent(keyDownUp) + input.dispatchEvent(keydownArrowUp) expect(document.activeElement).toEqual(input, 'input still focused') textarea.focus() - textarea.dispatchEvent(keyDownUp) + textarea.dispatchEvent(keydownArrowUp) expect(document.activeElement).toEqual(textarea, 'textarea still focused') - // Key down + // Key ArrowDown input.focus() - input.dispatchEvent(keyDown) + input.dispatchEvent(keydownArrowDown) expect(document.activeElement).toEqual(input, 'input still focused') textarea.focus() - textarea.dispatchEvent(keyDown) + textarea.dispatchEvent(keydownArrowDown) expect(document.activeElement).toEqual(textarea, 'textarea still focused') - // Key escape + // Key Escape input.focus() - input.dispatchEvent(keyDownEscape) + input.dispatchEvent(keydownEscape) expect(triggerDropdown.classList.contains('show')).toEqual(false, 'dropdown menu is not shown') done() @@ -1523,9 +1519,9 @@ describe('Dropdown', () => { // Key escape button.focus() // Key escape - const keyDownEscape = createEvent('keydown') - keyDownEscape.which = 27 - button.dispatchEvent(keyDownEscape) + const keydownEscape = createEvent('keydown') + keydownEscape.key = 'Escape' + button.dispatchEvent(keydownEscape) setTimeout(() => { expect(dropdown.toggle).not.toHaveBeenCalled() diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js index 52676844f4a812eb497eb4b9d78c37e526d93c76..a2077b59442df8d7d1bdcebf5f6ddac16aab4f28 100644 --- a/js/tests/unit/modal.spec.js +++ b/js/tests/unit/modal.spec.js @@ -450,7 +450,7 @@ describe('Modal', () => { modalEl.addEventListener('shown.bs.modal', () => { const keydownEscape = createEvent('keydown') - keydownEscape.which = 27 + keydownEscape.key = 'Escape' modalEl.dispatchEvent(keydownEscape) }) @@ -479,7 +479,7 @@ describe('Modal', () => { modalEl.addEventListener('shown.bs.modal', () => { const keydownTab = createEvent('keydown') - keydownTab.which = 9 + keydownTab.key = 'Tab' modalEl.dispatchEvent(keydownTab) setTimeout(expectDone, 30) @@ -584,7 +584,7 @@ describe('Modal', () => { modalEl.addEventListener('shown.bs.modal', () => { const keydownEscape = createEvent('keydown') - keydownEscape.which = 27 + keydownEscape.key = 'Escape' modalEl.dispatchEvent(keydownEscape) shownCallback() @@ -611,7 +611,7 @@ describe('Modal', () => { modalEl.addEventListener('shown.bs.modal', () => { const keydownEscape = createEvent('keydown') - keydownEscape.which = 27 + keydownEscape.key = 'Escape' modalEl.dispatchEvent(keydownEscape) shownCallback()