Skip to content

Commit

Permalink
fix: collapsing blocks with children with icons (#7111)
Browse files Browse the repository at this point in the history
* fix: collapsing blocks with icons

* chore: add tests for hiding icons of collapsed children
  • Loading branch information
BeksOmega authored May 22, 2023
1 parent d90d005 commit 6e3d052
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 133 deletions.
4 changes: 2 additions & 2 deletions core/interfaces/i_has_bubble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

export interface IHasBubble {
/** @return True if the bubble is currently open, false otherwise. */
isBubbleVisible(): boolean;
bubbleIsVisible(): boolean;

/** Sets whether the bubble is open or not. */
setBubbleVisible(visible: boolean): void;
Expand All @@ -15,6 +15,6 @@ export interface IHasBubble {
/** Type guard that checks whether the given object is a IHasBubble. */
export function hasBubble(obj: any): obj is IHasBubble {
return (
obj.isBubbleVisible !== undefined && obj.setBubbleVisible !== undefined
obj.bubbleIsVisible !== undefined && obj.setBubbleVisible !== undefined
);
}
11 changes: 8 additions & 3 deletions core/rendered_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {Connection} from './connection.js';
import type {ConnectionDB} from './connection_db.js';
import {ConnectionType} from './connection_type.js';
import * as eventUtils from './events/utils.js';
import {hasBubble} from './interfaces/i_has_bubble.js';
import * as internalConstants from './internal_constants.js';
import {Coordinate} from './utils/coordinate.js';
import * as dom from './utils/dom.js';
Expand Down Expand Up @@ -428,9 +429,13 @@ export class RenderedConnection extends Connection {
connections[j].setTracking(false);
}
// Close all bubbles of all children.
const icons = block.getIcons();
for (let j = 0; j < icons.length; j++) {
icons[j].setVisible(false);
for (const icon of block.getIcons()) {
if (hasBubble(icon)) {
icon.setBubbleVisible(false);
} else if (icon.setVisible) {
// TODO (#7042): Remove old icon handling code.
icon.setVisible(false);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/renderers/measurables/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class Icon extends Measurable {
// TODO(#7042): Remove references to old icon API.
this.isVisible =
(icon.isVisible && icon.isVisible()) ||
(hasBubble(icon) && icon.isBubbleVisible());
(hasBubble(icon) && icon.bubbleIsVisible());
this.type |= Types.ICON;

const size =
Expand Down
117 changes: 45 additions & 72 deletions tests/mocha/block_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
createChangeListenerSpy,
createMockEvent,
} from './test_helpers/events.js';
import {MockIcon, MockBubbleIcon} from './test_helpers/icon_mocks.js';

suite('Blocks', function () {
setup(function () {
Expand Down Expand Up @@ -1418,38 +1419,6 @@ suite('Blocks', function () {
});

suite('Icon management', function () {
class MockIcon {
getType() {
return 'mock icon';
}

initView() {}

dispose() {}

getWeight() {}

getSize() {
return new Blockly.utils.Size(0, 0);
}

applyColour() {}

hideForInsertionMarker() {}

updateEditable() {}

updateCollapsed() {}

isShownWhenCollapsed() {}

setOffsetInBlock() {}

onLocationChange() {}

onClick() {}
}

class MockIconA extends MockIcon {
getType() {
return 'A';
Expand Down Expand Up @@ -1624,54 +1593,58 @@ suite('Blocks', function () {
workspaceTeardown.call(this, this.workspace);
});

test('Has Icon', function () {
const block = Blockly.Xml.domToBlock(
Blockly.utils.xml.textToDom('<block type="statement_block"/>'),
test("Collapsing the block closes its contained children's bubbles", function () {
const parentBlock = Blockly.serialization.blocks.append(
{
'type': 'statement_block',
'inputs': {
'STATEMENT': {
'block': {
'type': 'statement_block',
},
},
},
},
this.workspace
);
block.setCommentText('test text');
block.comment.setVisible(true);
chai.assert.isTrue(block.comment.isVisible());
block.setCollapsed(true);
chai.assert.isFalse(block.comment.isVisible());
});
const childBlock = parentBlock.getInputTargetBlock('STATEMENT');
const icon = new MockBubbleIcon();
childBlock.addIcon(icon);
icon.setBubbleVisible(true);

test('Child Has Icon', function () {
const block = Blockly.Xml.domToBlock(
Blockly.utils.xml.textToDom(
'<block type="statement_block">' +
' <statement name="STATEMENT">' +
' <block type="statement_block"/>' +
' </statement>' +
'</block>'
),
this.workspace
parentBlock.setCollapsed(true);

chai.assert.isFalse(
icon.bubbleIsVisible(),
"Expected collapsing the parent block to hide the child block's " +
"icon's bubble"
);
const childBlock = block.getInputTargetBlock('STATEMENT');
childBlock.setCommentText('test text');
childBlock.comment.setVisible(true);
chai.assert.isTrue(childBlock.comment.isVisible());
block.setCollapsed(true);
chai.assert.isFalse(childBlock.comment.isVisible());
});

test('Next Block Has Icon', function () {
const block = Blockly.Xml.domToBlock(
Blockly.utils.xml.textToDom(
'<block type="statement_block">' +
' <next>' +
' <block type="statement_block"/>' +
' </next>' +
'</block>'
),
test("Collapsing a block does not close its following childrens' bubbles", function () {
const parentBlock = Blockly.serialization.blocks.append(
{
'type': 'statement_block',
'next': {
'block': {
'type': 'statement_block',
},
},
},
this.workspace
);
const nextBlock = block.getNextBlock();
nextBlock.setCommentText('test text');
nextBlock.comment.setVisible(true);
chai.assert.isTrue(nextBlock.comment.isVisible());
block.setCollapsed(true);
chai.assert.isTrue(nextBlock.comment.isVisible());
const nextBlock = parentBlock.getNextBlock();
const icon = new MockBubbleIcon();
nextBlock.addIcon(icon);
icon.setBubbleVisible(true);

parentBlock.setCollapsed(true);

chai.assert.isTrue(
icon.bubbleIsVisible(),
'Expected collapsing the parent block to not hide the next ' +
"block's bubble"
);
});
});
});
Expand Down
56 changes: 1 addition & 55 deletions tests/mocha/icon_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
sharedTestTeardown,
} from './test_helpers/setup_teardown.js';
import {defineEmptyBlock} from './test_helpers/block_definitions.js';
import {MockIcon, MockSerializableIcon} from './test_helpers/icon_mocks.js';

suite('Icon', function () {
setup(function () {
Expand All @@ -22,61 +23,6 @@ suite('Icon', function () {
sharedTestTeardown.call(this);
});

class MockIcon {
getType() {
return 'mock icon';
}

initView() {}

dispose() {}

getWeight() {}

getSize() {
return new Blockly.utils.Size(0, 0);
}

applyColour() {}

hideForInsertionMarker() {}

updateEditable() {}

updateCollapsed() {}

isShownWhenCollapsed() {}

setOffsetInBlock() {}

onLocationChange() {}

onClick() {}
}

class MockSerializableIcon extends MockIcon {
constructor() {
super();
this.state = '';
}

getType() {
return 'serializable icon';
}

getWeight() {
return 1;
}

saveState() {
return 'some state';
}

loadState(state) {
this.state = state;
}
}

class MockNonSerializableIcon extends MockIcon {
getType() {
return 'non-serializable icon';
Expand Down
81 changes: 81 additions & 0 deletions tests/mocha/test_helpers/icon_mocks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

export class MockIcon {
getType() {
return 'mock icon';
}

initView() {}

dispose() {}

getWeight() {}

getSize() {
return new Blockly.utils.Size(0, 0);
}

applyColour() {}

hideForInsertionMarker() {}

updateEditable() {}

updateCollapsed() {}

isShownWhenCollapsed() {}

setOffsetInBlock() {}

onLocationChange() {}

onClick() {}
}

export class MockSerializableIcon extends MockIcon {
constructor() {
super();
this.state = '';
}

getType() {
return 'serializable icon';
}

getWeight() {
return 1;
}

saveState() {
return 'some state';
}

loadState(state) {
this.state = state;
}
}

export class MockBubbleIcon extends MockIcon {
constructor() {
super();
this.visible = false;
}

getType() {
return 'bubble icon';
}

updateCollapsed() {}

bubbleIsVisible() {
return this.visible;
}

setBubbleVisible(visible) {
this.visible = visible;
}
}

0 comments on commit 6e3d052

Please sign in to comment.