Bladeren bron

Merge pull request #7650 from ckeditor/i/4060

Fix (undo): Undo/redo stacks should be cleared on `DataController#set()`. Closes #4060.
Feature (engine): The method `DataController#set()` is now decorated so plugins can listen to `editor.setData()` calls.

MAJOR BREAKING CHANGE: The method `editor.setData()` clears the undo and redo stacks.
Maciej 5 jaren geleden
bovenliggende
commit
4a12d38094

+ 11 - 0
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: '<p>Foo</p>', title: '<h1>Bar</h1>' } ); // Sets data on the `main` and `title` roots.
 	 *
+	 * @fires set
 	 * @param {String|Object.<String,String>} 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 );

+ 10 - 0
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' );

+ 2 - 0
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() );
 	}
 
 	/**

+ 8 - 0
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 );
+		} );
 	} );
 } );

+ 8 - 0
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 );
+		} );
 	} );
 } );