Revert "fix: Auto close drop-down divs on lost focus (#9175)" (#9204)

This reverts commit 4c78c1d4a3 / PR #9175.
This commit is contained in:
Christopher Allen
2025-07-07 17:40:58 +01:00
committed by GitHub
parent 4c78c1d4a3
commit 7ad18f717a
5 changed files with 13 additions and 358 deletions

View File

@@ -155,7 +155,7 @@ suite('DropDownDiv', function () {
});
test('Escape dismisses DropDownDiv', function () {
let hidden = false;
Blockly.DropDownDiv.show(this, false, 0, 0, 0, 0, false, false, () => {
Blockly.DropDownDiv.show(this, false, 0, 0, 0, 0, false, () => {
hidden = true;
});
assert.isFalse(hidden);
@@ -252,34 +252,6 @@ suite('DropDownDiv', function () {
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block);
assert.strictEqual(document.activeElement, dropDownDivElem);
});
test('without auto close on lost focus lost focus does not hide drop-down div', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.DropDownDiv.showPositionedByField(field, null, null, true, false);
// Focus an element outside of the drop-down.
document.getElementById('nonTreeElementForEphemeralFocus').focus();
// Even though the drop-down lost focus, it should still be visible.
const dropDownDivElem = document.querySelector('.blocklyDropDownDiv');
assert.strictEqual(dropDownDivElem.style.opacity, '1');
});
test('with auto close on lost focus lost focus hides drop-down div', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.DropDownDiv.showPositionedByField(field, null, null, true, true);
// Focus an element outside of the drop-down.
document.getElementById('nonTreeElementForEphemeralFocus').focus();
// the drop-down should now be hidden since it lost focus.
const dropDownDivElem = document.querySelector('.blocklyDropDownDiv');
assert.strictEqual(dropDownDivElem.style.opacity, '0');
});
});
suite('showPositionedByBlock()', function () {
@@ -353,48 +325,6 @@ suite('DropDownDiv', function () {
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block);
assert.strictEqual(document.activeElement, dropDownDivElem);
});
test('without auto close on lost focus lost focus does not hide drop-down div', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.DropDownDiv.showPositionedByBlock(
field,
block,
null,
null,
true,
false,
);
// Focus an element outside of the drop-down.
document.getElementById('nonTreeElementForEphemeralFocus').focus();
// Even though the drop-down lost focus, it should still be visible.
const dropDownDivElem = document.querySelector('.blocklyDropDownDiv');
assert.strictEqual(dropDownDivElem.style.opacity, '1');
});
test('with auto close on lost focus lost focus hides drop-down div', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.DropDownDiv.showPositionedByBlock(
field,
block,
null,
null,
true,
true,
);
// Focus an element outside of the drop-down.
document.getElementById('nonTreeElementForEphemeralFocus').focus();
// the drop-down should now be hidden since it lost focus.
const dropDownDivElem = document.querySelector('.blocklyDropDownDiv');
assert.strictEqual(dropDownDivElem.style.opacity, '0');
});
});
suite('hideWithoutAnimation()', function () {

View File

@@ -5975,172 +5975,5 @@ suite('FocusManager', function () {
);
assert.strictEqual(document.activeElement, nodeElem);
});
test('with focus change callback initially calls focus change callback with initial state', function () {
const callback = sinon.fake();
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
const ephemeralElement = document.getElementById(
'nonTreeElementForEphemeralFocus',
);
this.focusManager.takeEphemeralFocus(ephemeralElement, callback);
assert.strictEqual(callback.callCount, 1);
assert.isTrue(callback.firstCall.calledWithExactly(true));
});
test('with focus change callback finishes ephemeral does not calls focus change callback again', function () {
const callback = sinon.fake();
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
const ephemeralElement = document.getElementById(
'nonTreeElementForEphemeralFocus',
);
const finishFocusCallback = this.focusManager.takeEphemeralFocus(
ephemeralElement,
callback,
);
callback.resetHistory();
finishFocusCallback();
assert.isFalse(callback.called);
});
test('with focus change callback set focus to ephemeral child does not call focus change callback again', function () {
const callback = sinon.fake();
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
const ephemeralElement = document.getElementById(
'nonTreeElementForEphemeralFocus',
);
const ephemeralElementChild = document.getElementById(
'nonTreeElementForEphemeralFocus.child1',
);
this.focusManager.takeEphemeralFocus(ephemeralElement, callback);
callback.resetHistory();
ephemeralElementChild.focus();
// Focusing a child element shouldn't invoke the callback since the
// ephemeral element's tree still holds focus.
assert.isFalse(callback.called);
});
test('with focus change callback set focus to non-ephemeral element calls focus change callback', function () {
const callback = sinon.fake();
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
const ephemeralElement = document.getElementById(
'nonTreeElementForEphemeralFocus',
);
const ephemeralElement2 = document.getElementById(
'nonTreeElementForEphemeralFocus2',
);
this.focusManager.takeEphemeralFocus(ephemeralElement, callback);
ephemeralElement2.focus();
// There should be a second call that indicates focus was lost.
assert.strictEqual(callback.callCount, 2);
assert.isTrue(callback.secondCall.calledWithExactly(false));
});
test('with focus change callback set focus to non-ephemeral element then back calls focus change callback again', function () {
const callback = sinon.fake();
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
const ephemeralElement = document.getElementById(
'nonTreeElementForEphemeralFocus',
);
const ephemeralElementChild = document.getElementById(
'nonTreeElementForEphemeralFocus.child1',
);
const ephemeralElement2 = document.getElementById(
'nonTreeElementForEphemeralFocus2',
);
this.focusManager.takeEphemeralFocus(ephemeralElement, callback);
ephemeralElement2.focus();
ephemeralElementChild.focus();
// The latest call should be returning focus.
assert.strictEqual(callback.callCount, 3);
assert.isTrue(callback.thirdCall.calledWithExactly(true));
});
test('with focus change callback set focus to non-ephemeral element with auto return finishes ephemeral', function () {
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
this.focusManager.focusNode(this.testFocusableTree2Node1);
const ephemeralElement = document.getElementById(
'nonTreeGroupForEphemeralFocus',
);
const ephemeralElement2 = document.getElementById(
'nonTreeElementForEphemeralFocus2',
);
const finishFocusCallback = this.focusManager.takeEphemeralFocus(
ephemeralElement,
(hasFocus) => {
if (!hasFocus) finishFocusCallback();
},
);
// Force focus away, triggering the callback's automatic returning logic.
ephemeralElement2.focus();
// The original focused node should be restored.
const nodeElem = this.testFocusableTree2Node1.getFocusableElement();
const activeElems = Array.from(
document.querySelectorAll(ACTIVE_FOCUS_NODE_CSS_SELECTOR),
);
assert.strictEqual(
this.focusManager.getFocusedNode(),
this.testFocusableTree2Node1,
);
assert.strictEqual(activeElems.length, 1);
assert.includesClass(
nodeElem.classList,
FocusManager.ACTIVE_FOCUS_NODE_CSS_CLASS_NAME,
);
assert.strictEqual(document.activeElement, nodeElem);
});
test('with focus on non-ephemeral element ephemeral ended does not restore to focused node', function () {
this.focusManager.registerTree(this.testFocusableTree2);
this.focusManager.registerTree(this.testFocusableGroup2);
this.focusManager.focusNode(this.testFocusableTree2Node1);
const ephemeralElement = document.getElementById(
'nonTreeGroupForEphemeralFocus',
);
const ephemeralElement2 = document.getElementById(
'nonTreeElementForEphemeralFocus2',
);
const finishFocusCallback =
this.focusManager.takeEphemeralFocus(ephemeralElement);
// Force focus away, triggering the callback's automatic returning logic.
ephemeralElement2.focus();
finishFocusCallback();
// The original node should not be focused since the ephemeral element
// lost its own DOM focus while ephemeral focus was active. Instead, the
// newly active element should still hold focus.
const activeElems = Array.from(
document.querySelectorAll(ACTIVE_FOCUS_NODE_CSS_SELECTOR),
);
const passiveElems = Array.from(
document.querySelectorAll(PASSIVE_FOCUS_NODE_CSS_SELECTOR),
);
assert.isEmpty(activeElems);
assert.strictEqual(passiveElems.length, 1);
assert.includesClass(
this.testFocusableTree2Node1.getFocusableElement().classList,
FocusManager.PASSIVE_FOCUS_NODE_CSS_CLASS_NAME,
);
assert.strictEqual(document.activeElement, ephemeralElement2);
assert.isFalse(this.focusManager.ephemeralFocusTaken());
});
});
});

View File

@@ -94,13 +94,7 @@
</div>
</div>
<div id="testUnfocusableElement">Unfocusable element</div>
<div id="nonTreeElementForEphemeralFocus" tabindex="-1">
<div
id="nonTreeElementForEphemeralFocus.child1"
tabindex="-1"
style="margin-left: 1em"></div>
</div>
<div id="nonTreeElementForEphemeralFocus2" tabindex="-1"></div>
<div id="nonTreeElementForEphemeralFocus" />
<svg width="250" height="250">
<g id="testFocusableGroup1">
<g id="testFocusableGroup1.node1">