From b472baf17645190e493bf157ec958456d9688d32 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Thu, 18 Jun 2026 14:52:05 +0100 Subject: [PATCH 1/2] fix: Bring blocks to the front when focused via keyboard --- packages/blockly/core/block_svg.ts | 33 +++++++++++++++----- packages/blockly/core/rendered_connection.ts | 8 +++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/packages/blockly/core/block_svg.ts b/packages/blockly/core/block_svg.ts index 7c265cf5411..70015d1fd80 100644 --- a/packages/blockly/core/block_svg.ts +++ b/packages/blockly/core/block_svg.ts @@ -1354,6 +1354,29 @@ export class BlockSvg */ bringToFront(blockOnly = false) { const previouslyFocused = getFocusManager().getFocusedNode(); + this.moveSvgRootToFront(blockOnly); + if (previouslyFocused) { + // Bringing a block to the front of the stack doesn't fundamentally change + // the logical structure of the page, but it does change element ordering + // which can take automatically take away focus from a node. Ensure focus + // is restored to avoid a discontinuity. + getFocusManager().focusNode(previouslyFocused); + } + } + + /** + * Reorders this block's SVG root and those of its parents (unless + * `blockOnly`` is set to `true`) to the end of their respective parents so + * they render on top of their siblings. + * + * Unlike `bringToFront`, this does not preserve focus across the reorder, so + * it is safe to call from within a focus callback + * + * @param blockOnly True to only move this block to the front without + * adjusting its parents. + * @internal + */ + moveSvgRootToFront(blockOnly = false) { /* eslint-disable-next-line @typescript-eslint/no-this-alias */ let block: this | null = this; if (block.isDeadOrDying()) { @@ -1370,13 +1393,6 @@ export class BlockSvg if (blockOnly) break; block = block.getParent(); } while (block); - if (previouslyFocused) { - // Bringing a block to the front of the stack doesn't fundamentally change - // the logical structure of the page, but it does change element ordering - // which can take automatically take away focus from a node. Ensure focus - // is restored to avoid a discontinuity. - getFocusManager().focusNode(previouslyFocused); - } } /** @@ -1909,6 +1925,9 @@ export class BlockSvg onNodeFocus(): void { this.recomputeAriaContext(); this.select(); + if (!this.workspace.isFlyout) { + this.moveSvgRootToFront(); + } const focusedNode = getFocusManager().getFocusedNode(); if (focusedNode && focusedNode !== this) { renderManagement.finishQueuedRenders().then(() => { diff --git a/packages/blockly/core/rendered_connection.ts b/packages/blockly/core/rendered_connection.ts index 707c77cc0a9..df704442eed 100644 --- a/packages/blockly/core/rendered_connection.ts +++ b/packages/blockly/core/rendered_connection.ts @@ -700,8 +700,12 @@ export class RenderedConnection /** See IFocusableNode.onNodeFocus. */ onNodeFocus(): void { this.highlight(); - this.getSourceBlock().workspace.scrollBoundsIntoView( - this.getSourceBlock().getBoundingRectangleWithoutChildren(), + const sourceBlock = this.getSourceBlock(); + if (!sourceBlock.workspace.isFlyout) { + sourceBlock.moveSvgRootToFront(); + } + sourceBlock.workspace.scrollBoundsIntoView( + sourceBlock.getBoundingRectangleWithoutChildren(), ); } From 8aadcdc5f9f82f025dd97a1527701e467c5bf3a3 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Thu, 18 Jun 2026 15:38:45 +0100 Subject: [PATCH 2/2] Address test failures --- packages/blockly/core/block_svg.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/blockly/core/block_svg.ts b/packages/blockly/core/block_svg.ts index 70015d1fd80..e79a70eac97 100644 --- a/packages/blockly/core/block_svg.ts +++ b/packages/blockly/core/block_svg.ts @@ -1385,10 +1385,11 @@ export class BlockSvg do { const root = block.getSvgRoot(); const parent = root.parentNode; - const childNodes = parent!.childNodes; + if (!parent) return; + const childNodes = parent.childNodes; // Avoid moving the block if it's already at the bottom. if (childNodes[childNodes.length - 1] !== root) { - parent!.appendChild(root); + parent.appendChild(root); } if (blockOnly) break; block = block.getParent();