From 0363d67c183e8488e7bce9f0d8d3b03ca44bad64 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Mon, 7 Jul 2025 22:15:53 +0000 Subject: [PATCH] fix: Ensure widget divs restore focus correctly. This avoids reintroducing #9203. --- core/widgetdiv.ts | 18 ++++++++++++------ tests/mocha/widget_div_test.js | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/core/widgetdiv.ts b/core/widgetdiv.ts index d07f7fb50..f9b89de56 100644 --- a/core/widgetdiv.ts +++ b/core/widgetdiv.ts @@ -146,6 +146,18 @@ export function hide() { const div = containerDiv; if (!div) return; + + (common.getMainWorkspace() as WorkspaceSvg).markFocused(); + + if (returnEphemeralFocus) { + returnEphemeralFocus(); + returnEphemeralFocus = null; + } + + // Content must be cleared after returning ephemeral focus since otherwise it + // may force focus changes which could desynchronize the focus manager and + // make it think the user directed focus away from the widget div (which will + // then notify it to not restore focus back to any previously focused node). div.style.display = 'none'; div.style.left = ''; div.style.top = ''; @@ -163,12 +175,6 @@ export function hide() { dom.removeClass(div, themeClassName); themeClassName = ''; } - (common.getMainWorkspace() as WorkspaceSvg).markFocused(); - - if (returnEphemeralFocus) { - returnEphemeralFocus(); - returnEphemeralFocus = null; - } } /** diff --git a/tests/mocha/widget_div_test.js b/tests/mocha/widget_div_test.js index 61c942471..b20533bc3 100644 --- a/tests/mocha/widget_div_test.js +++ b/tests/mocha/widget_div_test.js @@ -423,5 +423,26 @@ suite('WidgetDiv', function () { assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block); assert.strictEqual(document.activeElement, blockFocusableElem); }); + + test('for showing nested div with ephemeral focus restores DOM focus', function () { + const block = this.setUpBlockWithField(); + const field = Array.from(block.getFields())[0]; + Blockly.getFocusManager().focusNode(block); + const nestedDiv = document.createElement('div'); + nestedDiv.tabIndex = -1; + Blockly.WidgetDiv.getDiv().appendChild(nestedDiv); + Blockly.WidgetDiv.show(field, false, () => {}, null, true); + nestedDiv.focus(); // It's valid to focus this during ephemeral focus. + + // Hiding will cause the now focused child div to be removed, leading to + // ephemeral focus being lost if the implementation doesn't handle + // returning ephemeral focus correctly. + Blockly.WidgetDiv.hide(); + + // Hiding the div should restore focus back to the block. + const blockFocusableElem = block.getFocusableElement(); + assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block); + assert.strictEqual(document.activeElement, blockFocusableElem); + }); }); });