Procházet zdrojové kódy

Added: Postfixer cleaning incorrect blockquotes.

Szymon Cofalik před 7 roky
rodič
revize
2bda7d2f6a

+ 2 - 0
packages/ckeditor5-block-quote/package.json

@@ -23,6 +23,8 @@
     "@ckeditor/ckeditor5-list": "^11.0.1",
     "@ckeditor/ckeditor5-paragraph": "^10.0.2",
     "@ckeditor/ckeditor5-typing": "^11.0.0",
+    "@ckeditor/ckeditor5-engine": "^10.2.0",
+    "@ckeditor/ckeditor5-basic-styles": "^10.0.2",
     "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",

+ 54 - 0
packages/ckeditor5-block-quote/src/blockquoteediting.js

@@ -8,6 +8,9 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Range from '@ckeditor/ckeditor5-engine/src/model/range';
+import Position from '@ckeditor/ckeditor5-engine/src/model/position';
+
 import BlockQuoteCommand from './blockquotecommand';
 
 /**
@@ -40,6 +43,57 @@ export default class BlockQuoteEditing extends Plugin {
 		} );
 
 		editor.conversion.elementToElement( { model: 'blockQuote', view: 'blockquote' } );
+
+		// Postfixer which cleans incorrect model states connected with block quotes.
+		editor.model.document.registerPostFixer( writer => {
+			const changes = editor.model.document.differ.getChanges();
+
+			for ( const entry of changes ) {
+				if ( entry.type == 'insert' ) {
+					const element = entry.position.nodeAfter;
+
+					if ( !element ) {
+						// We are inside a text node.
+						continue;
+					}
+
+					if ( element.is( 'blockQuote' ) && element.isEmpty ) {
+						// Added an empty blockQuote - remove it.
+						writer.remove( element );
+
+						return true;
+					} else if ( element.is( 'blockQuote' ) && !schema.checkChild( entry.position, element ) ) {
+						// Added a blockQuote in incorrect place - most likely inside another blockQuote. Unwrap it
+						// so the content inside is not lost.
+						writer.unwrap( element );
+
+						return true;
+					} else if ( element.is( 'element' ) ) {
+						// Just added an element. Check its children to see if there are no nested blockQuotes somewhere inside.
+						const range = Range.createIn( element );
+
+						for ( const child of range.getItems() ) {
+							if ( child.is( 'blockQuote' ) && !schema.checkChild( Position.createBefore( child ), child ) ) {
+								writer.unwrap( child );
+
+								return true;
+							}
+						}
+					}
+				} else if ( entry.type == 'remove' ) {
+					const parent = entry.position.parent;
+
+					if ( parent.is( 'blockQuote' ) && parent.isEmpty ) {
+						// Something got removed and now blockQuote is empty. Remove the blockQuote as well.
+						writer.remove( parent );
+
+						return true;
+					}
+				}
+			}
+
+			return false;
+		} );
 	}
 
 	/**

+ 65 - 1
packages/ckeditor5-block-quote/tests/blockquoteediting.js

@@ -6,6 +6,9 @@
 import BlockQuoteEditing from '../src/blockquoteediting';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import ListEditing from '@ckeditor/ckeditor5-list/src/listediting';
+import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting';
+
+import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -18,7 +21,7 @@ describe( 'BlockQuoteEditing', () => {
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ BlockQuoteEditing, Paragraph ]
+				plugins: [ BlockQuoteEditing, Paragraph, BoldEditing ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
@@ -77,4 +80,65 @@ describe( 'BlockQuoteEditing', () => {
 				expect( editor.getData() ).to.equal( '<blockquote><ul><li>xx</li></ul></blockquote>' );
 			} );
 	} );
+
+	it( 'should remove empty blockQuote elements', () => {
+		setModelData( model, '<blockQuote></blockQuote><paragraph>Foo</paragraph>' );
+
+		expect( editor.getData() ).to.equal( '<p>Foo</p>' );
+	} );
+
+	it( 'should remove blockQuotes which became empty', () => {
+		setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
+
+		model.change( writer => {
+			const root = model.document.getRoot();
+			const bq = root.getChild( 0 );
+
+			writer.remove( Range.createIn( bq ) );
+		} );
+
+		expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' ); // Autoparagraphed.
+	} );
+
+	it( 'should unwrap a blockQuote if it was inserted into another blockQuote', () => {
+		setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
+
+		model.change( writer => {
+			const root = model.document.getRoot();
+			const bq = writer.createElement( 'blockQuote' );
+			const p = writer.createElement( 'paragraph' );
+
+			writer.insertText( 'Bar', p, 0 ); // <p>Bar</p>.
+			writer.insert( p, bq, 0 ); // <blockquote><p>Bar</p></blockquote>.
+			writer.insert( bq, root.getChild( 0 ), 1 ); // Insert after <p>Foo</p>.
+		} );
+
+		expect( editor.getData() ).to.equal( '<blockquote><p>Foo</p><p>Bar</p></blockquote>' );
+	} );
+
+	it( 'should unwrap nested blockQuote if it was wrapped into another blockQuote', () => {
+		setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote><paragraph>Bar</paragraph>' );
+
+		model.change( writer => {
+			const root = model.document.getRoot();
+
+			writer.wrap( Range.createIn( root ), 'blockQuote' );
+		} );
+
+		expect( editor.getData() ).to.equal( '<blockquote><p>Foo</p><p>Bar</p></blockquote>' );
+	} );
+
+	it( 'postfixer should do nothing on attribute change', () => {
+		// This is strictly a 100% CC test.
+		setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
+
+		model.change( writer => {
+			const root = model.document.getRoot();
+			const p = root.getChild( 0 ).getChild( 0 );
+
+			writer.setAttribute( 'bold', true, Range.createIn( p ) );
+		} );
+
+		expect( editor.getData() ).to.equal( '<blockquote><p><strong>Foo</strong></p></blockquote>' );
+	} );
 } );