Skip to content

Commit

Permalink
fix: insertion markers and change events to work with JSO hooks (#5378)
Browse files Browse the repository at this point in the history
* fix: add tests for fixing change events

* fix: change events and insertion markers

* fix: build:

* fix: remove duplicate code

* fix: requires
  • Loading branch information
BeksOmega authored and alschmiedt committed Sep 20, 2021
1 parent 1d4cbd1 commit 486123e
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 22 deletions.
40 changes: 30 additions & 10 deletions core/events/events_block_change.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ goog.module.declareLegacyNamespace();

/* eslint-disable-next-line no-unused-vars */
const Block = goog.requireType('Blockly.Block');
/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
const Events = goog.require('Blockly.Events');
const Xml = goog.require('Blockly.Xml');
const object = goog.require('Blockly.utils.object');
Expand Down Expand Up @@ -125,24 +127,42 @@ BlockChange.prototype.run = function(forward) {
block.setInputsInline(!!value);
break;
case 'mutation': {
let oldMutation = '';
if (block.mutationToDom) {
const oldMutationDom = block.mutationToDom();
oldMutation = oldMutationDom && Xml.domToText(oldMutationDom);
}
if (block.domToMutation) {
const dom = Xml.textToDom(/** @type {string} */
(value) || '<mutation/>');
block.domToMutation(dom);
const oldState = BlockChange.getExtraBlockState_(
/** @type {!BlockSvg} */ (block));
if (block.loadExtraState) {
block.loadExtraState(JSON.parse(/** @type {string} */ (value) || '{}'));
} else if (block.domToMutation) {
block.domToMutation(
Xml.textToDom(/** @type {string} */ (value) || '<mutation/>'));
}
Events.fire(new BlockChange(block, 'mutation', null, oldMutation, value));
Events.fire(new BlockChange(block, 'mutation', null, oldState, value));
break;
}
default:
console.warn('Unknown change type: ' + this.element);
}
};

// TODO (#5397): Encapsulate this in the BlocklyMutationChange event when
// refactoring change events.
/**
* Returns the extra state of the given block (either as XML or a JSO, depending
* on the block's definition).
* @param {!BlockSvg} block The block to get the extra state of.
* @return {string} A stringified version of the extra state of the given block.
* @package
*/
BlockChange.getExtraBlockState_ = function(block) {
if (block.saveExtraState) {
const state = block.saveExtraState();
return state ? JSON.stringify(state) : '';
} else if (block.mutationToDom) {
const state = block.mutationToDom();
return state ? Xml.domToText(state) : '';
}
return '';
};

registry.register(registry.Type.EVENT, Events.CHANGE, BlockChange);

exports = BlockChange;
2 changes: 1 addition & 1 deletion core/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -897,11 +897,11 @@ Field.prototype.setValue = function(newValue) {
return;
}

this.doValueUpdate_(newValue);
if (source && Events.isEnabled()) {
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
source, 'field', this.name || null, oldValue, newValue));
}
this.doValueUpdate_(newValue);
if (this.isDirty_) {
this.forceRerender();
}
Expand Down
7 changes: 6 additions & 1 deletion core/insertion_marker_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,12 @@ InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlock) {
try {
result = this.workspace_.newBlock(imType);
result.setInsertionMarker(true);
if (sourceBlock.mutationToDom) {
if (sourceBlock.saveExtraState) {
const state = sourceBlock.saveExtraState();
if (state) {
result.loadExtraState(state);
}
} else if (sourceBlock.mutationToDom) {
const oldMutationDom = sourceBlock.mutationToDom();
if (oldMutationDom) {
result.domToMutation(oldMutationDom);
Expand Down
13 changes: 5 additions & 8 deletions core/mutator.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const Svg = goog.require('Blockly.utils.Svg');
/* eslint-disable-next-line no-unused-vars */
const Workspace = goog.requireType('Blockly.Workspace');
const WorkspaceSvg = goog.require('Blockly.WorkspaceSvg');
const Xml = goog.require('Blockly.Xml');
const dom = goog.require('Blockly.utils.dom');
const internalConstants = goog.require('Blockly.internalConstants');
const object = goog.require('Blockly.utils.object');
Expand Down Expand Up @@ -408,9 +407,8 @@ Mutator.prototype.workspaceChanged_ = function(e) {
// When the mutator's workspace changes, update the source block.
if (this.rootBlock_.workspace == this.workspace_) {
Events.setGroup(true);
const block = this.block_;
const oldMutationDom = block.mutationToDom();
const oldMutation = oldMutationDom && Xml.domToText(oldMutationDom);
const block = /** @type {!BlockSvg} */ (this.block_);
const oldExtraState = Events.BlockChange.getExtraBlockState_(block);

// Switch off rendering while the source block is rebuilt.
const savedRendered = block.rendered;
Expand All @@ -428,11 +426,10 @@ Mutator.prototype.workspaceChanged_ = function(e) {
block.render();
}

const newMutationDom = block.mutationToDom();
const newMutation = newMutationDom && Xml.domToText(newMutationDom);
if (oldMutation != newMutation) {
const newExtraState = Events.BlockChange.getExtraBlockState_(block);
if (oldExtraState != newExtraState) {
Events.fire(new (Events.get(Events.BLOCK_CHANGE))(
block, 'mutation', null, oldMutation, newMutation));
block, 'mutation', null, oldExtraState, newExtraState));
// Ensure that any bump is part of this mutation's event group.
const group = Events.getGroup();
setTimeout(function() {
Expand Down
2 changes: 1 addition & 1 deletion tests/deps.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion tests/deps.mocha.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions tests/mocha/block_change_event_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

goog.module('Blockly.test.blockChangeEvent');

const {defineMutatorBlocks, sharedTestSetup, sharedTestTeardown} = goog.require('Blockly.test.helpers');


suite('Block Change Event', function() {
setup(function() {
sharedTestSetup.call(this);
this.workspace = new Blockly.Workspace();
});

teardown(function() {
sharedTestTeardown.call(this);
});

suite('Undo and Redo', function() {
suite('Mutation', function() {
setup(function() {
defineMutatorBlocks();
});

teardown(function() {
Blockly.Extensions.unregister('xml_mutator');
Blockly.Extensions.unregister('jso_mutator');
});

suite('XML', function() {
test('Undo', function() {
const block = this.workspace.newBlock('xml_block', 'block_id');
block.domToMutation(
Blockly.Xml.textToDom('<mutation hasInput="true"/>'));
const blockChange = new Blockly.Events.BlockChange(
block, 'mutation', null, '', '<mutation hasInput="true"/>');
blockChange.run(false);
chai.assert.isFalse(block.hasInput);
});

test('Redo', function() {
const block = this.workspace.newBlock('xml_block', 'block_id');
const blockChange = new Blockly.Events.BlockChange(
block, 'mutation', null, '', '<mutation hasInput="true"/>');
blockChange.run(true);
chai.assert.isTrue(block.hasInput);
});
});

suite('JSO', function() {
test('Undo', function() {
const block = this.workspace.newBlock('jso_block', 'block_id');
block.loadExtraState({hasInput: true});
const blockChange = new Blockly.Events.BlockChange(
block, 'mutation', null, '', '{"hasInput":true}');
blockChange.run(false);
chai.assert.isFalse(block.hasInput);
});

test('Redo', function() {
const block = this.workspace.newBlock('jso_block', 'block_id');
const blockChange = new Blockly.Events.BlockChange(
block, 'mutation', null, '', '{"hasInput":true}');
blockChange.run(true);
chai.assert.isTrue(block.hasInput);
});
});
});
});
});
2 changes: 2 additions & 0 deletions tests/mocha/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

<script>
goog.require('Blockly.test.astNode');
goog.require('Blockly.test.blockChangeEvent');
goog.require('Blockly.test.blockJson');
goog.require('Blockly.test.blocks');
goog.require('Blockly.test.comments');
Expand Down Expand Up @@ -88,6 +89,7 @@
goog.require('Blockly.test.keydown');
goog.require('Blockly.test.logicTernary');
goog.require('Blockly.test.metrics');
goog.require('Blockly.test.mutator');
goog.require('Blockly.test.names');
goog.require('Blockly.test.procedures');
goog.require('Blockly.test.registry');
Expand Down
71 changes: 71 additions & 0 deletions tests/mocha/mutator_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

goog.module('Blockly.test.mutator');

const {createRenderedBlock, defineMutatorBlocks, sharedTestSetup, sharedTestTeardown} = goog.require('Blockly.test.helpers');


suite('Mutator', function() {
setup(function() {
sharedTestSetup.call(this);
});

suite('Firing change event', function() {
setup(function() {
this.workspace = Blockly.inject('blocklyDiv', {});
defineMutatorBlocks();
});

teardown(function() {
Blockly.Extensions.unregister('xml_mutator');
Blockly.Extensions.unregister('jso_mutator');
sharedTestTeardown.call(this);
});

test('No change', function() {
var block = createRenderedBlock(this.workspace, 'xml_block');
block.mutator.setVisible(true);
var mutatorWorkspace = block.mutator.getWorkspace();
// Trigger mutator change listener.
createRenderedBlock(mutatorWorkspace, 'checkbox_block');
chai.assert.isTrue(
this.eventsFireStub.getCalls().every(
({args}) =>
args[0].type !== Blockly.Events.BLOCK_CHANGE ||
args[0].element !== 'mutation'));
});

test('XML', function() {
var block = createRenderedBlock(this.workspace, 'xml_block');
block.mutator.setVisible(true);
var mutatorWorkspace = block.mutator.getWorkspace();
mutatorWorkspace.getBlockById('check_block')
.setFieldValue('TRUE', 'CHECK');
chai.assert.isTrue(
this.eventsFireStub.getCalls().some(
({args}) =>
args[0].type === Blockly.Events.BLOCK_CHANGE &&
args[0].element === 'mutation' &&
/<mutation.*><\/mutation>/.test(args[0].newValue)));
});

test('JSO', function() {
var block = createRenderedBlock(this.workspace, 'jso_block');
block.mutator.setVisible(true);
var mutatorWorkspace = block.mutator.getWorkspace();
mutatorWorkspace.getBlockById('check_block')
.setFieldValue('TRUE', 'CHECK');
chai.assert.isTrue(
this.eventsFireStub.getCalls().some(
({args}) =>
args[0].type === Blockly.Events.BLOCK_CHANGE &&
args[0].element === 'mutation' &&
args[0].newValue === '{"hasInput":true}'));
});
});
});
Loading

0 comments on commit 486123e

Please sign in to comment.