mirror of
https://github.com/google/blockly.git
synced 2026-01-05 08:00:09 +01:00
feat: add more navigation shortcuts (#9523)
This commit is contained in:
@@ -15,6 +15,7 @@ import {isCopyable as isICopyable} from './interfaces/i_copyable.js';
|
||||
import {isDeletable as isIDeletable} from './interfaces/i_deletable.js';
|
||||
import {isDraggable} from './interfaces/i_draggable.js';
|
||||
import {IFocusableNode} from './interfaces/i_focusable_node.js';
|
||||
import {RenderedConnection} from './rendered_connection.js';
|
||||
import {KeyboardShortcut, ShortcutRegistry} from './shortcut_registry.js';
|
||||
import {aria} from './utils.js';
|
||||
import {Coordinate} from './utils/coordinate.js';
|
||||
@@ -36,6 +37,12 @@ export enum names {
|
||||
REDO = 'redo',
|
||||
READ_FULL_BLOCK_SUMMARY = 'read_full_block_summary',
|
||||
READ_BLOCK_PARENT_SUMMARY = 'read_block_parent_summary',
|
||||
JUMP_TOP_STACK = 'jump_to_top_of_stack',
|
||||
JUMP_BOTTOM_STACK = 'jump_to_bottom_of_stack',
|
||||
JUMP_BLOCK_START = 'jump_to_block_start',
|
||||
JUMP_BLOCK_END = 'jump_to_block_end',
|
||||
JUMP_FIRST_BLOCK = 'jump_to_first_block',
|
||||
JUMP_LAST_BLOCK = 'jump_to_last_block',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,6 +396,20 @@ export function registerRedo() {
|
||||
ShortcutRegistry.registry.register(redoShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* PreconditionFn that returns true if the focused thing is a block or
|
||||
* belongs to a block (such as field, icon, etc.)
|
||||
*/
|
||||
const focusedNodeHasBlockParent = function (workspace: WorkspaceSvg) {
|
||||
return (
|
||||
!workspace.isDragging() &&
|
||||
!getFocusManager().ephemeralFocusTaken() &&
|
||||
!!getFocusManager().getFocusedNode() &&
|
||||
// Either a block or something that has a parent block is focused
|
||||
!!workspace.getCursor().getSourceBlock()
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut for re-reading the current selected block's
|
||||
* summary with additional verbosity to help provide context on where the user
|
||||
@@ -400,15 +421,7 @@ export function registerRedo() {
|
||||
export function registerReadFullBlockSummary() {
|
||||
const readFullBlockSummaryShortcut: KeyboardShortcut = {
|
||||
name: names.READ_FULL_BLOCK_SUMMARY,
|
||||
preconditionFn(workspace) {
|
||||
return (
|
||||
!workspace.isDragging() &&
|
||||
!getFocusManager().ephemeralFocusTaken() &&
|
||||
!!getFocusManager().getFocusedNode() &&
|
||||
// Either a block or something that has a parent block is focused
|
||||
!!workspace.getCursor().getSourceBlock()
|
||||
);
|
||||
},
|
||||
preconditionFn: focusedNodeHasBlockParent,
|
||||
callback(workspace, e) {
|
||||
const selectedBlock = workspace.getCursor().getSourceBlock();
|
||||
if (!selectedBlock) return false;
|
||||
@@ -433,15 +446,7 @@ export function registerReadBlockParentSummary() {
|
||||
]);
|
||||
const readBlockParentSummaryShortcut: KeyboardShortcut = {
|
||||
name: names.READ_BLOCK_PARENT_SUMMARY,
|
||||
preconditionFn(workspace) {
|
||||
return (
|
||||
!workspace.isDragging() &&
|
||||
!getFocusManager().ephemeralFocusTaken() &&
|
||||
!!getFocusManager().getFocusedNode() &&
|
||||
// Either a block or something that has a parent block is focused
|
||||
!!workspace.getCursor().getSourceBlock()
|
||||
);
|
||||
},
|
||||
preconditionFn: focusedNodeHasBlockParent,
|
||||
callback(workspace, e) {
|
||||
const selectedBlock = workspace.getCursor().getSourceBlock();
|
||||
if (!selectedBlock) return false;
|
||||
@@ -460,6 +465,166 @@ export function registerReadBlockParentSummary() {
|
||||
ShortcutRegistry.registry.register(readBlockParentSummaryShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut that sets the focus to the block
|
||||
* that owns the current focused node.
|
||||
*/
|
||||
export function registerJumpBlockStart() {
|
||||
const jumpBlockStartShortcut: KeyboardShortcut = {
|
||||
name: names.JUMP_BLOCK_START,
|
||||
preconditionFn: (workspace) => {
|
||||
return !workspace.isFlyout && focusedNodeHasBlockParent(workspace);
|
||||
},
|
||||
callback(workspace) {
|
||||
const selectedBlock = workspace.getCursor().getSourceBlock();
|
||||
if (!selectedBlock) return false;
|
||||
getFocusManager().focusNode(selectedBlock);
|
||||
return true;
|
||||
},
|
||||
keyCodes: [KeyCodes.HOME],
|
||||
};
|
||||
ShortcutRegistry.registry.register(jumpBlockStartShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut that sets the focus to the
|
||||
* last input of the block that owns the current focused node.
|
||||
*/
|
||||
export function registerJumpBlockEnd() {
|
||||
const jumpBlockEndShortcut: KeyboardShortcut = {
|
||||
name: names.JUMP_BLOCK_END,
|
||||
preconditionFn: (workspace) => {
|
||||
return !workspace.isFlyout && focusedNodeHasBlockParent(workspace);
|
||||
},
|
||||
callback(workspace) {
|
||||
const selectedBlock = workspace.getCursor().getSourceBlock();
|
||||
if (!selectedBlock) return false;
|
||||
const inputs = selectedBlock.inputList;
|
||||
if (!inputs.length) return false;
|
||||
const connection = inputs[inputs.length - 1].connection;
|
||||
if (!connection || !(connection instanceof RenderedConnection))
|
||||
return false;
|
||||
getFocusManager().focusNode(connection);
|
||||
return true;
|
||||
},
|
||||
keyCodes: [KeyCodes.END],
|
||||
};
|
||||
ShortcutRegistry.registry.register(jumpBlockEndShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut that sets the focus to the top block
|
||||
* in the current stack.
|
||||
*/
|
||||
export function registerJumpTopStack() {
|
||||
const jumpTopStackShortcut: KeyboardShortcut = {
|
||||
name: names.JUMP_TOP_STACK,
|
||||
preconditionFn: (workspace) => {
|
||||
return !workspace.isFlyout && focusedNodeHasBlockParent(workspace);
|
||||
},
|
||||
callback(workspace) {
|
||||
const selectedBlock = workspace.getCursor().getSourceBlock();
|
||||
if (!selectedBlock) return false;
|
||||
const topOfStack = selectedBlock.getRootBlock();
|
||||
getFocusManager().focusNode(topOfStack);
|
||||
return true;
|
||||
},
|
||||
keyCodes: [KeyCodes.PAGE_UP],
|
||||
};
|
||||
ShortcutRegistry.registry.register(jumpTopStackShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut that sets the focus to the bottom block
|
||||
* in the current stack.
|
||||
*/
|
||||
export function registerJumpBottomStack() {
|
||||
const jumpBottomStackShortcut: KeyboardShortcut = {
|
||||
name: names.JUMP_BOTTOM_STACK,
|
||||
preconditionFn: (workspace) => {
|
||||
return !workspace.isFlyout && focusedNodeHasBlockParent(workspace);
|
||||
},
|
||||
callback(workspace) {
|
||||
const selectedBlock = workspace.getCursor().getSourceBlock();
|
||||
if (!selectedBlock) return false;
|
||||
// To get the bottom block in a stack, first go to the top of the stack
|
||||
// Then get the last next connection
|
||||
// Then get the last descendant of that block
|
||||
const lastBlock = selectedBlock
|
||||
.getRootBlock()
|
||||
.lastConnectionInStack(false)
|
||||
?.getSourceBlock();
|
||||
if (!lastBlock) return false;
|
||||
const descendants = lastBlock.getDescendants(true);
|
||||
const bottomOfStack = descendants[descendants.length - 1];
|
||||
getFocusManager().focusNode(bottomOfStack);
|
||||
return true;
|
||||
},
|
||||
keyCodes: [KeyCodes.PAGE_DOWN],
|
||||
};
|
||||
ShortcutRegistry.registry.register(jumpBottomStackShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut that sets the focus to the first
|
||||
* block in the workspace.
|
||||
*/
|
||||
export function registerJumpFirstBlock() {
|
||||
const ctrlHome = ShortcutRegistry.registry.createSerializedKey(
|
||||
KeyCodes.HOME,
|
||||
[KeyCodes.CTRL],
|
||||
);
|
||||
const metaHome = ShortcutRegistry.registry.createSerializedKey(
|
||||
KeyCodes.HOME,
|
||||
[KeyCodes.META],
|
||||
);
|
||||
const jumpFirstBlockShortcut: KeyboardShortcut = {
|
||||
name: names.JUMP_FIRST_BLOCK,
|
||||
preconditionFn: (workspace) => {
|
||||
return (
|
||||
!workspace.isDragging() && !getFocusManager().ephemeralFocusTaken()
|
||||
);
|
||||
},
|
||||
callback(workspace) {
|
||||
const topBlocks = workspace.getTopBlocks(true);
|
||||
if (!topBlocks.length) return false;
|
||||
getFocusManager().focusNode(topBlocks[0]);
|
||||
return true;
|
||||
},
|
||||
keyCodes: [ctrlHome, metaHome],
|
||||
};
|
||||
ShortcutRegistry.registry.register(jumpFirstBlockShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut that sets the focus to the last
|
||||
* block in the workspace.
|
||||
*/
|
||||
export function registerJumpLastBlock() {
|
||||
const ctrlEnd = ShortcutRegistry.registry.createSerializedKey(KeyCodes.END, [
|
||||
KeyCodes.CTRL,
|
||||
]);
|
||||
const metaEnd = ShortcutRegistry.registry.createSerializedKey(KeyCodes.END, [
|
||||
KeyCodes.META,
|
||||
]);
|
||||
const jumpLastBlockShortcut: KeyboardShortcut = {
|
||||
name: names.JUMP_LAST_BLOCK,
|
||||
preconditionFn: (workspace) => {
|
||||
return (
|
||||
!workspace.isDragging() && !getFocusManager().ephemeralFocusTaken()
|
||||
);
|
||||
},
|
||||
callback(workspace) {
|
||||
const allBlocks = workspace.getAllBlocks(true);
|
||||
if (!allBlocks.length) return false;
|
||||
getFocusManager().focusNode(allBlocks[allBlocks.length - 1]);
|
||||
return true;
|
||||
},
|
||||
keyCodes: [ctrlEnd, metaEnd],
|
||||
};
|
||||
ShortcutRegistry.registry.register(jumpLastBlockShortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all default keyboard shortcut item. This should be called once per
|
||||
* instance of KeyboardShortcutRegistry.
|
||||
@@ -476,6 +641,12 @@ export function registerDefaultShortcuts() {
|
||||
registerRedo();
|
||||
registerReadFullBlockSummary();
|
||||
registerReadBlockParentSummary();
|
||||
registerJumpTopStack();
|
||||
registerJumpBottomStack();
|
||||
registerJumpBlockStart();
|
||||
registerJumpBlockEnd();
|
||||
registerJumpFirstBlock();
|
||||
registerJumpLastBlock();
|
||||
}
|
||||
|
||||
registerDefaultShortcuts();
|
||||
|
||||
@@ -560,4 +560,363 @@ suite('Keyboard Shortcut Items', function () {
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
const blockJson = {
|
||||
'blocks': {
|
||||
'languageVersion': 0,
|
||||
'blocks': [
|
||||
{
|
||||
'type': 'controls_repeat_ext',
|
||||
'id': 'controls_repeat_1',
|
||||
'x': 63,
|
||||
'y': 88,
|
||||
'inputs': {
|
||||
'TIMES': {
|
||||
'shadow': {
|
||||
'type': 'math_number',
|
||||
'id': 'math_number_1',
|
||||
'fields': {
|
||||
'NUM': 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
'DO': {
|
||||
'block': {
|
||||
'type': 'controls_forEach',
|
||||
'id': 'controls_forEach_1',
|
||||
'fields': {
|
||||
'VAR': {
|
||||
'id': '/wU7DoTDScBz~6hbq-[E',
|
||||
},
|
||||
},
|
||||
'inputs': {
|
||||
'LIST': {
|
||||
'block': {
|
||||
'type': 'lists_repeat',
|
||||
'id': 'lists_repeat_1',
|
||||
'inputs': {
|
||||
'ITEM': {
|
||||
'block': {
|
||||
'type': 'lists_getIndex',
|
||||
'id': 'lists_getIndex_1',
|
||||
'fields': {
|
||||
'MODE': 'GET',
|
||||
'WHERE': 'FROM_START',
|
||||
},
|
||||
'inputs': {
|
||||
'VALUE': {
|
||||
'block': {
|
||||
'type': 'variables_get',
|
||||
'id': 'Lhk_B9iVsV%BhhJ%h]m$',
|
||||
'fields': {
|
||||
'VAR': {
|
||||
'id': '.*~ZjUJ#Sua{h6xyVp7`',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'NUM': {
|
||||
'shadow': {
|
||||
'type': 'math_number',
|
||||
'id': 'math_number_2',
|
||||
'fields': {
|
||||
'NUM': 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
'type': 'controls_forEach',
|
||||
'id': 'controls_forEach_2',
|
||||
'x': 63,
|
||||
'y': 288,
|
||||
'fields': {
|
||||
'VAR': {
|
||||
'id': '+rcR|2HqfZ=vK}N8L{RU',
|
||||
},
|
||||
},
|
||||
'inputs': {
|
||||
'DO': {
|
||||
'block': {
|
||||
'type': 'controls_repeat_ext',
|
||||
'id': 'controls_repeat_2',
|
||||
'inputs': {
|
||||
'TIMES': {
|
||||
'shadow': {
|
||||
'type': 'math_number',
|
||||
'id': 'math_number_3',
|
||||
'fields': {
|
||||
'NUM': 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'next': {
|
||||
'block': {
|
||||
'type': 'text_print',
|
||||
'id': 'text_print_1',
|
||||
'inputs': {
|
||||
'TEXT': {
|
||||
'block': {
|
||||
'type': 'text',
|
||||
'id': 'text_1',
|
||||
'fields': {
|
||||
'TEXT': 'last block inside a loop',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'next': {
|
||||
'block': {
|
||||
'type': 'text_print',
|
||||
'id': 'text_print_2',
|
||||
'inputs': {
|
||||
'TEXT': {
|
||||
'block': {
|
||||
'type': 'text',
|
||||
'id': 'text_2',
|
||||
'fields': {
|
||||
'TEXT': 'last block on workspace',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
suite('Jump shortcuts', function () {
|
||||
setup(function () {
|
||||
this.getFocusedNodeStub = sinon.stub(
|
||||
Blockly.getFocusManager(),
|
||||
'getFocusedNode',
|
||||
);
|
||||
this.focusNodeSpy = sinon.stub(Blockly.getFocusManager(), 'focusNode');
|
||||
Blockly.serialization.workspaces.load(blockJson, this.workspace);
|
||||
});
|
||||
|
||||
test('Home focuses current block if block is focused', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, inListBlock);
|
||||
});
|
||||
|
||||
test('Home focuses owning block if field is focused', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
const fieldToFocus = inListBlock.getField('MODE');
|
||||
this.getFocusedNodeStub.returns(fieldToFocus);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, inListBlock);
|
||||
});
|
||||
|
||||
test('End focuses last input on owning block', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
const fieldToFocus = inListBlock.getField('MODE');
|
||||
this.getFocusedNodeStub.returns(fieldToFocus);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END),
|
||||
);
|
||||
const expectedFocus = inListBlock.getInput('AT').connection;
|
||||
sinon.assert.calledWith(this.focusNodeSpy, expectedFocus);
|
||||
});
|
||||
|
||||
test('End has no effect if block has no inputs', function () {
|
||||
const textBlock = this.workspace.getBlockById('text_1');
|
||||
this.getFocusedNodeStub.returns(textBlock);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END),
|
||||
);
|
||||
sinon.assert.notCalled(this.focusNodeSpy);
|
||||
});
|
||||
|
||||
test('CtrlHome focuses top block in workspace if block is focused', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
const topBlock = this.workspace.getBlockById('controls_repeat_1');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, topBlock);
|
||||
});
|
||||
|
||||
test('CtrlHome focuses top block in workspace if field is focused', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
const fieldToFocus = inListBlock.getField('MODE');
|
||||
this.getFocusedNodeStub.returns(fieldToFocus);
|
||||
const topBlock = this.workspace.getBlockById('controls_repeat_1');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, topBlock);
|
||||
});
|
||||
|
||||
test('CtrlHome focuses top block in workspace if workspace is focused', function () {
|
||||
this.getFocusedNodeStub.returns(this.workspace);
|
||||
const topBlock = this.workspace.getBlockById('controls_repeat_1');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, topBlock);
|
||||
});
|
||||
|
||||
test('CtrlEnd focuses last block in workspace if block is focused', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
const lastBlock = this.workspace.getBlockById('text_2');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, lastBlock);
|
||||
});
|
||||
|
||||
test('CtrlEnd focuses last block in workspace if field is focused', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
const fieldToFocus = inListBlock.getField('MODE');
|
||||
this.getFocusedNodeStub.returns(fieldToFocus);
|
||||
const lastBlock = this.workspace.getBlockById('text_2');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, lastBlock);
|
||||
});
|
||||
|
||||
test('CtrlEnd focuses last block in workspace if workspace is focused', function () {
|
||||
this.getFocusedNodeStub.returns(this.workspace);
|
||||
const lastBlock = this.workspace.getBlockById('text_2');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, lastBlock);
|
||||
});
|
||||
|
||||
test('PageUp focuses on first block in stack', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
const fieldToFocus = inListBlock.getField('MODE');
|
||||
this.getFocusedNodeStub.returns(fieldToFocus);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.PAGE_UP),
|
||||
);
|
||||
const expectedFocus = this.workspace.getBlockById('controls_repeat_1');
|
||||
sinon.assert.calledWith(this.focusNodeSpy, expectedFocus);
|
||||
});
|
||||
|
||||
test('PageDown focuses on last block in stack with nested row blocks', function () {
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
const fieldToFocus = inListBlock.getField('MODE');
|
||||
this.getFocusedNodeStub.returns(fieldToFocus);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.PAGE_DOWN),
|
||||
);
|
||||
const expectedFocus = this.workspace.getBlockById('math_number_2');
|
||||
sinon.assert.calledWith(this.focusNodeSpy, expectedFocus);
|
||||
});
|
||||
|
||||
test('PageDown focuses on last block in stack with many stack blocks', function () {
|
||||
const blockToFocus = this.workspace.getBlockById('text_1');
|
||||
this.getFocusedNodeStub.returns(blockToFocus);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.PAGE_DOWN),
|
||||
);
|
||||
const expectedFocus = this.workspace.getBlockById('text_2');
|
||||
sinon.assert.calledWith(this.focusNodeSpy, expectedFocus);
|
||||
});
|
||||
|
||||
suite('in flyout', function () {
|
||||
test('Home has no effect', function () {
|
||||
this.workspace.internalIsFlyout = true;
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME),
|
||||
);
|
||||
sinon.assert.notCalled(this.focusNodeSpy);
|
||||
});
|
||||
test('End has no effect', function () {
|
||||
this.workspace.internalIsFlyout = true;
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END),
|
||||
);
|
||||
sinon.assert.notCalled(this.focusNodeSpy);
|
||||
});
|
||||
test('CtrlHome focuses top block in flyout workspace', function () {
|
||||
this.workspace.internalIsFlyout = true;
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
const topBlock = this.workspace.getBlockById('controls_repeat_1');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.HOME, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, topBlock);
|
||||
});
|
||||
test('CtrlEnd focuses last block in flyout workspace', function () {
|
||||
this.workspace.internalIsFlyout = true;
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
const lastBlock = this.workspace.getBlockById('text_2');
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.END, [
|
||||
Blockly.utils.KeyCodes.CTRL,
|
||||
]),
|
||||
);
|
||||
sinon.assert.calledWith(this.focusNodeSpy, lastBlock);
|
||||
});
|
||||
test('PageUp has no effect', function () {
|
||||
this.workspace.internalIsFlyout = true;
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.PAGE_UP),
|
||||
);
|
||||
sinon.assert.notCalled(this.focusNodeSpy);
|
||||
});
|
||||
test('PageDown has no effect', function () {
|
||||
this.workspace.internalIsFlyout = true;
|
||||
const inListBlock = this.workspace.getBlockById('lists_getIndex_1');
|
||||
this.getFocusedNodeStub.returns(inListBlock);
|
||||
this.injectionDiv.dispatchEvent(
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.PAGE_DOWN),
|
||||
);
|
||||
sinon.assert.notCalled(this.focusNodeSpy);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user