diff --git a/lib/features/keyboard/KeyboardBindings.js b/lib/features/keyboard/KeyboardBindings.js index 9e4c8efea..b3f1d4bd1 100644 --- a/lib/features/keyboard/KeyboardBindings.js +++ b/lib/features/keyboard/KeyboardBindings.js @@ -8,7 +8,8 @@ import { KEYS_COPY, KEYS_PASTE, KEYS_UNDO, - KEYS_REDO + KEYS_REDO, + KEY_CODES } from './KeyboardUtil'; /** @@ -21,7 +22,8 @@ export { KEYS_COPY, KEYS_PASTE, KEYS_UNDO, - KEYS_REDO + KEYS_REDO, + KEY_CODES }; @@ -79,7 +81,7 @@ KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) // undo - // (CTRL|CMD) + Z + // (CTRL|CMD) + Z (any keyboard layout) addListener('undo', function(context) { var event = context.keyEvent; @@ -92,8 +94,8 @@ KeyboardBindings.prototype.registerBindings = function(keyboard, editorActions) }); // redo - // CTRL + Y - // CMD + SHIFT + Z + // CTRL + Y (any keyboard layout) + // CMD + SHIFT + Z (any keyboard layout) addListener('redo', function(context) { var event = context.keyEvent; diff --git a/lib/features/keyboard/KeyboardUtil.js b/lib/features/keyboard/KeyboardUtil.js index 7ed848f12..880c0287a 100644 --- a/lib/features/keyboard/KeyboardUtil.js +++ b/lib/features/keyboard/KeyboardUtil.js @@ -5,6 +5,12 @@ export var KEYS_PASTE = [ 'v', 'V' ]; export var KEYS_REDO = [ 'y', 'Y' ]; export var KEYS_UNDO = [ 'z', 'Z' ]; +// Use key codes for consistent behavior across keyboard layouts +export var KEY_CODES = { + Z: 'KeyZ', + Y: 'KeyY' +}; + /** * Returns true if event was triggered with any modifier * @param {KeyboardEvent} event @@ -66,7 +72,10 @@ export function isPaste(event) { * @param {KeyboardEvent} event */ export function isUndo(event) { - return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event); + return isCmd(event) && !isShift(event) && ( + isKey(KEYS_UNDO, event) || + event.code === KEY_CODES.Z + ); } /** @@ -74,8 +83,13 @@ export function isUndo(event) { */ export function isRedo(event) { return isCmd(event) && ( - isKey(KEYS_REDO, event) || ( + isKey(KEYS_REDO, event) || + event.code === KEY_CODES.Y || + ( isKey(KEYS_UNDO, event) && isShift(event) + ) || + ( + event.code === KEY_CODES.Z && isShift(event) ) ); } diff --git a/test/spec/features/keyboard/RedoSpec.js b/test/spec/features/keyboard/RedoSpec.js index e54da0d44..bc4aa5b46 100644 --- a/test/spec/features/keyboard/RedoSpec.js +++ b/test/spec/features/keyboard/RedoSpec.js @@ -15,6 +15,8 @@ import { createKeyEvent } from 'test/util/KeyEvents'; var KEYS_REDO = [ 'y', 'Y' ]; var KEYS_UNDO = [ 'z', 'Z' ]; +var KEY_CODE_Y = 'KeyY'; +var KEY_CODE_Z = 'KeyZ'; describe('features/keyboard - redo', function() { @@ -31,20 +33,26 @@ describe('features/keyboard - redo', function() { }; var decisionTable = [ { - desc: 'should call redo', - keys: KEYS_UNDO, + desc: 'should call redo (Y key)', + keys: KEYS_REDO, ctrlKey: true, - shiftKey: true, + shiftKey: false, called: true }, { - desc: 'should call redo', - keys: KEYS_REDO, + desc: 'should call redo (KeyY code)', + keyCode: KEY_CODE_Y, ctrlKey: true, shiftKey: false, called: true }, { - desc: 'should call redo', - keys: KEYS_REDO, + desc: 'should call redo (Z + Shift)', + keys: KEYS_UNDO, + ctrlKey: true, + shiftKey: true, + called: true + }, { + desc: 'should call redo (KeyZ + Shift)', + keyCode: KEY_CODE_Z, ctrlKey: true, shiftKey: true, called: true @@ -79,16 +87,39 @@ describe('features/keyboard - redo', function() { forEach(decisionTable, function(testCase) { - forEach(testCase.keys, function(key) { + if (testCase.keys) { + forEach(testCase.keys, function(key) { + + it(testCase.desc, inject(function(keyboard, editorActions) { + + // given + var redoSpy = sinon.spy(editorActions, 'trigger'); + + var event = createKeyEvent(key, { + ctrlKey: testCase.ctrlKey, + shiftKey: testCase.shiftKey + }); + + // when + keyboard._keyHandler(event); + + // then + expect(redoSpy.calledWith('redo')).to.be.equal(testCase.called); + })); + + }); + } else if (testCase.keyCode) { it(testCase.desc, inject(function(keyboard, editorActions) { // given var redoSpy = sinon.spy(editorActions, 'trigger'); + var key = testCase.keyCode === KEY_CODE_Y ? 'y' : 'z'; var event = createKeyEvent(key, { ctrlKey: testCase.ctrlKey, - shiftKey: testCase.shiftKey + shiftKey: testCase.shiftKey, + code: testCase.keyCode }); // when @@ -98,7 +129,7 @@ describe('features/keyboard - redo', function() { expect(redoSpy.calledWith('redo')).to.be.equal(testCase.called); })); - }); + } }); diff --git a/test/spec/features/keyboard/UndoSpec.js b/test/spec/features/keyboard/UndoSpec.js index b21de0690..fa48802b2 100644 --- a/test/spec/features/keyboard/UndoSpec.js +++ b/test/spec/features/keyboard/UndoSpec.js @@ -14,6 +14,7 @@ import keyboardModule from 'lib/features/keyboard'; import { createKeyEvent } from 'test/util/KeyEvents'; var KEYS_UNDO = [ 'z', 'Z' ]; +var KEY_CODE_Z = 'KeyZ'; describe('features/keyboard - undo', function() { @@ -30,28 +31,28 @@ describe('features/keyboard - undo', function() { }; var decisionTable = [ { - desc: 'should call undo', + desc: 'should call undo (Z key)', keys: KEYS_UNDO, ctrlKey: true, shiftKey: false, called: true }, { - desc: 'should not call undo', - keys: KEYS_UNDO, + desc: 'should call undo (KeyZ code)', + keyCode: KEY_CODE_Z, ctrlKey: true, - shiftKey: true, - called: false + shiftKey: false, + called: true }, { desc: 'should not call undo', keys: KEYS_UNDO, ctrlKey: false, - shiftKey: true, + shiftKey: false, called: false }, { desc: 'should not call undo', keys: KEYS_UNDO, - ctrlKey: false, - shiftKey: false, + ctrlKey: true, + shiftKey: true, called: false } ]; @@ -60,16 +61,38 @@ describe('features/keyboard - undo', function() { forEach(decisionTable, function(testCase) { - forEach(testCase.keys, function(key) { + if (testCase.keys) { + forEach(testCase.keys, function(key) { + + it(testCase.desc, inject(function(keyboard, editorActions) { + + // given + var undoSpy = sinon.spy(editorActions, 'trigger'); + + var event = createKeyEvent(key, { + ctrlKey: testCase.ctrlKey, + shiftKey: testCase.shiftKey + }); + + // when + keyboard._keyHandler(event); + + // then + expect(undoSpy.calledWith('undo')).to.be.equal(testCase.called); + })); + + }); + } else if (testCase.keyCode) { it(testCase.desc, inject(function(keyboard, editorActions) { // given var undoSpy = sinon.spy(editorActions, 'trigger'); - var event = createKeyEvent(key, { + var event = createKeyEvent('z', { ctrlKey: testCase.ctrlKey, - shiftKey: testCase.shiftKey + shiftKey: testCase.shiftKey, + code: testCase.keyCode }); // when @@ -79,7 +102,7 @@ describe('features/keyboard - undo', function() { expect(undoSpy.calledWith('undo')).to.be.equal(testCase.called); })); - }); + } });