From ff861b022fb80e1f658ef7d6129516143bbca872 Mon Sep 17 00:00:00 2001 From: Victor Naumov Date: Mon, 31 Oct 2022 12:35:44 +0500 Subject: [PATCH] feat: drop KeyboardEvent.keyCode in favor of `code` This fixes the issue of not detecting key strokes on non-latin keyboards. BREAKING CHANGE: * Keyboard-related features no longer use KeyboardEvent#keyCode. Use a polyfill (e.g. [keyboardevent-key-polyfill](https://www.npmjs.com/package/keyboardevent-key-polyfill) if you need to support old browsers. Co-authored-by: Maciej Barelkowski --- CHANGELOG.md | 4 ++ lib/features/dragging/Dragging.js | 4 +- lib/features/hand-tool/HandTool.js | 2 +- lib/features/keyboard/KeyboardBindings.js | 13 ++---- lib/features/keyboard/KeyboardUtil.js | 15 +++---- lib/features/search-pad/SearchPad.js | 23 ++++------ test/spec/features/hand-tool/HandToolSpec.js | 8 ++-- test/spec/features/keyboard/CopySpec.js | 2 +- test/spec/features/keyboard/KeyboardSpec.js | 15 ++----- test/spec/features/keyboard/PasteSpec.js | 2 +- test/spec/features/keyboard/RedoSpec.js | 4 +- test/spec/features/keyboard/UndoSpec.js | 2 +- .../spec/features/search-pad/SearchPadSpec.js | 42 +++++++++---------- test/util/KeyEvents.js | 14 ++----- 14 files changed, 62 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71562f50a..35b3c84c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,13 @@ All notable changes to [diagram-js](https://github.com/bpmn-io/diagram-js) are d _**Note:** Yet to be released changes appear here._ +* `FEAT`: drop KeyboardEvent.keyCode in favor of `code` ([#681](https://github.com/bpmn-io/diagram-js/pull/681)) + ### Breaking Changes * `FEAT`: new popupMenu UI featuring menu and group titles, search, entry descriptions and documentation urls ([#687](https://github.com/bpmn-io/diagram-js/pull/687)) * `FEAT`: introduction of `.djs-parent` class to canvas and popup menu root ([#687](https://github.com/bpmn-io/diagram-js/pull/687)) +* Keyboard-related features no longer use KeyboardEvent#keyCode. + Use a polyfill (e.g. [keyboardevent-key-polyfill](https://www.npmjs.com/package/keyboardevent-key-polyfill) if you need to support old browsers. ## 10.0.0 diff --git a/lib/features/dragging/Dragging.js b/lib/features/dragging/Dragging.js index 87b9bb597..6947cd114 100644 --- a/lib/features/dragging/Dragging.js +++ b/lib/features/dragging/Dragging.js @@ -25,6 +25,8 @@ import { delta as deltaPos } from '../../util/PositionUtil'; +import { isKey } from '../keyboard/KeyboardUtil'; + var DRAG_ACTIVE_CLS = 'djs-drag-active'; @@ -282,7 +284,7 @@ export default function Dragging(eventBus, canvas, selection, elementRegistry) { function checkCancel(event) { - if (event.which === 27) { + if (isKey('Escape')) { preventDefault(event); cancel(); diff --git a/lib/features/hand-tool/HandTool.js b/lib/features/hand-tool/HandTool.js index b5bb71b62..fa28355e1 100644 --- a/lib/features/hand-tool/HandTool.js +++ b/lib/features/hand-tool/HandTool.js @@ -155,5 +155,5 @@ HandTool.prototype.isActive = function() { // helpers ////////// function isSpace(keyEvent) { - return isKey(' ', keyEvent); + return isKey('Space', keyEvent); } \ No newline at end of file diff --git a/lib/features/keyboard/KeyboardBindings.js b/lib/features/keyboard/KeyboardBindings.js index 66627893e..6a20be9c8 100644 --- a/lib/features/keyboard/KeyboardBindings.js +++ b/lib/features/keyboard/KeyboardBindings.js @@ -9,15 +9,10 @@ import { var LOW_PRIORITY = 500; -export var KEYCODE_C = 67; -export var KEYCODE_V = 86; -export var KEYCODE_Y = 89; -export var KEYCODE_Z = 90; - -export var KEYS_COPY = [ 'c', 'C', KEYCODE_C ]; -export var KEYS_PASTE = [ 'v', 'V', KEYCODE_V ]; -export var KEYS_REDO = [ 'y', 'Y', KEYCODE_Y ]; -export var KEYS_UNDO = [ 'z', 'Z', KEYCODE_Z ]; +export var KEYS_COPY = [ 'c', 'C', 'KeyC' ]; +export var KEYS_PASTE = [ 'v', 'V', 'KeyV' ]; +export var KEYS_REDO = [ 'y', 'Y', 'KeyY' ]; +export var KEYS_UNDO = [ 'z', 'Z', 'KeyZ' ]; /** diff --git a/lib/features/keyboard/KeyboardUtil.js b/lib/features/keyboard/KeyboardUtil.js index 67b7abed8..a69711e22 100644 --- a/lib/features/keyboard/KeyboardUtil.js +++ b/lib/features/keyboard/KeyboardUtil.js @@ -1,14 +1,9 @@ import { isArray } from 'min-dash'; -var KEYCODE_C = 67; -var KEYCODE_V = 86; -var KEYCODE_Y = 89; -var KEYCODE_Z = 90; - -var KEYS_COPY = [ 'c', 'C', KEYCODE_C ]; -var KEYS_PASTE = [ 'v', 'V', KEYCODE_V ]; -var KEYS_REDO = [ 'y', 'Y', KEYCODE_Y ]; -var KEYS_UNDO = [ 'z', 'Z', KEYCODE_Z ]; +var KEYS_COPY = [ 'c', 'C', 'KeyC' ]; +var KEYS_PASTE = [ 'v', 'V', 'KeyV' ]; +var KEYS_REDO = [ 'y', 'Y', 'KeyY' ]; +var KEYS_UNDO = [ 'z', 'Z', 'KeyZ' ]; /** * Returns true if event was triggered with any modifier @@ -41,7 +36,7 @@ export function isCmd(event) { export function isKey(keys, event) { keys = isArray(keys) ? keys : [ keys ]; - return keys.indexOf(event.key) !== -1 || keys.indexOf(event.keyCode) !== -1; + return keys.indexOf(event.key) !== -1 || keys.indexOf(event.code) !== -1; } /** diff --git a/lib/features/search-pad/SearchPad.js b/lib/features/search-pad/SearchPad.js index c9d32857b..15d978df3 100644 --- a/lib/features/search-pad/SearchPad.js +++ b/lib/features/search-pad/SearchPad.js @@ -16,6 +16,8 @@ import { escapeHTML } from '../../util/EscapeUtil'; +import { isKey } from '../keyboard/KeyboardUtil'; + /** * Provides searching infrastructure */ @@ -93,13 +95,11 @@ SearchPad.prototype._bindEvents = function() { // navigate results listen(this._container, SearchPad.INPUT_SELECTOR, 'keydown', function(e) { - // up - if (e.keyCode === 38) { + if (isKey('ArrowUp', e)) { e.preventDefault(); } - // down - if (e.keyCode === 40) { + if (isKey('ArrowDown', e)) { e.preventDefault(); } }); @@ -107,31 +107,26 @@ SearchPad.prototype._bindEvents = function() { // handle keyboard input listen(this._container, SearchPad.INPUT_SELECTOR, 'keyup', function(e) { - // escape - if (e.keyCode === 27) { + if (isKey('Escape', e)) { return self.close(); } - // enter - if (e.keyCode === 13) { + if (isKey('Enter', e)) { var selected = self._getCurrentResult(); return selected ? self._select(selected) : self.close(); } - // up - if (e.keyCode === 38) { + if (isKey('ArrowUp', e)) { return self._scrollToDirection(true); } - // down - if (e.keyCode === 40) { + if (isKey('ArrowDown', e)) { return self._scrollToDirection(); } - // left && right // do not search while navigating text input - if (e.keyCode === 37 || e.keyCode === 39) { + if (isKey([ 'ArrowLeft', 'ArrowRight' ], e)) { return; } diff --git a/test/spec/features/hand-tool/HandToolSpec.js b/test/spec/features/hand-tool/HandToolSpec.js index 9d1cd4f01..33ec7ce73 100644 --- a/test/spec/features/hand-tool/HandToolSpec.js +++ b/test/spec/features/hand-tool/HandToolSpec.js @@ -128,7 +128,7 @@ describe('features/hand-tool', function() { // when eventBus.fire('keyboard.keydown', { - keyEvent: createKeyEvent(' ') + keyEvent: createKeyEvent('Space') }); // then @@ -152,12 +152,12 @@ describe('features/hand-tool', function() { // given eventBus.fire('keyboard.keydown', { - keyEvent: createKeyEvent(' ') + keyEvent: createKeyEvent('Space') }); // when eventBus.fire('keyboard.keyup', { - keyEvent: createKeyEvent(' ') + keyEvent: createKeyEvent('Space') }); // then @@ -169,7 +169,7 @@ describe('features/hand-tool', function() { // given eventBus.fire('keyboard.keydown', { - keyEvent: createKeyEvent(' ') + keyEvent: createKeyEvent('Space') }); // when diff --git a/test/spec/features/keyboard/CopySpec.js b/test/spec/features/keyboard/CopySpec.js index d5570e931..e8b8308e8 100644 --- a/test/spec/features/keyboard/CopySpec.js +++ b/test/spec/features/keyboard/CopySpec.js @@ -14,7 +14,7 @@ import editorActionsModule from 'lib/features/editor-actions'; import { createKeyEvent } from 'test/util/KeyEvents'; -var KEYS_COPY = [ 'c', 'C', 67 ]; +var KEYS_COPY = [ 'c', 'C', 'KeyC' ]; describe('features/keyboard - copy', function() { diff --git a/test/spec/features/keyboard/KeyboardSpec.js b/test/spec/features/keyboard/KeyboardSpec.js index 95618d9f5..bcce70a55 100755 --- a/test/spec/features/keyboard/KeyboardSpec.js +++ b/test/spec/features/keyboard/KeyboardSpec.js @@ -21,7 +21,7 @@ import { createKeyEvent } from 'test/util/KeyEvents'; describe('features/keyboard', function() { - var TEST_KEY = 99; + var TEST_KEY = 'Numpad3'; var defaultDiagramConfig = { modules: [ @@ -442,15 +442,6 @@ describe('features/keyboard', function() { // helpers ////////// function dispatchKeyboardEvent(target, type) { - var event; - - try { - event = new KeyboardEvent(type); - } catch (e) { - event = document.createEvent('KeyboardEvent'); - - event.initEvent(type, true, false); - } - + var event = createKeyEvent('Any', { type: type }); target.dispatchEvent(event); -} \ No newline at end of file +} diff --git a/test/spec/features/keyboard/PasteSpec.js b/test/spec/features/keyboard/PasteSpec.js index 585a555e0..e4f39d294 100644 --- a/test/spec/features/keyboard/PasteSpec.js +++ b/test/spec/features/keyboard/PasteSpec.js @@ -14,7 +14,7 @@ import editorActionsModule from 'lib/features/editor-actions'; import { createKeyEvent } from 'test/util/KeyEvents'; -var KEYS_PASTE = [ 'v', 'V', 86 ]; +var KEYS_PASTE = [ 'v', 'V', 'KeyV' ]; describe('features/keyboard - paste', function() { diff --git a/test/spec/features/keyboard/RedoSpec.js b/test/spec/features/keyboard/RedoSpec.js index 16a94d038..709d3ceb5 100644 --- a/test/spec/features/keyboard/RedoSpec.js +++ b/test/spec/features/keyboard/RedoSpec.js @@ -13,8 +13,8 @@ import keyboardModule from 'lib/features/keyboard'; import { createKeyEvent } from 'test/util/KeyEvents'; -var KEYS_REDO = [ 'y', 'Y', 89 ]; -var KEYS_UNDO = [ 'z', 'Z', 90 ]; +var KEYS_REDO = [ 'y', 'Y', 'KeyY' ]; +var KEYS_UNDO = [ 'z', 'Z', 'KeyZ' ]; describe('features/keyboard - redo', function() { diff --git a/test/spec/features/keyboard/UndoSpec.js b/test/spec/features/keyboard/UndoSpec.js index 6f93428c7..f5d9c783f 100644 --- a/test/spec/features/keyboard/UndoSpec.js +++ b/test/spec/features/keyboard/UndoSpec.js @@ -13,7 +13,7 @@ import keyboardModule from 'lib/features/keyboard'; import { createKeyEvent } from 'test/util/KeyEvents'; -var KEYS_UNDO = [ 'z', 'Z', 90 ]; +var KEYS_UNDO = [ 'z', 'Z', 'KeyZ' ]; describe('features/keyboard - undo', function() { diff --git a/test/spec/features/search-pad/SearchPadSpec.js b/test/spec/features/search-pad/SearchPadSpec.js index f92c0e3d4..9114431ee 100644 --- a/test/spec/features/search-pad/SearchPadSpec.js +++ b/test/spec/features/search-pad/SearchPadSpec.js @@ -3,6 +3,10 @@ import { inject } from 'test/TestHelper'; +import { + createKeyEvent +} from 'test/util/KeyEvents'; + import searchPadModule from 'lib/features/search-pad'; import SearchPad from 'lib/features/search-pad/SearchPad'; @@ -267,7 +271,7 @@ describe('features/searchPad', function() { typeText(input_node, 'two'); // when - triggerKeyEvent(input_node, 'keyup', 13); + triggerKeyEvent(input_node, 'keyup', 'Enter'); // then expect(capturedEvents).to.eql([ @@ -296,7 +300,7 @@ describe('features/searchPad', function() { typeText(input_node, 'one'); // when - triggerKeyEvent(input_node, 'keyup', 13); + triggerKeyEvent(input_node, 'keyup', 'Enter'); // then var overlay = overlays.get({ element: element }); @@ -321,7 +325,7 @@ describe('features/searchPad', function() { }); // when - triggerKeyEvent(input_node, 'keyup', 13); + triggerKeyEvent(input_node, 'keyup', 'Enter'); // then var newViewbox = canvas.viewbox(); @@ -338,7 +342,7 @@ describe('features/searchPad', function() { typeText(input_node, 'one'); // when - triggerKeyEvent(input_node, 'keyup', 13); + triggerKeyEvent(input_node, 'keyup', 'Enter'); // then var newViewbox = canvas.viewbox(); @@ -352,7 +356,7 @@ describe('features/searchPad', function() { typeText(input_node, 'one'); // when - triggerKeyEvent(input_node, 'keyup', 13); + triggerKeyEvent(input_node, 'keyup', 'Enter'); // then expect(selection.isSelected(element)).to.be.true; @@ -362,7 +366,7 @@ describe('features/searchPad', function() { it('should close on escape', inject(function(canvas, eventBus, searchPad) { // when - triggerKeyEvent(input_node, 'keyup', 27); + triggerKeyEvent(input_node, 'keyup', 'Escape'); // then expect(searchPad.isOpen()).to.equal(false); @@ -377,14 +381,14 @@ describe('features/searchPad', function() { var result_nodes = domQueryAll(SearchPad.RESULT_SELECTOR, canvas.getContainer()); // when press 'down' - triggerKeyEvent(input_node, 'keyup', 40); + triggerKeyEvent(input_node, 'keyup', 'ArrowDown'); // then expect(domClasses(result_nodes[0]).has(SearchPad.RESULT_SELECTED_CLASS)).to.be.false; expect(domClasses(result_nodes[1]).has(SearchPad.RESULT_SELECTED_CLASS)).to.be.true; // when press 'up' - triggerKeyEvent(input_node, 'keyup', 38); + triggerKeyEvent(input_node, 'keyup', 'ArrowUp'); // then expect(domClasses(result_nodes[0]).has(SearchPad.RESULT_SELECTED_CLASS)).to.be.true; @@ -405,7 +409,7 @@ describe('features/searchPad', function() { typeText(input_node, 'two'); // when press 'down' - var e = triggerKeyEvent(input_node, 'keydown', 40); + var e = triggerKeyEvent(input_node, 'keydown', 'ArrowDown'); expect(e.defaultPrevented).to.be.true; })); @@ -416,7 +420,7 @@ describe('features/searchPad', function() { typeText(input_node, 'two'); // when press 'up' - var e = triggerKeyEvent(input_node, 'keydown', 38); + var e = triggerKeyEvent(input_node, 'keydown', 'ArrowUp'); expect(e.defaultPrevented).to.be.true; })); @@ -428,7 +432,7 @@ describe('features/searchPad', function() { typeText(input_node, 'two'); // when press 'left' - triggerKeyEvent(input_node, 'keyup', 37); + triggerKeyEvent(input_node, 'keyup', 'ArrowLeft'); // then expect(find).callCount('two'.length); @@ -442,7 +446,7 @@ describe('features/searchPad', function() { typeText(input_node, 'two'); // when press 'right' - triggerKeyEvent(input_node, 'keyup', 39); + triggerKeyEvent(input_node, 'keyup', 'ArrowRight'); // then expect(find).callCount('two'.length); @@ -453,18 +457,12 @@ describe('features/searchPad', function() { }); -function triggerKeyEvent(element, event, code) { - var e = document.createEvent('Events'); - - if (e.initEvent) { - e.initEvent(event, true, true); - } +function triggerKeyEvent(element, eventType, code) { + var event = createKeyEvent(code, { type: eventType }); - e.keyCode = code; - e.which = code; - element.dispatchEvent(e); + element.dispatchEvent(event); - return e; + return event; } diff --git a/test/util/KeyEvents.js b/test/util/KeyEvents.js index 60c480602..df2d5dbdc 100644 --- a/test/util/KeyEvents.js +++ b/test/util/KeyEvents.js @@ -1,12 +1,11 @@ import { - assign, - isString + assign } from 'min-dash'; /** * Create a fake key event for testing purposes. * - * @param {String|number} key the key or keyCode/charCode + * @param {String} key the key or code * @param {Object} [attrs] * @param {string} [attrs.type] * @@ -17,13 +16,8 @@ export function createKeyEvent(key, attrs) { attrs = {}; } - var event = document.createEvent('Events') || new document.defaultView.CustomEvent('keyEvent'); - - // init and mark as bubbles / cancelable - event.initEvent(attrs.type || 'keydown', false, true); - - var keyAttrs = isString(key) ? { key: key } : { keyCode: key, which: key }; - + var event = new Event(attrs.type || 'keydown', { bubbles: true, cancelable: true }); + var keyAttrs = { key: key, code: key }; delete attrs.type; return assign(event, keyAttrs, attrs);