diff --git a/packages/ckeditor5-engine/src/controller/datacontroller.js b/packages/ckeditor5-engine/src/controller/datacontroller.js index eab30b12e8e..52e9ec2029e 100644 --- a/packages/ckeditor5-engine/src/controller/datacontroller.js +++ b/packages/ckeditor5-engine/src/controller/datacontroller.js @@ -133,6 +133,7 @@ export default class DataController { this.upcastDispatcher.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } ); this.decorate( 'init' ); + this.decorate( 'set' ); // Fire `ready` event when initialisation has completed. Such low level listener gives possibility // to plug into initialisation pipeline without interrupting the initialisation flow. @@ -317,6 +318,7 @@ export default class DataController { * * dataController.set( { main: '

Foo

', title: '

Bar

' } ); // Sets data on the `main` and `title` roots. * + * @fires set * @param {String|Object.} data Input data as a string or an object containing `rootName` - `data` * pairs to set data on multiple roots at once. */ @@ -452,6 +454,15 @@ export default class DataController { * * @event init */ + + /** + * Event fired after {@link #set set() method} has been run. + * + * The `set` event is fired by decorated {@link #set} method. + * See {@link module:utils/observablemixin~ObservableMixin#decorate} for more information and samples. + * + * @event set + */ } mix( DataController, ObservableMixin ); diff --git a/packages/ckeditor5-engine/tests/controller/datacontroller.js b/packages/ckeditor5-engine/tests/controller/datacontroller.js index cdd2b60eb2d..e21c1bf2967 100644 --- a/packages/ckeditor5-engine/tests/controller/datacontroller.js +++ b/packages/ckeditor5-engine/tests/controller/datacontroller.js @@ -258,6 +258,16 @@ describe( 'DataController', () => { } ); describe( 'set()', () => { + it( 'should be decorated', () => { + const spy = sinon.spy(); + + data.on( 'set', spy ); + + data.set( 'foo bar' ); + + sinon.assert.calledWithExactly( spy, sinon.match.any, [ 'foo bar' ] ); + } ); + it( 'should set data to default main root', () => { schema.extend( '$text', { allowIn: '$root' } ); data.set( 'foo' ); diff --git a/packages/ckeditor5-undo/src/basecommand.js b/packages/ckeditor5-undo/src/basecommand.js index 5d272ebf714..ff54d00ee22 100644 --- a/packages/ckeditor5-undo/src/basecommand.js +++ b/packages/ckeditor5-undo/src/basecommand.js @@ -41,6 +41,8 @@ export default class BaseCommand extends Command { // Refresh state, so the command is inactive right after initialization. this.refresh(); + + this.listenTo( editor.data, 'set', () => this.clearStack() ); } /** diff --git a/packages/ckeditor5-undo/tests/redocommand.js b/packages/ckeditor5-undo/tests/redocommand.js index 51d9bf4bc07..a76a65fa83e 100644 --- a/packages/ckeditor5-undo/tests/redocommand.js +++ b/packages/ckeditor5-undo/tests/redocommand.js @@ -288,5 +288,13 @@ describe( 'RedoCommand', () => { expect( undoSpy.firstCall.args[ 1 ] ).to.equal( redoingBatch ); } ); } ); + + it( 'should clear stack on DataController set()', () => { + const spy = sinon.stub( redo, 'clearStack' ); + + editor.setData( 'foo' ); + + sinon.assert.called( spy ); + } ); } ); } ); diff --git a/packages/ckeditor5-undo/tests/undocommand.js b/packages/ckeditor5-undo/tests/undocommand.js index fd02da418b9..b7056590b2d 100644 --- a/packages/ckeditor5-undo/tests/undocommand.js +++ b/packages/ckeditor5-undo/tests/undocommand.js @@ -347,5 +347,13 @@ describe( 'UndoCommand', () => { expect( getCaseText( root ) ).to.equal( 'adbcef' ); expect( editor.model.document.selection.getFirstRange().isEqual( r( 1, 4 ) ) ).to.be.true; } ); + + it( 'should clear stack on DataController set()', () => { + const spy = sinon.stub( undo, 'clearStack' ); + + editor.setData( 'foo' ); + + sinon.assert.called( spy ); + } ); } ); } );