Skip to content

Commit bb8b7a9

Browse files
fix: allow nav into collapsed block row via right arrow (#9958)
This supports e.g. expand/collapse icons added outside core Blockly. MakeCode has an expand FieldImageNoText on collapsed blocks that wasn't otherwise navigable (inserted into COLLAPSED_INPUT_NAME): input.appendField(new FieldImageNoText(image, 24, 24, "Expand", () => { this.setCollapsed(false) }, false)); To support this: 1. the input that shares the block row id takes into account visibility 2. we drop the short circuit for collapsed blocks and rely on the filtering already in place to filter out the collapsed content Also filter icons whose updateCollapsed() hides them via display:none; without this they remain in the candidate list and the navigator can focus e.g. an invisible cog for a mutator workspace. Add tests for icon visibility.
1 parent 00882a8 commit bb8b7a9

3 files changed

Lines changed: 47 additions & 5 deletions

File tree

packages/blockly/core/inputs/input.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,13 @@ export class Input {
375375
getRowId(): string {
376376
const inputs = this.getSourceBlock().inputList;
377377

378-
// The first input in a block has the same ID as its parent block.
378+
// The first visible input shares the block's row id; this also covers
379+
// the collapsed-input placeholder, since every other input is hidden.
380+
if (this === inputs.find((i) => i.isVisible())) {
381+
return (this.getSourceBlock() as BlockSvg).getRowId();
382+
}
383+
384+
// Fallback when inputs[0] itself is hidden.
379385
if (this === inputs[0]) {
380386
return (this.getSourceBlock() as BlockSvg).getRowId();
381387
}

packages/blockly/core/keyboard_nav/navigation_policies/block_navigation_policy.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,12 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
119119
* @returns A list of navigable/focusable children of the given block.
120120
*/
121121
function getBlockNavigationCandidates(block: BlockSvg): IFocusableNode[] {
122-
// Collapsed blocks have no navigable children.
123-
if (block.isCollapsed()) return [];
124-
125122
const candidates: IFocusableNode[] = [];
126123

127124
// Icons and open bubbles are navigable.
128125
for (const icon of block.getIcons()) {
126+
// Icons hidden when the block is collapsed shouldn't be navigable.
127+
if (block.isCollapsed() && !icon.isShownWhenCollapsed()) continue;
129128
candidates.push(icon);
130129
let bubble;
131130
if (
@@ -162,7 +161,7 @@ function getBlockNavigationCandidates(block: BlockSvg): IFocusableNode[] {
162161
}
163162

164163
// The block's next connection is navigable.
165-
if (block.nextConnection) {
164+
if (block.nextConnection && !block.isCollapsed()) {
166165
candidates.push(block.nextConnection);
167166
}
168167

packages/blockly/tests/mocha/navigation_test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,43 @@ suite('Navigation', function () {
774774
const inNode = this.navigator.getFirstChild(this.blocks.buttonBlock);
775775
assert.isNull(inNode);
776776
});
777+
test('reachesClickableFieldOnCollapsedInput', function () {
778+
// Models a pattern where a clickable field is appended to
779+
// COLLAPSED_INPUT_NAME when the block collapses.
780+
const block = this.blocks.buttonBlock;
781+
block.setCollapsed(true);
782+
const input = block.getInput(Blockly.constants.COLLAPSED_INPUT_NAME);
783+
const expandButton = new Blockly.FieldImage(
784+
'https://www.gstatic.com/codesite/ph/images/star_on.gif',
785+
16,
786+
16,
787+
'expand',
788+
() => {},
789+
);
790+
input.appendField(expandButton);
791+
792+
const inNode = this.navigator.getInNode(block);
793+
assert.equal(inNode, expandButton);
794+
});
795+
test('skipsIconsHiddenWhenCollapsed', function () {
796+
// Comment icons are hidden when the block is collapsed, so they
797+
// should not be navigable.
798+
const block = this.blocks.statementInput1;
799+
block.setCommentText('comment');
800+
block.setCollapsed(true);
801+
assert.isNull(this.navigator.getInNode(block));
802+
});
803+
test('reachesIconsShownWhenCollapsed', function () {
804+
// Warning icons remain shown when the block is collapsed, so they
805+
// should still be navigable.
806+
const block = this.blocks.statementInput1;
807+
block.setWarningText('warning');
808+
block.setCollapsed(true);
809+
assert.equal(
810+
this.navigator.getInNode(block),
811+
block.getIcon(Blockly.icons.IconType.WARNING),
812+
);
813+
});
777814
});
778815

779816
suite('Out', function () {

0 commit comments

Comments
 (0)