mirror of
https://github.com/google/blockly.git
synced 2026-04-27 23:50:21 +02:00
Merge pull request #9592 from RaspberryPiFoundation/rollup
chore: Merge `main` into `v13`
This commit is contained in:
Generated
+1011
-1038
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,10 @@
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^20.1.0",
|
||||
"@commitlint/config-conventional": "^20.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run test --ws --if-present",
|
||||
"lint": "npm run lint --ws --if-present",
|
||||
|
||||
@@ -354,8 +354,8 @@ export class BlockSvg
|
||||
* @returns Object with .x and .y properties in workspace coordinates.
|
||||
*/
|
||||
override getRelativeToSurfaceXY(): Coordinate {
|
||||
const layerManger = this.workspace.getLayerManager();
|
||||
if (!layerManger) {
|
||||
const layerManager = this.workspace.getLayerManager();
|
||||
if (!layerManager) {
|
||||
throw new Error(
|
||||
'Cannot calculate position because the workspace has not been appended',
|
||||
);
|
||||
@@ -371,7 +371,7 @@ export class BlockSvg
|
||||
x += xy.x;
|
||||
y += xy.y;
|
||||
element = element.parentNode as SVGElement;
|
||||
} while (element && !layerManger.hasLayer(element));
|
||||
} while (element && !layerManager.hasLayer(element));
|
||||
}
|
||||
return new Coordinate(x, y);
|
||||
}
|
||||
@@ -1133,7 +1133,9 @@ export class BlockSvg
|
||||
if (this.isDeadOrDying()) return;
|
||||
const gesture = this.workspace.getGesture(e);
|
||||
if (gesture) {
|
||||
this.bringToFront();
|
||||
gesture.setStartIcon(icon);
|
||||
getFocusManager().focusNode(icon);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,7 +93,14 @@ let content = `
|
||||
|
||||
.blocklyBlockCanvas.blocklyCanvasTransitioning,
|
||||
.blocklyBubbleCanvas.blocklyCanvasTransitioning {
|
||||
transition: transform .5s;
|
||||
transition: transform .15s;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
.blocklyBlockCanvas.blocklyCanvasTransitioning,
|
||||
.blocklyBubbleCanvas.blocklyCanvasTransitioning {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.blocklyEmboss {
|
||||
|
||||
@@ -38,7 +38,6 @@ import * as dom from './utils/dom.js';
|
||||
import * as idGenerator from './utils/idgenerator.js';
|
||||
import {Svg} from './utils/svg.js';
|
||||
import * as toolbox from './utils/toolbox.js';
|
||||
import * as Variables from './variables.js';
|
||||
import {WorkspaceSvg} from './workspace_svg.js';
|
||||
|
||||
/**
|
||||
@@ -813,44 +812,23 @@ export abstract class Flyout
|
||||
* @internal
|
||||
*/
|
||||
createBlock(originalBlock: BlockSvg): BlockSvg {
|
||||
let newBlock = null;
|
||||
eventUtils.disable();
|
||||
const variablesBeforeCreation = this.targetWorkspace
|
||||
.getVariableMap()
|
||||
.getAllVariables();
|
||||
this.targetWorkspace.setResizesEnabled(false);
|
||||
try {
|
||||
newBlock = this.placeNewBlock(originalBlock);
|
||||
} finally {
|
||||
eventUtils.enable();
|
||||
const targetWorkspace = this.targetWorkspace;
|
||||
const svgRootOld = originalBlock.getSvgRoot();
|
||||
if (!svgRootOld) {
|
||||
throw Error('oldBlock is not rendered');
|
||||
}
|
||||
|
||||
// Close the flyout.
|
||||
this.targetWorkspace.hideChaff();
|
||||
// Clone the block.
|
||||
const json = this.serializeBlock(originalBlock);
|
||||
// Normally this resizes leading to weird jumps. Save it for terminateDrag.
|
||||
targetWorkspace.setResizesEnabled(false);
|
||||
const block = blocks.appendInternal(json, targetWorkspace, {
|
||||
recordUndo: true,
|
||||
}) as BlockSvg;
|
||||
|
||||
const newVariables = Variables.getAddedVariables(
|
||||
this.targetWorkspace,
|
||||
variablesBeforeCreation,
|
||||
);
|
||||
|
||||
if (eventUtils.isEnabled()) {
|
||||
eventUtils.setGroup(true);
|
||||
// Fire a VarCreate event for each (if any) new variable created.
|
||||
for (let i = 0; i < newVariables.length; i++) {
|
||||
const thisVariable = newVariables[i];
|
||||
eventUtils.fire(
|
||||
new (eventUtils.get(EventType.VAR_CREATE))(thisVariable),
|
||||
);
|
||||
}
|
||||
|
||||
// Block events come after var events, in case they refer to newly created
|
||||
// variables.
|
||||
eventUtils.fire(new (eventUtils.get(EventType.BLOCK_CREATE))(newBlock));
|
||||
}
|
||||
if (this.autoClose) {
|
||||
this.hide();
|
||||
}
|
||||
return newBlock;
|
||||
this.positionNewBlock(originalBlock, block);
|
||||
targetWorkspace.hideChaff();
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -873,30 +851,6 @@ export abstract class Flyout
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a block from the flyout to the workspace and position it correctly.
|
||||
*
|
||||
* @param oldBlock The flyout block to copy.
|
||||
* @returns The new block in the main workspace.
|
||||
*/
|
||||
private placeNewBlock(oldBlock: BlockSvg): BlockSvg {
|
||||
const targetWorkspace = this.targetWorkspace;
|
||||
const svgRootOld = oldBlock.getSvgRoot();
|
||||
if (!svgRootOld) {
|
||||
throw Error('oldBlock is not rendered');
|
||||
}
|
||||
|
||||
// Clone the block.
|
||||
const json = this.serializeBlock(oldBlock);
|
||||
// Normally this resizes leading to weird jumps. Save it for terminateDrag.
|
||||
targetWorkspace.setResizesEnabled(false);
|
||||
const block = blocks.append(json, targetWorkspace) as BlockSvg;
|
||||
|
||||
this.positionNewBlock(oldBlock, block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a block to JSON.
|
||||
*
|
||||
|
||||
@@ -427,7 +427,13 @@ Css.register(`
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
.blocklyFlyoutButton:hover {
|
||||
@media (hover: hover) {
|
||||
.blocklyFlyoutButton:hover {
|
||||
fill: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.blocklyFlyoutButton:active {
|
||||
fill: #aaa;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import * as eventUtils from './events/utils.js';
|
||||
import type {Field} from './field.js';
|
||||
import {getFocusManager} from './focus_manager.js';
|
||||
import type {IBubble} from './interfaces/i_bubble.js';
|
||||
import {hasContextMenu} from './interfaces/i_contextmenu.js';
|
||||
import {IDraggable, isDraggable} from './interfaces/i_draggable.js';
|
||||
import {IDragger} from './interfaces/i_dragger.js';
|
||||
import type {IFlyout} from './interfaces/i_flyout.js';
|
||||
@@ -273,24 +274,19 @@ export class Gesture {
|
||||
throw new Error(`Cannot update dragging from the flyout because the ' +
|
||||
'flyout's target workspace is undefined`);
|
||||
}
|
||||
if (
|
||||
!this.flyout.isScrollable() ||
|
||||
this.flyout.isDragTowardWorkspace(this.currentDragDeltaXY)
|
||||
) {
|
||||
this.startWorkspace_ = this.flyout.targetWorkspace;
|
||||
this.startWorkspace_.updateScreenCalculationsIfScrolled();
|
||||
// Start the event group now, so that the same event group is used for
|
||||
// block creation and block dragging.
|
||||
if (!eventUtils.getGroup()) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
// The start block is no longer relevant, because this is a drag.
|
||||
this.startBlock = null;
|
||||
this.targetBlock = this.flyout.createBlock(this.targetBlock);
|
||||
getFocusManager().focusNode(this.targetBlock);
|
||||
return true;
|
||||
|
||||
this.startWorkspace_ = this.flyout.targetWorkspace;
|
||||
this.startWorkspace_.updateScreenCalculationsIfScrolled();
|
||||
// Start the event group now, so that the same event group is used for
|
||||
// block creation and block dragging.
|
||||
if (!eventUtils.getGroup()) {
|
||||
eventUtils.setGroup(true);
|
||||
}
|
||||
return false;
|
||||
// The start block is no longer relevant, because this is a drag.
|
||||
this.startBlock = null;
|
||||
this.targetBlock = this.flyout.createBlock(this.targetBlock);
|
||||
getFocusManager().focusNode(this.targetBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -732,22 +728,12 @@ export class Gesture {
|
||||
* @internal
|
||||
*/
|
||||
handleRightClick(e: PointerEvent) {
|
||||
if (this.targetBlock) {
|
||||
this.bringBlockToFront();
|
||||
this.targetBlock.workspace.hideChaff(!!this.flyout);
|
||||
this.targetBlock.showContextMenu(e);
|
||||
} else if (this.startBubble) {
|
||||
this.startBubble.showContextMenu(e);
|
||||
} else if (this.startComment) {
|
||||
this.startComment.workspace.hideChaff();
|
||||
this.startComment.showContextMenu(e);
|
||||
} else if (this.startWorkspace_ && !this.flyout) {
|
||||
this.startWorkspace_.hideChaff();
|
||||
getFocusManager().focusNode(this.startWorkspace_);
|
||||
this.startWorkspace_.showContextMenu(e);
|
||||
const selection = getFocusManager().getFocusedNode();
|
||||
if (hasContextMenu(selection)) {
|
||||
this.startWorkspace_?.hideChaff(!!this.flyout);
|
||||
selection.showContextMenu(e);
|
||||
}
|
||||
|
||||
// TODO: Handle right-click on a bubble.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -773,7 +759,12 @@ export class Gesture {
|
||||
this.setStartWorkspace(ws);
|
||||
this.mostRecentEvent = e;
|
||||
|
||||
if (!this.targetBlock && !this.startBubble && !this.startComment) {
|
||||
if (
|
||||
!this.targetBlock &&
|
||||
!this.startBubble &&
|
||||
!this.startComment &&
|
||||
!this.startIcon
|
||||
) {
|
||||
// Ensure the workspace is selected if nothing else should be. Note that
|
||||
// this is focusNode() instead of focusTree() because if any active node
|
||||
// is focused in the workspace it should be defocused.
|
||||
@@ -878,12 +869,6 @@ export class Gesture {
|
||||
);
|
||||
}
|
||||
|
||||
// Note that the order is important here: bringing a block to the front will
|
||||
// cause it to become focused and showing the field editor will capture
|
||||
// focus ephemerally. It's important to ensure that focus is properly
|
||||
// restored back to the block after field editing has completed.
|
||||
this.bringBlockToFront();
|
||||
|
||||
// Only show the editor if the field's editor wasn't already open
|
||||
// right before this gesture started.
|
||||
const dropdownAlreadyOpen = this.currentDropdownOwner === this.startField;
|
||||
@@ -899,7 +884,6 @@ export class Gesture {
|
||||
'Cannot do an icon click because the start icon is undefined',
|
||||
);
|
||||
}
|
||||
this.bringBlockToFront();
|
||||
this.startIcon.onClick();
|
||||
}
|
||||
|
||||
@@ -938,7 +922,6 @@ export class Gesture {
|
||||
);
|
||||
eventUtils.fire(event);
|
||||
}
|
||||
this.bringBlockToFront();
|
||||
eventUtils.setGroup(false);
|
||||
}
|
||||
|
||||
@@ -957,19 +940,6 @@ export class Gesture {
|
||||
|
||||
// TODO (fenichel): Move bubbles to the front.
|
||||
|
||||
/**
|
||||
* Move the dragged/clicked block to the front of the workspace so that it is
|
||||
* not occluded by other blocks.
|
||||
*/
|
||||
private bringBlockToFront() {
|
||||
// Blocks in the flyout don't overlap, so skip the work.
|
||||
if (this.targetBlock && !this.flyout) {
|
||||
// Always ensure the block being dragged/clicked has focus.
|
||||
getFocusManager().focusNode(this.targetBlock);
|
||||
this.targetBlock.bringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
/* Begin functions for populating a gesture at pointerdown. */
|
||||
|
||||
/**
|
||||
@@ -1039,8 +1009,9 @@ export class Gesture {
|
||||
* @internal
|
||||
*/
|
||||
setStartBlock(block: BlockSvg) {
|
||||
// If the gesture already went through a bubble, don't set the start block.
|
||||
if (!this.startBlock && !this.startBubble) {
|
||||
// If the gesture already went through a block child, don't set the start
|
||||
// block.
|
||||
if (!this.startBlock && !this.startBubble && !this.startIcon) {
|
||||
this.startBlock = block;
|
||||
if (block.isInFlyout && block !== block.getRootBlock()) {
|
||||
this.setTargetBlock(block.getRootBlock());
|
||||
@@ -1064,7 +1035,8 @@ export class Gesture {
|
||||
this.setTargetBlock(block.getParent()!);
|
||||
} else {
|
||||
this.targetBlock = block;
|
||||
getFocusManager().focusNode(block);
|
||||
getFocusManager().focusNode(this.targetBlock);
|
||||
this.targetBlock.bringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import type {Block} from '../block.js';
|
||||
import type {BlockSvg} from '../block_svg.js';
|
||||
import * as browserEvents from '../browser_events.js';
|
||||
import type {IContextMenu} from '../interfaces/i_contextmenu.js';
|
||||
import type {IFocusableTree} from '../interfaces/i_focusable_tree.js';
|
||||
import {hasBubble} from '../interfaces/i_has_bubble.js';
|
||||
import type {IIcon} from '../interfaces/i_icon.js';
|
||||
@@ -26,7 +27,7 @@ import type {IconType} from './icon_types.js';
|
||||
* block (such as warnings or comments) as opposed to fields, which provide
|
||||
* "actual" information, related to how a block functions.
|
||||
*/
|
||||
export abstract class Icon implements IIcon {
|
||||
export abstract class Icon implements IIcon, IContextMenu {
|
||||
/**
|
||||
* The position of this icon relative to its blocks top-start,
|
||||
* in workspace units.
|
||||
@@ -196,4 +197,8 @@ export abstract class Icon implements IIcon {
|
||||
getSourceBlock(): Block {
|
||||
return this.sourceBlock;
|
||||
}
|
||||
|
||||
showContextMenu(e: PointerEvent) {
|
||||
(this.getSourceBlock() as BlockSvg).showContextMenu(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,3 +14,8 @@ export interface IContextMenu {
|
||||
*/
|
||||
showContextMenu(e: Event): void;
|
||||
}
|
||||
|
||||
/** @returns true if the given object implements IContextMenu. */
|
||||
export function hasContextMenu(obj: any): obj is IContextMenu {
|
||||
return obj && typeof obj.showContextMenu === 'function';
|
||||
}
|
||||
|
||||
@@ -73,11 +73,11 @@ export class LayerManager {
|
||||
* @internal
|
||||
*/
|
||||
appendToAnimationLayer(elem: IRenderedElement) {
|
||||
const currentTransform = this.dragLayer?.getAttribute('transform');
|
||||
const currentTransform = this.dragLayer?.style.transform;
|
||||
// Only update the current transform when appending, so animations don't
|
||||
// move if the workspace moves.
|
||||
if (currentTransform) {
|
||||
this.animationLayer?.setAttribute('transform', currentTransform);
|
||||
if (currentTransform && this.animationLayer) {
|
||||
this.animationLayer.style.transform = currentTransform;
|
||||
}
|
||||
this.animationLayer?.appendChild(elem.getSvgRoot());
|
||||
}
|
||||
@@ -88,10 +88,12 @@ export class LayerManager {
|
||||
* @internal
|
||||
*/
|
||||
translateLayers(newCoord: Coordinate, newScale: number) {
|
||||
const translation = `translate(${newCoord.x}, ${newCoord.y}) scale(${newScale})`;
|
||||
this.dragLayer?.setAttribute('transform', translation);
|
||||
const translation = `translate(${newCoord.x}px, ${newCoord.y}px) scale(${newScale})`;
|
||||
if (this.dragLayer) {
|
||||
this.dragLayer.style.transform = translation;
|
||||
}
|
||||
for (const [_, layer] of this.layers) {
|
||||
layer.setAttribute('transform', translation);
|
||||
layer.style.transform = translation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2467,17 +2467,17 @@ export class WorkspaceSvg
|
||||
* valid gesture exists.
|
||||
* @internal
|
||||
*/
|
||||
getGesture(e: PointerEvent): Gesture | null {
|
||||
getGesture(e?: PointerEvent): Gesture | null {
|
||||
// TODO(#8960): Query Mover.isMoving to see if move is in progress
|
||||
// rather than relying on .keyboardMoveInProgress status flag.
|
||||
if (this.keyboardMoveInProgress) {
|
||||
// Normally these would be called from Gesture.doStart.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e?.preventDefault();
|
||||
e?.stopPropagation();
|
||||
return null;
|
||||
}
|
||||
|
||||
const isStart = e.type === 'pointerdown';
|
||||
const isStart = e?.type === 'pointerdown';
|
||||
if (isStart && this.currentGesture_?.hasStarted()) {
|
||||
console.warn('Tried to start the same gesture twice.');
|
||||
// That's funny. We must have missed a mouse up.
|
||||
|
||||
@@ -373,8 +373,10 @@ export class ZoomControls implements IPositionable {
|
||||
* @param e A mouse down event.
|
||||
*/
|
||||
private zoom(amount: number, e: PointerEvent) {
|
||||
this.workspace.beginCanvasTransition();
|
||||
this.workspace.markFocused();
|
||||
this.workspace.zoomCenter(amount);
|
||||
setTimeout(this.workspace.endCanvasTransition.bind(this.workspace), 150);
|
||||
this.fireZoomEvent();
|
||||
Touch.clearTouchIdentifier(); // Don't block future drags.
|
||||
e.stopPropagation(); // Don't start a workspace scroll.
|
||||
@@ -459,7 +461,7 @@ export class ZoomControls implements IPositionable {
|
||||
this.workspace.zoomCenter(amount);
|
||||
this.workspace.scrollCenter();
|
||||
|
||||
setTimeout(this.workspace.endCanvasTransition.bind(this.workspace), 500);
|
||||
setTimeout(this.workspace.endCanvasTransition.bind(this.workspace), 150);
|
||||
this.fireZoomEvent();
|
||||
Touch.clearTouchIdentifier(); // Don't block future drags.
|
||||
e.stopPropagation(); // Don't start a workspace scroll.
|
||||
|
||||
Generated
-10931
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@
|
||||
"start": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"http-server ./ -s -o /tests/playground.html -c-1\"",
|
||||
"tsc": "gulp tsc",
|
||||
"test": "gulp test",
|
||||
"test:browser": "cd tests/browser && npx mocha",
|
||||
"test:browser": "npx mocha --config tests/browser/.mocharc.js",
|
||||
"test:generators": "gulp testGenerators",
|
||||
"test:mocha:interactive": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"gulp interactiveMocha\"",
|
||||
"test:compile:advanced": "gulp buildAdvancedCompilationTest --debug",
|
||||
@@ -104,8 +104,6 @@
|
||||
"@blockly/dev-tools": "^9.0.2",
|
||||
"@blockly/keyboard-navigation": "^3.0.1",
|
||||
"@blockly/theme-modern": "^7.0.1",
|
||||
"@commitlint/cli": "^20.1.0",
|
||||
"@commitlint/config-conventional": "^20.0.0",
|
||||
"@hyperjump/browser": "^1.1.4",
|
||||
"@hyperjump/json-schema": "^1.5.0",
|
||||
"@microsoft/api-documenter": "7.22.4",
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
module.exports = {
|
||||
ui: 'tdd',
|
||||
require: __dirname + '/test/hooks.mjs',
|
||||
spec: 'tests/browser/test/**/*_test.mjs',
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import * as Blockly from '../../build/src/core/blockly.js';
|
||||
import {assert} from '../../node_modules/chai/index.js';
|
||||
import {defineEmptyBlock} from './test_helpers/block_definitions.js';
|
||||
import {MockIcon, MockSerializableIcon} from './test_helpers/icon_mocks.js';
|
||||
@@ -11,6 +12,26 @@ import {
|
||||
sharedTestSetup,
|
||||
sharedTestTeardown,
|
||||
} from './test_helpers/setup_teardown.js';
|
||||
import {simulateClick} from './test_helpers/user_input.js';
|
||||
|
||||
class TestIcon extends Blockly.icons.Icon {
|
||||
showContextMenu(e) {
|
||||
const menuItems = [
|
||||
{text: 'Test icon menu item', enabled: true, callback: () => {}},
|
||||
];
|
||||
Blockly.ContextMenu.show(
|
||||
e,
|
||||
menuItems,
|
||||
false,
|
||||
this.getSourceBlock().workspace,
|
||||
this.workspaceLocation,
|
||||
);
|
||||
}
|
||||
|
||||
getType() {
|
||||
new Blockly.icons.IconType('test');
|
||||
}
|
||||
}
|
||||
|
||||
suite('Icon', function () {
|
||||
setup(function () {
|
||||
@@ -366,4 +387,45 @@ suite('Icon', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contextual menus', function () {
|
||||
setup(function () {
|
||||
this.workspace = Blockly.inject('blocklyDiv', {});
|
||||
Blockly.icons.registry.register(
|
||||
new Blockly.icons.IconType('test'),
|
||||
TestIcon,
|
||||
);
|
||||
|
||||
this.block = this.workspace.newBlock('empty_block');
|
||||
this.block.initSvg();
|
||||
});
|
||||
|
||||
test('are shown when icons are right clicked', function () {
|
||||
const icon = new TestIcon(this.block);
|
||||
this.block.addIcon(icon);
|
||||
simulateClick(icon.getFocusableElement(), {button: 2});
|
||||
|
||||
const menu = document.querySelector('.blocklyContextMenu');
|
||||
assert.isNotNull(menu);
|
||||
assert.isTrue(menu.innerText.includes('Test icon menu item'));
|
||||
});
|
||||
|
||||
test('default to the contextual menu of the parent block', function () {
|
||||
this.block.setCommentText('hello there');
|
||||
const icon = this.block.getIcon(Blockly.icons.IconType.COMMENT);
|
||||
simulateClick(icon.getFocusableElement(), {button: 2});
|
||||
|
||||
const expectedItems =
|
||||
Blockly.ContextMenuRegistry.registry.getContextMenuOptions({
|
||||
block: this.block,
|
||||
});
|
||||
|
||||
assert.isNotEmpty(expectedItems);
|
||||
const menu = document.querySelector('.blocklyContextMenu');
|
||||
for (const item of expectedItems) {
|
||||
if (!item.text) continue;
|
||||
assert.isTrue(menu.innerText.includes(item.text));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
sharedTestSetup,
|
||||
sharedTestTeardown,
|
||||
} from './test_helpers/setup_teardown.js';
|
||||
import {dispatchPointerEvent} from './test_helpers/user_input.js';
|
||||
import {testAWorkspace} from './test_helpers/workspace.js';
|
||||
|
||||
suite('WorkspaceSvg', function () {
|
||||
@@ -114,6 +115,17 @@ suite('WorkspaceSvg', function () {
|
||||
assert.equal(true, shadowBlock.isDeadOrDying());
|
||||
});
|
||||
|
||||
test('getGesture returns null when no gesture is in progress', function () {
|
||||
const gesture = this.workspace.getGesture();
|
||||
assert.isNull(gesture);
|
||||
});
|
||||
|
||||
test('getGesture returns the current gesture when one is in progress', function () {
|
||||
dispatchPointerEvent(this.workspace.getSvgGroup(), 'pointerdown');
|
||||
const gesture = this.workspace.getGesture();
|
||||
assert.isNotNull(gesture);
|
||||
});
|
||||
|
||||
suite('updateToolbox', function () {
|
||||
test('Passes in null when toolbox exists', function () {
|
||||
assert.throws(
|
||||
|
||||
Reference in New Issue
Block a user