Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion packages/blockly/core/dropdowndiv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import * as browserEvents from './browser_events.js';
import * as common from './common.js';
import type {Field} from './field.js';
import {ReturnEphemeralFocus, getFocusManager} from './focus_manager.js';
import * as aria from './utils/aria.js';
import * as dom from './utils/dom.js';
import * as idGenerator from './utils/idgenerator.js';
import * as math from './utils/math.js';
import {Rect} from './utils/rect.js';
import type {Size} from './utils/size.js';
Expand Down Expand Up @@ -127,6 +129,7 @@ export function createDom() {
div = document.createElement('div');
div.className = 'blocklyDropDownDiv';
div.tabIndex = -1;
div.id = idGenerator.getNextUniqueId();
const parentDiv = common.getParentContainer() || document.body;
parentDiv.appendChild(div);

Expand Down Expand Up @@ -399,6 +402,16 @@ export function show<T>(
dom.addClass(div, renderedClassName);
dom.addClass(div, themeClassName);

const existingOwnership = aria.getState(
mainWorkspace.getFocusableElement(),
aria.State.OWNS,
);
aria.setState(
mainWorkspace.getFocusableElement(),
aria.State.OWNS,
existingOwnership ? [existingOwnership, div.id] : div.id,
);

// When we change `translate` multiple times in close succession,
// Chrome may choose to wait and apply them all at once.
// Since we want the translation to initial X, Y to be immediate,
Expand Down Expand Up @@ -714,7 +727,16 @@ export function hideWithoutAnimation() {
}
owner = null;

(common.getMainWorkspace() as WorkspaceSvg).markFocused();
const workspace = common.getMainWorkspace() as WorkspaceSvg;
const existingOwnership =
aria.getState(workspace.getFocusableElement(), aria.State.OWNS) ?? '';
aria.setState(
workspace.getFocusableElement(),
aria.State.OWNS,
existingOwnership.replace(div.id, ''),
);

workspace.markFocused();

if (returnEphemeralFocus) {
returnEphemeralFocus();
Expand Down
33 changes: 27 additions & 6 deletions packages/blockly/core/widgetdiv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import * as browserEvents from './browser_events.js';
import * as common from './common.js';
import {Field} from './field.js';
import {ReturnEphemeralFocus, getFocusManager} from './focus_manager.js';
import * as aria from './utils/aria.js';
import * as dom from './utils/dom.js';
import * as idGenerator from './utils/idgenerator.js';
import type {Rect} from './utils/rect.js';
import type {Size} from './utils/size.js';
import type {WorkspaceSvg} from './workspace_svg.js';
Expand Down Expand Up @@ -85,6 +87,7 @@ export function createDom() {
containerDiv = existingContainer as HTMLDivElement;
} else {
containerDiv = document.createElement('div');
containerDiv.id = idGenerator.getNextUniqueId();
containerDiv.className = containerClassName;
containerDiv.tabIndex = -1;
}
Expand Down Expand Up @@ -126,6 +129,17 @@ export function show(
const div = containerDiv;
if (!div) return;

ownerWorkspace = workspace ?? (common.getMainWorkspace() as WorkspaceSvg);
const existingOwnership = aria.getState(
ownerWorkspace.getFocusableElement(),
aria.State.OWNS,
);
aria.setState(
ownerWorkspace.getFocusableElement(),
aria.State.OWNS,
existingOwnership ? [existingOwnership, div.id] : div.id,
);

const parentDiv = common.getParentContainer();
parentDiv?.appendChild(div);

Expand All @@ -136,11 +150,8 @@ export function show(
// workspace to this function, attempt to derive it from the field.
workspace = (newOwner as Field).getSourceBlock()?.workspace as WorkspaceSvg;
}
ownerWorkspace = workspace ?? null;
const rendererWorkspace =
workspace ?? (common.getMainWorkspace() as WorkspaceSvg);
rendererClassName = rendererWorkspace.getRenderer().getClassName();
themeClassName = rendererWorkspace.getTheme().getClassName();
rendererClassName = ownerWorkspace.getRenderer().getClassName();
themeClassName = ownerWorkspace.getTheme().getClassName();
if (rendererClassName) {
dom.addClass(div, rendererClassName);
}
Expand Down Expand Up @@ -182,12 +193,22 @@ export function hide() {
dom.removeClass(div, themeClassName);
themeClassName = '';
}
(common.getMainWorkspace() as WorkspaceSvg).markFocused();
ownerWorkspace?.markFocused();

if (returnEphemeralFocus) {
returnEphemeralFocus();
returnEphemeralFocus = null;
}

if (!ownerWorkspace || !containerDiv) return;

const existingOwnership =
aria.getState(ownerWorkspace.getFocusableElement(), aria.State.OWNS) ?? '';
aria.setState(
ownerWorkspace.getFocusableElement(),
aria.State.OWNS,
existingOwnership.replace(containerDiv.id, ''),
);
}

/**
Expand Down
51 changes: 51 additions & 0 deletions packages/blockly/tests/mocha/dropdowndiv_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,20 @@ suite('DropDownDiv', function () {
assert.strictEqual(dropDownDivElem.style.left, '45px');
assert.strictEqual(dropDownDivElem.style.top, '60px');
});

test('sets the dropdowndiv as owned by the workspace', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];

Blockly.DropDownDiv.show(field, false, 50, 60, 70, 80, false);
assert.equal(
Blockly.utils.aria.getState(
Blockly.getMainWorkspace().getFocusableElement(),
Blockly.utils.aria.State.OWNS,
),
Blockly.DropDownDiv.getContentDiv().parentElement.id,
);
});
});

suite('showPositionedByField()', function () {
Expand Down Expand Up @@ -388,6 +402,21 @@ suite('DropDownDiv', function () {
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block);
assert.strictEqual(document.activeElement, blockFocusableElem);
});

test('clears ownership of the dropdowndiv by the workspace', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.DropDownDiv.showPositionedByField(field, null, null, true);
Blockly.DropDownDiv.hideWithoutAnimation();

assert.isNull(
Blockly.utils.aria.getState(
Blockly.getMainWorkspace().getFocusableElement(),
Blockly.utils.aria.State.OWNS,
),
);
});
});

suite('for div positioned by block', function () {
Expand Down Expand Up @@ -454,6 +483,28 @@ suite('DropDownDiv', function () {
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block);
assert.strictEqual(document.activeElement, blockFocusableElem);
});

test('clears ownership of the dropdowndiv by the workspace', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.DropDownDiv.showPositionedByBlock(
field,
block,
null,
null,
true,
);

Blockly.DropDownDiv.hideWithoutAnimation();

assert.isNull(
Blockly.utils.aria.getState(
Blockly.getMainWorkspace().getFocusableElement(),
Blockly.utils.aria.State.OWNS,
),
);
});
});
});
});
3 changes: 3 additions & 0 deletions packages/blockly/tests/mocha/field_textinput_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ suite('Text Input Fields', function () {
},
markFocused: function () {},
options: {},
getFocusableElement: function () {
return document.createElement('div');
},
};
field.sourceBlock_ = {
workspace: workspace,
Expand Down
31 changes: 31 additions & 0 deletions packages/blockly/tests/mocha/widget_div_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,21 @@ suite('WidgetDiv', function () {
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block);
assert.strictEqual(document.activeElement, widgetDivElem);
});

test('makes the widget div owned by the workspace', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);

Blockly.WidgetDiv.show(field, false, () => {}, null, true);
assert.equal(
Blockly.utils.aria.getState(
Blockly.getMainWorkspace().getFocusableElement(),
Blockly.utils.aria.State.OWNS,
),
Blockly.WidgetDiv.getDiv().id,
);
});
});

suite('hide()', function () {
Expand Down Expand Up @@ -424,5 +439,21 @@ suite('WidgetDiv', function () {
assert.strictEqual(Blockly.getFocusManager().getFocusedNode(), block);
assert.strictEqual(document.activeElement, blockFocusableElem);
});

test('clears ownership of the widget div by the workspace', function () {
const block = this.setUpBlockWithField();
const field = Array.from(block.getFields())[0];
Blockly.getFocusManager().focusNode(block);
Blockly.WidgetDiv.show(field, false, () => {}, null, true);

Blockly.WidgetDiv.hide();

assert.isNull(
Blockly.utils.aria.getState(
Blockly.getMainWorkspace().getFocusableElement(),
Blockly.utils.aria.State.OWNS,
),
);
});
});
});
Loading