Enter accessibility (#2982)

* Fix shift clicking on a block

* Add tests for toggle keyboar nav
This commit is contained in:
alschmiedt
2019-09-09 16:41:06 -07:00
committed by GitHub
parent 22fdf2ac20
commit 4a80889ef1
6 changed files with 119 additions and 31 deletions

View File

@@ -216,9 +216,7 @@ Blockly.onKeyDown_ = function(e) {
if (mainWorkspace.options.readOnly) {
// When in read only mode handle key actions for keyboard navigation.
if (Blockly.keyboardAccessibilityMode) {
Blockly.navigation.onKeyPress(e);
}
Blockly.navigation.onKeyPress(e);
return;
}
@@ -227,8 +225,7 @@ Blockly.onKeyDown_ = function(e) {
// Pressing esc closes the context menu.
Blockly.hideChaff();
Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_EXIT);
} else if (Blockly.keyboardAccessibilityMode &&
Blockly.navigation.onKeyPress(e)) {
} else if (Blockly.navigation.onKeyPress(e)) {
// If the keyboard or field handled the key press return.
return;
} else if (e.keyCode == Blockly.utils.KeyCodes.BACKSPACE ||

View File

@@ -498,9 +498,12 @@ Blockly.Gesture.prototype.doStart = function(e) {
Blockly.Tooltip.block();
if (this.targetBlock_) {
this.targetBlock_.select();
if (!this.targetBlock_.isInFlyout && e.shiftKey) {
Blockly.navigation.enableKeyboardAccessibility();
this.creatorWorkspace_.cursor.setLocation(
Blockly.navigation.getTopNode(this.targetBlock_));
} else {
this.targetBlock_.select();
}
}
@@ -760,7 +763,6 @@ Blockly.Gesture.prototype.doBlockClick_ = function() {
* @private
*/
Blockly.Gesture.prototype.doWorkspaceClick_ = function(e) {
Blockly.navigation.disableKeyboardAccessibility();
var ws = this.creatorWorkspace_;
if (e.shiftKey) {
Blockly.navigation.enableKeyboardAccessibility();

View File

@@ -37,10 +37,15 @@ goog.require('Blockly.utils.KeyCodes');
Blockly.user.keyMap.map_ = {};
/**
* List of modifier keys checked when serializing the key event.
* @type {Array<string>}
* Object holding valid modifiers.
* @enum {string}
*/
Blockly.user.keyMap.modifierKeys = ['Shift', 'Control', 'Alt', 'Meta'];
Blockly.user.keyMap.modifierKeys = {
SHIFT: 'Shift',
CONTROL: 'Control',
ALT: 'Alt',
META: 'Meta'
};
/**
* Update the key map to contain the new action.
@@ -113,9 +118,9 @@ Blockly.user.keyMap.getKeyByAction = function(action) {
* @return {!string} A string containing the serialized key event.
*/
Blockly.user.keyMap.serializeKeyEvent = function(e) {
var modifierKeys = Blockly.user.keyMap.modifierKeys;
var modifiers = Object.values(Blockly.user.keyMap.modifierKeys);
var key = '';
for (var i = 0, keyName; keyName = modifierKeys[i]; i++) {
for (var i = 0, keyName; keyName = modifiers[i]; i++) {
if (e.getModifierState(keyName)) {
key += keyName;
}
@@ -124,6 +129,27 @@ Blockly.user.keyMap.serializeKeyEvent = function(e) {
return key;
};
/**
* Create the serialized key code that will be used in the key map.
* @param {!number} keyCode Number code representing the key.
* @param {!Array<string>} modifiers List of modifiers to be used with the key.
* All valid modifiers can be found in the Blockly.user.keyMap.modifierKeys.
* @return {string} The serialized key code for the given modifiers and key.
*/
Blockly.user.keyMap.createSerializedKey = function(keyCode, modifiers) {
var key = '';
var validModifiers = Object.values(Blockly.user.keyMap.modifierKeys);
for (var i = 0, keyName; keyName = modifiers[i]; i++) {
if (validModifiers.indexOf(keyName) > -1) {
key += keyName;
} else {
throw Error(keyName + ' is not a valid modifier key.');
}
}
key += keyCode;
return key;
};
/**
* Creates the default key map.
* @return {!Object<string,Blockly.Action>} An object holding the default key
@@ -131,6 +157,9 @@ Blockly.user.keyMap.serializeKeyEvent = function(e) {
*/
Blockly.user.keyMap.createDefaultKeyMap = function() {
var map = {};
var controlK = Blockly.user.keyMap.createSerializedKey(
Blockly.utils.KeyCodes.K, [Blockly.user.keyMap.modifierKeys.CONTROL]);
map[Blockly.utils.KeyCodes.W] = Blockly.navigation.ACTION_PREVIOUS;
map[Blockly.utils.KeyCodes.A] = Blockly.navigation.ACTION_OUT;
map[Blockly.utils.KeyCodes.S] = Blockly.navigation.ACTION_NEXT;
@@ -141,5 +170,6 @@ Blockly.user.keyMap.createDefaultKeyMap = function() {
map[Blockly.utils.KeyCodes.T] = Blockly.navigation.ACTION_TOOLBOX;
map[Blockly.utils.KeyCodes.E] = Blockly.navigation.ACTION_EXIT;
map[Blockly.utils.KeyCodes.ESC] = Blockly.navigation.ACTION_EXIT;
map[controlK] = Blockly.navigation.ACTION_TOGGLE_KEYBOARD_NAV;
return map;
};

View File

@@ -105,7 +105,8 @@ Blockly.navigation.actionNames = {
MARK: 'mark',
DISCONNECT: 'disconnect',
TOOLBOX: 'toolbox',
EXIT: 'exit'
EXIT: 'exit',
TOGGLE_KEYBOARD_NAV: 'toggle_keyboard_nav'
};
/**
* Set the navigation cursor.
@@ -153,7 +154,7 @@ Blockly.navigation.removeMark = function() {
* block.
* @package
*/
Blockly.navigation.getTopNode_ = function(block) {
Blockly.navigation.getTopNode = function(block) {
var prevConnection = block.previousConnection;
var outConnection = block.outputConnection;
var topConnection = prevConnection ? prevConnection : outConnection;
@@ -390,7 +391,7 @@ Blockly.navigation.insertFromFlyout = function() {
}
Blockly.navigation.focusWorkspace();
Blockly.navigation.cursor_.setLocation(Blockly.navigation.getTopNode_(newBlock));
Blockly.navigation.cursor_.setLocation(Blockly.navigation.getTopNode(newBlock));
Blockly.navigation.removeMark();
};
@@ -724,9 +725,8 @@ Blockly.navigation.disconnectBlocks = function() {
/*************************/
/**
* Sets the cursor to the previous or output connection of the selected block
* on the workspace.
* If no block is selected, places the cursor at a fixed point on the workspace.
* Finds where the cursor should go on the workspace. This is either the top
* block or a set position on the workspace.
*/
Blockly.navigation.focusWorkspace = function() {
var cursor = Blockly.navigation.cursor_;
@@ -735,11 +735,8 @@ Blockly.navigation.focusWorkspace = function() {
Blockly.navigation.resetFlyout(reset);
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
if (Blockly.selected) {
cursor.setLocation(Blockly.navigation.getTopNode_(Blockly.selected));
Blockly.selected.unselect();
} else if (topBlocks.length > 0) {
cursor.setLocation(Blockly.navigation.getTopNode_(topBlocks[0]));
if (topBlocks.length > 0) {
cursor.setLocation(Blockly.navigation.getTopNode(topBlocks[0]));
} else {
var ws = cursor.workspace_;
// TODO: Find the center of the visible workspace.
@@ -860,15 +857,22 @@ Blockly.navigation.onKeyPress = function(e) {
var actionHandled = false;
if (action) {
if (!readOnly) {
if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) {
actionHandled = curNode.getLocation().onBlocklyAction(action);
}
if (!actionHandled) {
if (Blockly.keyboardAccessibilityMode) {
if (!readOnly) {
if (curNode && curNode.getType() === Blockly.ASTNode.types.FIELD) {
actionHandled = curNode.getLocation().onBlocklyAction(action);
}
if (!actionHandled) {
actionHandled = Blockly.navigation.onBlocklyAction(action);
}
// If in readonly mode only handle valid actions.
} else if (Blockly.navigation.READONLY_ACTION_LIST.indexOf(action) > -1) {
actionHandled = Blockly.navigation.onBlocklyAction(action);
}
} else if (Blockly.navigation.READONLY_ACTION_LIST.indexOf(action) > -1) {
actionHandled = Blockly.navigation.onBlocklyAction(action);
// If not in accessibility mode only hanlde turning on keyboard navigation.
} else if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) {
Blockly.navigation.enableKeyboardAccessibility();
actionHandled = true;
}
}
return actionHandled;
@@ -882,7 +886,10 @@ Blockly.navigation.onKeyPress = function(e) {
* @package
*/
Blockly.navigation.onBlocklyAction = function(action) {
if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_WS) {
if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) {
Blockly.navigation.disableKeyboardAccessibility();
return true;
} else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_WS) {
return Blockly.navigation.workspaceOnAction_(action);
} else if (Blockly.navigation.currentState_ === Blockly.navigation.STATE_FLYOUT) {
return Blockly.navigation.flyoutOnAction_(action);
@@ -1116,6 +1123,13 @@ Blockly.navigation.ACTION_TOOLBOX = new Blockly.Action(
Blockly.navigation.ACTION_EXIT = new Blockly.Action(
Blockly.navigation.actionNames.EXIT, 'Close the current modal, such as a toolbox or field editor.');
/**
* The action to toggle keyboard navigation mode on and off.
* @type {!Blockly.Action}
*/
Blockly.navigation.ACTION_TOGGLE_KEYBOARD_NAV = new Blockly.Action(
Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV, 'Turns on and off keyboard navigation.');
/**
* List of actions that can be performed in read only mode.
* @type {!Array<!Blockly.Action>}

View File

@@ -370,6 +370,37 @@ suite('Navigation', function() {
field.onBlocklyAction.restore();
Blockly.navigation.onBlocklyAction.restore();
});
test('Toggle Action Off', function() {
var cursor = new Blockly.Cursor();
Blockly.navigation.setCursor(cursor);
this.mockEvent.keyCode = 'Control75';
sinon.spy(Blockly.navigation, 'onBlocklyAction');
Blockly.keyboardAccessibilityMode = true;
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
chai.assert.isTrue(isHandled);
chai.assert.isTrue(Blockly.navigation.onBlocklyAction.calledOnce);
chai.assert.isFalse(Blockly.keyboardAccessibilityMode);
Blockly.navigation.onBlocklyAction.restore();
});
test('Toggle Action On', function() {
var cursor = new Blockly.Cursor();
Blockly.navigation.setCursor(cursor);
this.workspace = Blockly.inject('blocklyDiv', {readOnly: false});
this.mockEvent.keyCode = 'Control75';
sinon.stub(Blockly.navigation, 'focusWorkspace');
Blockly.keyboardAccessibilityMode = false;
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
chai.assert.isTrue(isHandled);
chai.assert.isTrue(Blockly.navigation.focusWorkspace.calledOnce);
chai.assert.isTrue(Blockly.keyboardAccessibilityMode);
Blockly.navigation.focusWorkspace.restore();
this.workspace.dispose();
});
suite('Test key press in read only mode', function() {
setup(function() {
Blockly.defineBlocksWithJsonArray([{

View File

@@ -351,6 +351,14 @@ function toggleRenderingDebug(state) {
}
}
function toggleAccessibilityMode(state) {
if (state) {
Blockly.navigation.enableKeyboardAccessibility();
} else {
Blockly.navigation.disableKeyboardAccessibility();
}
}
function logger(e) {
console.log(e);
}
@@ -520,6 +528,12 @@ h1 {
<input type="checkbox" onclick="toggleRenderingDebug(this.checked)" id="blockRenderDebugCheck">
</p>
<p>
Enable Accessibility Mode: &nbsp;
<input type="checkbox" onclick="toggleAccessibilityMode(this.checked)" id="accessibilityModeCheck">
</p>
<!-- The next three blocks of XML are sample toolboxes for testing basic
configurations. For more information on building toolboxes, see https://developers.google.com/blockly/guides/configure/web/toolbox -->