diff --git a/packages/blockly/core/dragging/block_drag_strategy.ts b/packages/blockly/core/dragging/block_drag_strategy.ts index 3ed6fbead18..514fb273a25 100644 --- a/packages/blockly/core/dragging/block_drag_strategy.ts +++ b/packages/blockly/core/dragging/block_drag_strategy.ts @@ -241,6 +241,7 @@ export class BlockDragStrategy implements IDragStrategy { recordUndo: true, }, ) as BlockSvg; + newBlock.setDragging(true); eventUtils.setRecordUndo(false); newBlock.render(); this.positionNewBlock(this.block, newBlock); diff --git a/packages/blockly/core/dropdowndiv.ts b/packages/blockly/core/dropdowndiv.ts index 76876d58c26..b129ead29d9 100644 --- a/packages/blockly/core/dropdowndiv.ts +++ b/packages/blockly/core/dropdowndiv.ts @@ -147,7 +147,10 @@ export function createDom() { content, 'keydown', null, - common.globalShortcutHandler, + (e: KeyboardEvent) => { + common.globalShortcutHandler(e); + e.stopPropagation(); + }, ); arrow = document.createElement('div'); diff --git a/packages/blockly/core/widgetdiv.ts b/packages/blockly/core/widgetdiv.ts index 05b3db8bed0..d76710cb0dd 100644 --- a/packages/blockly/core/widgetdiv.ts +++ b/packages/blockly/core/widgetdiv.ts @@ -102,7 +102,10 @@ export function createDom() { containerDiv, 'keydown', null, - common.globalShortcutHandler, + (e: KeyboardEvent) => { + common.globalShortcutHandler(e); + e.stopPropagation(); + }, ); container.appendChild(containerDiv); diff --git a/packages/blockly/tests/mocha/shortcut_items_test.js b/packages/blockly/tests/mocha/shortcut_items_test.js index 3b46e096dda..d80147c0863 100644 --- a/packages/blockly/tests/mocha/shortcut_items_test.js +++ b/packages/blockly/tests/mocha/shortcut_items_test.js @@ -1885,6 +1885,22 @@ suite('Keyboard Shortcut Items', function () { this.workspace.getInjectionDiv().dispatchEvent(event); assert.equal(this.workspace.getTopComments().length, 1); }); + + test('Is idempotent when a contextual menu is open', function () { + const comment = this.workspace.newComment(); + comment.setText('Hello'); + Blockly.getFocusManager().focusNode(comment); + comment.showContextMenu(); + assert.equal(this.workspace.getTopComments().length, 1); + const event = createKeyDownEvent(Blockly.utils.KeyCodes.D); + // The WidgetDiv (used by the dropdown menu) registers the global shortcut + // handler for its own div, since it may live outside of the injection + // div. Ensure that it also prevents event bubbling to the main shortcut + // handler, which could cause shortcuts like Duplicate to be invoked + // multiple times from one keypress. See #10250. + Blockly.WidgetDiv.getDiv().dispatchEvent(event); + assert.equal(this.workspace.getTopComments().length, 2); + }); }); suite('Clean up workspace (C)', function () { diff --git a/packages/blockly/tests/mocha/test_helpers/user_input.js b/packages/blockly/tests/mocha/test_helpers/user_input.js index 709955af7db..b342631c9c2 100644 --- a/packages/blockly/tests/mocha/test_helpers/user_input.js +++ b/packages/blockly/tests/mocha/test_helpers/user_input.js @@ -39,6 +39,7 @@ export function dispatchPointerEvent(target, type, properties) { export function createKeyDownEvent(keyCode, modifiers) { const event = { keyCode: keyCode, + bubbles: true, }; if (modifiers && modifiers.length > 0) { event.altKey = modifiers.includes(KeyCodes.ALT);