diff --git a/core/components/tree/basenode.js b/core/components/tree/basenode.js index 57975c693..f9a1dcff3 100644 --- a/core/components/tree/basenode.js +++ b/core/components/tree/basenode.js @@ -40,7 +40,7 @@ goog.require('Blockly.utils.style'); * * @param {string} content The content of the node label treated as * plain-text and will be HTML escaped. - * @param {Blockly.tree.BaseNode.Config} config The configuration for the tree. + * @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree. * @constructor * @extends {Blockly.Component} */ @@ -49,7 +49,7 @@ Blockly.tree.BaseNode = function(content, config) { /** * The configuration for the tree. - * @type {Blockly.tree.BaseNode.Config} + * @type {!Blockly.tree.BaseNode.Config} * @private */ this.config_ = config; @@ -487,6 +487,17 @@ Blockly.tree.BaseNode.prototype.select = function() { } }; +/** + * Selects the first node. + * @protected + */ +Blockly.tree.BaseNode.prototype.selectFirst = function() { + var tree = this.getTree(); + if (tree && this.firstChild_) { + tree.setSelectedItem(this.firstChild_); + } +}; + /** * Called from the tree to instruct the node change its selection state. * @param {boolean} selected The new selection state. @@ -885,7 +896,7 @@ Blockly.tree.BaseNode.prototype.getElement = function() { /** * @return {Element} The row is the div that is used to draw the node without * the children. - * @protected + * @package */ Blockly.tree.BaseNode.prototype.getRowElement = function() { var el = this.getElement(); @@ -1116,43 +1127,22 @@ Blockly.tree.BaseNode.prototype.onKeyDown = function(e) { if (e.altKey) { break; } - if (this.hasChildren()) { - if (!this.getExpanded()) { - this.setExpanded(true); - } else { - this.getFirstChild().select(); - } - } + handled = this.selectChild(); break; case Blockly.utils.KeyCodes.LEFT: if (e.altKey) { break; } - if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) { - this.setExpanded(false); - } else { - var parent = this.getParent(); - var tree = this.getTree(); - // don't go to root if hidden - if (parent && (parent != tree)) { - parent.select(); - } - } + handled = this.selectParent(); break; case Blockly.utils.KeyCodes.DOWN: - var nextNode = this.getNextShownNode(); - if (nextNode) { - nextNode.select(); - } + handled = this.selectNext(); break; case Blockly.utils.KeyCodes.UP: - var previousNode = this.getPreviousShownNode(); - if (previousNode) { - previousNode.select(); - } + handled = this.selectPrevious(); break; default: @@ -1166,6 +1156,70 @@ Blockly.tree.BaseNode.prototype.onKeyDown = function(e) { return handled; }; + +/** + * Select the next node. + * @return {boolean} True if the action has been handled, false otherwise. + * @package + */ +Blockly.tree.BaseNode.prototype.selectNext = function() { + var nextNode = this.getNextShownNode(); + if (nextNode) { + nextNode.select(); + } + return true; +}; + +/** + * Select the previous node. + * @return {boolean} True if the action has been handled, false otherwise. + * @package + */ +Blockly.tree.BaseNode.prototype.selectPrevious = function() { + var previousNode = this.getPreviousShownNode(); + if (previousNode) { + previousNode.select(); + } + return true; +}; + +/** + * Select the parent node or collapse the current node. + * @return {boolean} True if the action has been handled, false otherwise. + * @package + */ +Blockly.tree.BaseNode.prototype.selectParent = function() { + if (this.hasChildren() && this.getExpanded() && this.isUserCollapsible_) { + this.setExpanded(false); + } else { + var parent = this.getParent(); + var tree = this.getTree(); + // don't go to root if hidden + if (parent && (parent != tree)) { + parent.select(); + } + } + return true; +}; + +/** + * Expand the current node if it's not already expanded, or select the + * child node. + * @return {boolean} True if the action has been handled, false otherwise. + * @package + */ +Blockly.tree.BaseNode.prototype.selectChild = function() { + if (this.hasChildren()) { + if (!this.getExpanded()) { + this.setExpanded(true); + } else { + this.getFirstChild().select(); + } + return true; + } + return false; +}; + /** * @return {Blockly.tree.BaseNode} The last shown descendant. * @protected @@ -1222,7 +1276,7 @@ Blockly.tree.BaseNode.prototype.getPreviousShownNode = function() { }; /** - * @return {Blockly.tree.BaseNode.Config} The configuration for the tree. + * @return {!Blockly.tree.BaseNode.Config} The configuration for the tree. * @protected */ Blockly.tree.BaseNode.prototype.getConfig = function() { diff --git a/core/components/tree/treecontrol.js b/core/components/tree/treecontrol.js index 62f7607c4..93cb55022 100644 --- a/core/components/tree/treecontrol.js +++ b/core/components/tree/treecontrol.js @@ -40,7 +40,7 @@ goog.require('Blockly.utils.style'); * Similar to Closure's goog.ui.tree.TreeControl * * @param {Blockly.Toolbox} toolbox The parent toolbox for this tree. - * @param {Blockly.tree.BaseNode.Config} config The configuration for the tree. + * @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree. * @constructor * @extends {Blockly.tree.BaseNode} */ diff --git a/core/components/tree/treenode.js b/core/components/tree/treenode.js index 1098a6531..5d4c9e0e4 100644 --- a/core/components/tree/treenode.js +++ b/core/components/tree/treenode.js @@ -39,7 +39,7 @@ goog.require('Blockly.utils.KeyCodes'); * @param {Blockly.Toolbox} toolbox The parent toolbox for this tree. * @param {string} content The content of the node label treated as * plain-text and will be HTML escaped. - * @param {Blockly.tree.BaseNode.Config} config The configuration for the tree. + * @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree. * @constructor * @extends {Blockly.tree.BaseNode} */ diff --git a/core/field_colour.js b/core/field_colour.js index 34ede9b86..49c1d4f6e 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -31,6 +31,7 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Field'); goog.require('Blockly.fieldRegistry'); +goog.require('Blockly.navigation'); goog.require('Blockly.utils.aria'); goog.require('Blockly.utils.colour'); goog.require('Blockly.utils.dom'); diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index 1079d17f1..a300e68a1 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -33,14 +33,6 @@ goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.user.keyMap'); -/** - * The current selected category if the toolbox is open or - * last selected category if focus is on a different element. - * @type {Blockly.tree.BaseNode} - * @private - */ -Blockly.navigation.currentCategory_ = null; - /** * A function to call to give feedback to the user about logs, warnings, and * errors. You can override this to customize feedback (e.g. warning sounds, @@ -154,94 +146,7 @@ Blockly.navigation.focusToolbox_ = function() { if (!Blockly.getMainWorkspace().getMarker().getCurNode()) { Blockly.navigation.markAtCursor_(); } - if (workspace && !Blockly.navigation.currentCategory_) { - Blockly.navigation.currentCategory_ = toolbox.tree_.firstChild_; - } - toolbox.tree_.setSelectedItem(Blockly.navigation.currentCategory_); -}; - -/** - * Select the next category. - * Taken from closure/goog/ui/tree/basenode.js - * @private - */ -Blockly.navigation.nextCategory_ = function() { - if (!Blockly.navigation.currentCategory_) { - return; - } - var curCategory = Blockly.navigation.currentCategory_; - var nextNode = curCategory.getNextShownNode(); - - if (nextNode) { - nextNode.select(); - Blockly.navigation.currentCategory_ = nextNode; - } -}; - -/** - * Select the previous category. - * Taken from closure/goog/ui/tree/basenode.js - * @private - */ -Blockly.navigation.previousCategory_ = function() { - if (!Blockly.navigation.currentCategory_) { - return; - } - var curCategory = Blockly.navigation.currentCategory_; - var previousNode = curCategory.getPreviousShownNode(); - - if (previousNode) { - previousNode.select(); - Blockly.navigation.currentCategory_ = previousNode; - } -}; - -/** - * Go to child category if there is a nested category. - * Taken from closure/goog/ui/tree/basenode.js - * @private - */ -Blockly.navigation.inCategory_ = function() { - if (!Blockly.navigation.currentCategory_) { - return; - } - var curCategory = Blockly.navigation.currentCategory_; - - if (curCategory.hasChildren()) { - if (!curCategory.getExpanded()) { - curCategory.setExpanded(true); - } else { - curCategory.getFirstChild().select(); - Blockly.navigation.currentCategory_ = curCategory.getFirstChild(); - } - } else { - Blockly.navigation.focusFlyout_(); - } -}; - -/** - * Go to parent category if we are in a child category. - * Taken from closure/goog/ui/tree/basenode.js - * @private - */ -Blockly.navigation.outCategory_ = function() { - if (!Blockly.navigation.currentCategory_) { - return; - } - var curCategory = Blockly.navigation.currentCategory_; - - if (curCategory.hasChildren() && curCategory.getExpanded() && curCategory.isUserCollapsible()) { - curCategory.setExpanded(false); - } else { - var parent = curCategory.getParent(); - var tree = curCategory.getTree(); - if (parent && parent != tree) { - parent.select(); - - Blockly.navigation.currentCategory_ = /** @type {Blockly.tree.BaseNode} */ - (parent); - } - } + toolbox.selectFirstCategory(); }; /***********************/ @@ -923,25 +828,18 @@ Blockly.navigation.flyoutOnAction_ = function(action) { * @private */ Blockly.navigation.toolboxOnAction_ = function(action) { - switch (action.name) { - case Blockly.navigation.actionNames.PREVIOUS: - Blockly.navigation.previousCategory_(); - return true; - case Blockly.navigation.actionNames.OUT: - Blockly.navigation.outCategory_(); - return true; - case Blockly.navigation.actionNames.NEXT: - Blockly.navigation.nextCategory_(); - return true; - case Blockly.navigation.actionNames.IN: - Blockly.navigation.inCategory_(); - return true; - case Blockly.navigation.actionNames.EXIT: - Blockly.navigation.focusWorkspace_(); - return true; - default: - return false; + if (action.name === Blockly.navigation.actionNames.EXIT) { + Blockly.navigation.focusWorkspace_(); + return true; } + var toolbox = Blockly.getMainWorkspace().getToolbox(); + var handled = toolbox.onBlocklyAction(action); + if (!handled && action.name === Blockly.navigation.actionNames.IN) { + Blockly.navigation.focusFlyout_(); + return true; + } + + return handled; }; /** diff --git a/core/theme.js b/core/theme.js index acca71a4a..e9fb6d66f 100644 --- a/core/theme.js +++ b/core/theme.js @@ -26,6 +26,20 @@ goog.provide('Blockly.Theme'); +/** + * Class for a theme. + * @param {!Object.} blockStyles A map from style + * names (strings) to objects with style attributes relating to blocks. + * @param {!Object.} categoryStyles A map from + * style names (strings) to objects with style attributes relating to + * categories. + * @constructor + */ +Blockly.Theme = function(blockStyles, categoryStyles) { + this.blockStyles_ = blockStyles; + this.categoryStyles_ = categoryStyles; +}; + /** * A block style. * @typedef {{ @@ -45,20 +59,6 @@ Blockly.Theme.BlockStyle; */ Blockly.Theme.CategoryStyle; -/** - * Class for a theme. - * @param {!Object.} blockStyles A map from style - * names (strings) to objects with style attributes relating to blocks. - * @param {!Object.} categoryStyles A map from - * style names (strings) to objects with style attributes relating to - * categories. - * @constructor - */ -Blockly.Theme = function(blockStyles, categoryStyles) { - this.blockStyles_ = blockStyles; - this.categoryStyles_ = categoryStyles; -}; - /** * Overrides or adds all values from blockStyles to blockStyles_ * @param {Object.} blockStyles Map of diff --git a/core/toolbox.js b/core/toolbox.js index b1c52677c..15443a0b2 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -30,6 +30,7 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.Ui'); goog.require('Blockly.Flyout'); goog.require('Blockly.HorizontalFlyout'); +goog.require('Blockly.navigation'); goog.require('Blockly.Touch'); goog.require('Blockly.tree.TreeControl'); goog.require('Blockly.tree.TreeNode'); @@ -77,7 +78,7 @@ Blockly.Toolbox = function(workspace) { /** * Configuration constants for Closure's tree UI. - * @type {Object.} + * @type {!Object.} * @private */ this.config_ = { @@ -96,7 +97,7 @@ Blockly.Toolbox = function(workspace) { /** * Configuration constants for tree separator. - * @type {Object.} + * @type {!Object.} * @private */ this.treeSeparatorConfig_ = { @@ -198,12 +199,16 @@ Blockly.Toolbox.prototype.init = function() { this.config_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif'; this.config_['cssCollapsedFolderIcon'] = 'blocklyTreeIconClosed' + (workspace.RTL ? 'Rtl' : 'Ltr'); - var tree = new Blockly.tree.TreeControl(this, this.config_); + var tree = new Blockly.tree.TreeControl(this, + /** @type {!Blockly.tree.BaseNode.Config} */ (this.config_)); this.tree_ = tree; tree.setSelectedItem(null); tree.onBeforeSelected(this.handleBeforeTreeSelected_); tree.onAfterSelected(this.handleAfterTreeSelected_); - var openNode = this.populate_(workspace.options.languageTree); + var openNode = null; + if (workspace.options.languageTree) { + openNode = this.populate_(workspace.options.languageTree); + } tree.render(this.HtmlDiv); if (openNode) { tree.setSelectedItem(openNode); @@ -214,7 +219,7 @@ Blockly.Toolbox.prototype.init = function() { // Trees have an implicit orientation of vertical, so we only need to set this // when the toolbox is in horizontal mode. if (this.horizontalLayout_) { - Blockly.utils.aria.setState(this.tree_.getElement(), + Blockly.utils.aria.setState(/** @type {!Element} */ (this.tree_.getElement()), Blockly.utils.aria.State.ORIENTATION, 'horizontal'); } }; @@ -281,6 +286,32 @@ Blockly.Toolbox.prototype.handleNodeSizeChanged_ = function() { Blockly.svgResize(this.workspace_); }; +/** + * Handles the given Blockly action on a toolbox. + * This is only triggered when keyboard accessibility mode is enabled. + * @param {!Blockly.Action} action The action to be handled. + * @return {boolean} True if the field handled the action, false otherwise. + * @package + */ +Blockly.Toolbox.prototype.onBlocklyAction = function(action) { + var selected = this.tree_.getSelectedItem(); + if (!selected) { + return false; + } + switch (action.name) { + case Blockly.navigation.actionNames.PREVIOUS: + return selected.selectPrevious(); + case Blockly.navigation.actionNames.OUT: + return selected.selectParent(); + case Blockly.navigation.actionNames.NEXT: + return selected.selectNext(); + case Blockly.navigation.actionNames.IN: + return selected.selectChild(); + default: + return false; + } +}; + /** * Dispose of this toolbox. */ @@ -344,7 +375,7 @@ Blockly.Toolbox.prototype.position = function() { /** * Fill the toolbox with categories and blocks. * @param {!Node} newTree DOM tree of blocks. - * @return {Node} Tree node to open at startup (or null). + * @return {Blockly.tree.BaseNode} Tree node to open at startup (or null). * @private */ Blockly.Toolbox.prototype.populate_ = function(newTree) { @@ -370,7 +401,7 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { * @param {!Blockly.tree.BaseNode} treeOut The TreeControl or TreeNode * object built from treeIn. * @param {string} pathToMedia The path to the Blockly media directory. - * @return {Node} Tree node to open at startup (or null). + * @return {Blockly.tree.BaseNode} Tree node to open at startup (or null). * @private */ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { @@ -432,7 +463,7 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { // Separator between two categories. // treeOut.add(new Blockly.Toolbox.TreeSeparator( - this.treeSeparatorConfig_)); + /** @type {!Blockly.tree.BaseNode.Config} */ (this.treeSeparatorConfig_))); break; } // Otherwise falls through. @@ -648,9 +679,20 @@ Blockly.Toolbox.prototype.refreshSelection = function() { } }; +/** + * Select the first toolbox category if no category is selected. + * @package + */ +Blockly.Toolbox.prototype.selectFirstCategory = function() { + var selectedItem = this.tree_.getSelectedItem(); + if (!selectedItem) { + this.tree_.selectFirst(); + } +}; + /** * A blank separator node in the tree. - * @param {Blockly.tree.BaseNode.Config} config The configuration for the tree. + * @param {!Blockly.tree.BaseNode.Config} config The configuration for the tree. * @constructor * @extends {Blockly.tree.TreeNode} */ diff --git a/tests/mocha/navigation_test.js b/tests/mocha/navigation_test.js index e6d8f468f..5a29f8d43 100644 --- a/tests/mocha/navigation_test.js +++ b/tests/mocha/navigation_test.js @@ -1,4 +1,40 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Navigation tests. + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + + suite('Navigation', function() { + function createNavigationWorkspace(enableKeyboardNav) { + var toolbox = document.getElementById('toolbox-categories'); + var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); + if (enableKeyboardNav) { + Blockly.navigation.enableKeyboardAccessibility(); + Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS; + } + return workspace; + } // Test that toolbox key handlers call through to the right functions and // transition correctly between toolbox, workspace, and flyout. @@ -15,8 +51,7 @@ suite('Navigation', function() { } ] }]); - var toolbox = document.getElementById('toolbox-categories'); - this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); + this.workspace = createNavigationWorkspace(true); Blockly.navigation.focusToolbox_(); this.mockEvent = { getModifierState: function() { @@ -31,7 +66,6 @@ suite('Navigation', function() { teardown(function() { delete Blockly.Blocks['basic_block']; this.workspace.dispose(); - Blockly.navigation.currentCategory_ = null; }); test('Next', function() { @@ -39,43 +73,45 @@ suite('Navigation', function() { chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX); - chai.assert.equal(Blockly.navigation.currentCategory_, + chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), this.secondCategory_); }); // Should be a no-op. test('Next at end', function() { - Blockly.navigation.nextCategory_(); + this.workspace.getToolbox().tree_.getSelectedItem().selectNext(); this.mockEvent.keyCode = Blockly.utils.KeyCodes.S; - var startCategory = Blockly.navigation.currentCategory_; + // Go forward one so that we can go back one. + Blockly.navigation.onKeyPress(this.mockEvent); + var startCategory = this.workspace.getToolbox().tree_.getSelectedItem(); chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX); - chai.assert.equal(Blockly.navigation.currentCategory_, + chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), startCategory); }); test('Previous', function() { // Go forward one so that we can go back one: - Blockly.navigation.nextCategory_(); + this.workspace.getToolbox().tree_.getSelectedItem().selectNext(); this.mockEvent.keyCode = Blockly.utils.KeyCodes.W; - chai.assert.equal(Blockly.navigation.currentCategory_, + chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), this.secondCategory_); chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX); - chai.assert.equal(Blockly.navigation.currentCategory_, + chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), this.firstCategory_); }); // Should be a no-op. test('Previous at start', function() { - var startCategory = Blockly.navigation.currentCategory_; + var startCategory = this.workspace.getToolbox().tree_.getSelectedItem(); this.mockEvent.keyCode = Blockly.utils.KeyCodes.W; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX); - chai.assert.equal(Blockly.navigation.currentCategory_, + chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), startCategory); }); @@ -132,8 +168,7 @@ suite('Navigation', function() { } ] }]); - var toolbox = document.getElementById('toolbox-categories'); - this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); + this.workspace = createNavigationWorkspace(true); Blockly.navigation.focusToolbox_(); Blockly.navigation.focusFlyout_(); this.mockEvent = { @@ -146,7 +181,6 @@ suite('Navigation', function() { teardown(function() { delete Blockly.Blocks['basic_block']; this.workspace.dispose(); - Blockly.navigation.currentCategory_ = null; }); // Should be a no-op @@ -225,9 +259,7 @@ suite('Navigation', function() { "previousStatement": null, "nextStatement": null }]); - var toolbox = document.getElementById('toolbox-categories'); - this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); - Blockly.navigation.enableKeyboardAccessibility(); + this.workspace = createNavigationWorkspace(true); this.basicBlock = this.workspace.newBlock('basic_block'); this.firstCategory_ = this.workspace.getToolbox().tree_.firstChild_; this.mockEvent = { @@ -240,7 +272,6 @@ suite('Navigation', function() { teardown(function() { delete Blockly.Blocks['basic_block']; this.workspace.dispose(); - Blockly.navigation.currentCategory_ = null; }); test('Previous', function() { @@ -310,7 +341,7 @@ suite('Navigation', function() { test('Toolbox', function() { this.mockEvent.keyCode = Blockly.utils.KeyCodes.T; chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent)); - chai.assert.equal(Blockly.navigation.currentCategory_, this.firstCategory_); + chai.assert.equal(this.workspace.getToolbox().tree_.getSelectedItem(), this.firstCategory_); chai.assert.equal(Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX); }); @@ -322,6 +353,7 @@ suite('Navigation', function() { Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap()); Blockly.mainWorkspace = this.workspace; Blockly.keyboardAccessibilityMode = true; + Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS; this.mockEvent = { getModifierState: function() { @@ -330,7 +362,9 @@ suite('Navigation', function() { }; }); test('Action does not exist', function() { + var block = new Blockly.Block(this.workspace); var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]); + field.setSourceBlock(block); sinon.spy(field, 'onBlocklyAction'); this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field)); @@ -343,7 +377,9 @@ suite('Navigation', function() { }); test('Action exists - field handles action', function() { + var block = new Blockly.Block(this.workspace); var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]); + field.setSourceBlock(block); sinon.stub(field, 'onBlocklyAction').callsFake(function(){ return true; }); @@ -357,7 +393,9 @@ suite('Navigation', function() { }); test('Action exists - field does not handle action', function() { + var block = new Blockly.Block(this.workspace); var field = new Blockly.FieldDropdown([['a','b'], ['c','d']]); + field.setSourceBlock(block); sinon.spy(field, 'onBlocklyAction'); this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field)); @@ -424,8 +462,10 @@ suite('Navigation', function() { this.workspace = new Blockly.Workspace({readOnly: true}); this.workspace.setCursor(new Blockly.Cursor()); Blockly.mainWorkspace = this.workspace; - this.fieldBlock1 = this.workspace.newBlock('field_block'); Blockly.keyboardAccessibilityMode = true; + Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS; + + this.fieldBlock1 = this.workspace.newBlock('field_block'); this.mockEvent = { getModifierState: function() { return false; @@ -435,7 +475,6 @@ suite('Navigation', function() { teardown(function() { delete Blockly.Blocks['field_block']; - Blockly.mainWorkspace = null; this.workspace.dispose(); }); @@ -472,8 +511,7 @@ suite('Navigation', function() { "nextStatement": null, }]); - var toolbox = document.getElementById('toolbox-categories'); - this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); + this.workspace = createNavigationWorkspace(true); var basicBlock = this.workspace.newBlock('basic_block'); var basicBlock2 = this.workspace.newBlock('basic_block'); @@ -485,7 +523,6 @@ suite('Navigation', function() { teardown(function() { delete Blockly.Blocks['basic_block']; this.workspace.dispose(); - Blockly.navigation.currentCategory_ = null; }); test('Insert from flyout with a valid connection marked', function() { @@ -562,8 +599,7 @@ suite('Navigation', function() { "helpUrl": "" }]); - var toolbox = document.getElementById('toolbox-categories'); - this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); + this.workspace = createNavigationWorkspace(true); var basicBlock = this.workspace.newBlock('basic_block'); var basicBlock2 = this.workspace.newBlock('basic_block'); @@ -592,7 +628,6 @@ suite('Navigation', function() { teardown(function() { delete Blockly.Blocks['basic_block']; this.workspace.dispose(); - Blockly.navigation.currentCategory_ = null; }); test('Connect cursor on previous into stack', function() { @@ -655,9 +690,7 @@ suite('Navigation', function() { "previousStatement": null, "nextStatement": null, }]); - var toolbox = document.getElementById('toolbox-categories'); - this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox}); - Blockly.navigation.enableKeyboardAccessibility(); + this.workspace = createNavigationWorkspace(true); this.basicBlockA = this.workspace.newBlock('basic_block'); this.basicBlockB = this.workspace.newBlock('basic_block'); }); diff --git a/tests/mocha/xml_procedures_test.js b/tests/mocha/xml_procedures_test.js index f720c9f82..b18006fdf 100644 --- a/tests/mocha/xml_procedures_test.js +++ b/tests/mocha/xml_procedures_test.js @@ -19,7 +19,7 @@ */ goog.require('Blockly.Blocks.procedures'); -goog.require('Blockly.Msg.en'); +goog.require('Blockly.Msg'); suite('Procedures XML', function() { suite('Deserialization', function() {