8
0
Pārlūkot izejas kodu

More tests and changes in DOM converter in order to handle UIElements.

Szymon Kupś 8 gadi atpakaļ
vecāks
revīzija
27eb91adf3

+ 21 - 5
packages/ckeditor5-engine/src/view/domconverter.js

@@ -195,24 +195,34 @@ export default class DomConverter {
 			if ( viewNode.is( 'documentFragment' ) ) {
 				// Create DOM document fragment.
 				domElement = domDocument.createDocumentFragment();
+
+				if ( options.bind ) {
+					this.bindDocumentFragments( domElement, viewNode );
+				}
 			} else if ( viewNode.is( 'uiElement' ) ) {
 				// UIElement has it's own render() method.
 				// https://github.com/ckeditor/ckeditor5-engine/issues/799
 				domElement = viewNode.render( domDocument );
+
+				if ( options.bind ) {
+					this.bindElements( domElement, viewNode );
+				}
+
+				return domElement;
 			} else {
 				// Create DOM element.
 				domElement = domDocument.createElement( viewNode.name );
 
+				if ( options.bind ) {
+					this.bindElements( domElement, viewNode );
+				}
+
 				// Copy element's attributes.
 				for ( const key of viewNode.getAttributeKeys() ) {
 					domElement.setAttribute( key, viewNode.getAttribute( key ) );
 				}
 			}
 
-			if ( options.bind ) {
-				this.bindDocumentFragments( domElement, viewNode );
-			}
-
 			if ( options.withChildren || options.withChildren === undefined ) {
 				for ( const child of this.viewChildrenToDom( viewNode, domDocument, options ) ) {
 					domElement.appendChild( child );
@@ -353,7 +363,7 @@ export default class DomConverter {
 			return null;
 		}
 
-		// When node is inside UIElement it has no representation in the view.
+		// When node is inside UIElement return that UIElement as it's view representation.
 		const uiElement = this.getParentUIElement( domNode, this._domToViewMapping );
 
 		if ( uiElement ) {
@@ -506,6 +516,12 @@ export default class DomConverter {
 			return this.domPositionToView( domParent.parentNode, indexOf( domParent ) );
 		}
 
+		// If position inside UIElement - return position before.
+		const viewElement = this.getCorrespondingViewElement( domParent );
+		if ( viewElement && viewElement.is( 'uiElement' ) ) {
+			return ViewPosition.createBefore( viewElement );
+		}
+
 		if ( this.isText( domParent ) ) {
 			if ( isInlineFiller( domParent ) ) {
 				return this.domPositionToView( domParent.parentNode, indexOf( domParent ) );

+ 0 - 23
packages/ckeditor5-engine/tests/view/domconverter/dom-to-view.js

@@ -8,7 +8,6 @@
 import ViewElement from '../../../src/view/element';
 import ViewRange from '../../../src/view/range';
 import ViewSelection from '../../../src/view/selection';
-import ViewUIElement from '../../../src/view/uielement';
 import DomConverter from '../../../src/view/domconverter';
 import ViewDocumentFragment from '../../../src/view/documentfragment';
 import { INLINE_FILLER, INLINE_FILLER_LENGTH, NBSP_FILLER } from '../../../src/view/filler';
@@ -176,28 +175,6 @@ describe( 'DomConverter', () => {
 			expect( converter.domToView( comment ) ).to.be.null;
 		} );
 
-		it( 'should return UIElement for nodes inside', () => {
-			class MyUIElement extends ViewUIElement {
-				render( doc ) {
-					const root = super.render( doc );
-					root.innerHTML = '<p>foo</p><span>bar</span>';
-
-					return root;
-				}
-			}
-
-			const uiElement = new MyUIElement( 'div' );
-			const domElement = converter.viewToDom( uiElement, document, { bind: true, withChildren: true } );
-
-			const domParagraph = domElement.childNodes[ 0 ];
-			const domSpan = domElement.childNodes[ 1 ];
-
-			expect( converter.domToView( domParagraph ) ).to.equal( uiElement );
-			expect( converter.domToView( domSpan ) ).to.be.equal( uiElement );
-			expect( converter.domToView( domParagraph.childNodes[ 0 ] ) ).equal( uiElement );
-			expect( converter.domToView( domSpan.childNodes[ 0 ] ) ).equal( uiElement );
-		} );
-
 		describe( 'it should clear whitespaces', () => {
 			it( 'at the beginning of block element', () => {
 				const domDiv = createElement( document, 'div', {}, [

+ 100 - 0
packages/ckeditor5-engine/tests/view/domconverter/uielement.js

@@ -0,0 +1,100 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document, HTMLElement */
+
+import ViewUIElement from '../../../src/view/uielement';
+import ViewContainer from '../../../src/view/containerelement';
+import DomConverter from '../../../src/view/domconverter';
+
+describe( 'DOMConverter UIElement integration', () => {
+	let converter;
+
+	class MyUIElement extends ViewUIElement {
+		render( domDocument ) {
+			const root = super.render( domDocument );
+			root.innerHTML = '<p><span>foo</span> bar</p>';
+
+			return root;
+		}
+	}
+
+	beforeEach( () => {
+		converter = new DomConverter();
+	} );
+
+	describe( 'viewToDom', () => {
+		it( 'should create DOM element from UIElement', () => {
+			const uiElement = new ViewUIElement( 'div' );
+			const domElement = converter.viewToDom( uiElement, document );
+
+			expect( domElement ).to.be.instanceOf( HTMLElement );
+		} );
+
+		it( 'should create DOM structure from UIElement', () => {
+			const myElement = new MyUIElement( 'div' );
+			const domElement = converter.viewToDom( myElement, document );
+
+			expect( domElement ).to.be.instanceOf( HTMLElement );
+			expect( domElement.innerHTML ).to.equal( '<p><span>foo</span> bar</p>' );
+		} );
+
+		it( 'should not bind rendered elements', () => {
+			const myElement = new MyUIElement( 'div' );
+			const domElement = converter.viewToDom( myElement, document, { bind: true } );
+			const domSpan = domElement.childNodes[ 0 ];
+
+			expect( converter.getCorrespondingView( domElement ) ).to.equal( myElement );
+			expect( converter.getCorrespondingView( domSpan ) ).to.be.falsy;
+			expect( converter.getCorrespondingView( domSpan.childNodes[ 0 ] ) ).to.be.falsy;
+		} );
+	} );
+
+	describe( 'domToView', () => {
+		it( 'should return UIElement itself', () => {
+			const uiElement = new MyUIElement( 'div' );
+			const domElement = converter.viewToDom( uiElement, document, { bind: true } );
+
+			expect( converter.domToView( domElement ) ).to.equal( uiElement );
+		} );
+
+		it( 'should return UIElement for nodes inside', () => {
+			const uiElement = new MyUIElement( 'div' );
+			const domElement = converter.viewToDom( uiElement, document, { bind: true } );
+
+			const domParagraph = domElement.childNodes[ 0 ];
+			const domSpan = domParagraph.childNodes[ 0 ];
+
+			expect( converter.domToView( domParagraph ) ).to.equal( uiElement );
+			expect( converter.domToView( domSpan ) ).to.be.equal( uiElement );
+			expect( converter.domToView( domParagraph.childNodes[ 0 ] ) ).equal( uiElement );
+			expect( converter.domToView( domSpan.childNodes[ 0 ] ) ).equal( uiElement );
+		} );
+	} );
+
+	describe( 'domPositionToView', () => {
+		it( 'should convert position inside UIElement to position before it', () => {
+			const uiElement = new MyUIElement( 'h1' );
+			const container = new ViewContainer( 'div', null, [ new ViewContainer( 'div' ), uiElement ] );
+			const domContainer = converter.viewToDom( container, document, { bind: true } );
+
+			const viewPosition = converter.domPositionToView( domContainer.childNodes[ 1 ], 0 );
+
+			expect( viewPosition.parent ).to.equal( container );
+			expect( viewPosition.offset ).to.equal( 1 );
+		} );
+
+		it( 'should convert position inside UIElement children to position before UIElement', () => {
+			const uiElement = new MyUIElement( 'h1' );
+			const container = new ViewContainer( 'div', null, [ new ViewContainer( 'div' ), uiElement ] );
+			const domContainer = converter.viewToDom( container, document, { bind: true } );
+
+			const viewPosition = converter.domPositionToView( domContainer.childNodes[ 1 ].childNodes[ 0 ], 1 );
+
+			expect( viewPosition.parent ).to.equal( container );
+			expect( viewPosition.offset ).to.equal( 1 );
+		} );
+	} );
+} );

+ 0 - 37
packages/ckeditor5-engine/tests/view/domconverter/view-to-dom.js

@@ -8,7 +8,6 @@
 import ViewText from '../../../src/view/text';
 import ViewElement from '../../../src/view/element';
 import ViewPosition from '../../../src/view/position';
-import ViewUIElement from '../../../src/view/uielement';
 import ViewContainerElement from '../../../src/view/containerelement';
 import ViewAttributeElement from '../../../src/view/attributeelement';
 import DomConverter from '../../../src/view/domconverter';
@@ -180,42 +179,6 @@ describe( 'DomConverter', () => {
 			expect( domTextNode.data ).to.equal( 'foo' );
 		} );
 
-		describe( 'UIElement', () => {
-			class MyUIElement extends ViewUIElement {
-				render( domDocument ) {
-					const root = super.render( domDocument );
-					root.innerHTML = '<span>foo bar baz</span>';
-
-					return root;
-				}
-			}
-
-			it( 'should create DOM element from UIElement', () => {
-				const uiElement = new ViewUIElement( 'div' );
-				const domElement = converter.viewToDom( uiElement, document );
-
-				expect( domElement ).to.be.instanceOf( HTMLElement );
-			} );
-
-			it( 'should create DOM structure from UIElement', () => {
-				const myElement = new MyUIElement( 'div' );
-				const domElement = converter.viewToDom( myElement, document );
-
-				expect( domElement ).to.be.instanceOf( HTMLElement );
-				expect( domElement.innerHTML ).to.equal( '<span>foo bar baz</span>' );
-			} );
-
-			it( 'should not bind rendered elements', () => {
-				const myElement = new MyUIElement( 'div' );
-				const domElement = converter.viewToDom( myElement, document, { bind: true } );
-				const domSpan = domElement.childNodes[ 0 ];
-
-				expect( converter.getCorrespondingView( domElement ) ).to.equal( myElement );
-				expect( converter.getCorrespondingView( domSpan ) ).to.be.falsy;
-				expect( converter.getCorrespondingView( domSpan.childNodes[ 0 ] ) ).to.be.falsy;
-			} );
-		} );
-
 		describe( 'it should convert spaces to &nbsp;', () => {
 			it( 'at the beginning of each container element', () => {
 				const viewDiv = new ViewContainerElement( 'div', null, [

+ 1 - 0
packages/ckeditor5-engine/tests/view/manual/uielement.html

@@ -0,0 +1 @@
+<div id="editor"><p>This is editor instance. <span></span> Text after UIElement</p></div>

+ 54 - 0
packages/ckeditor5-engine/tests/view/manual/uielement.js

@@ -0,0 +1,54 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Undo from '@ckeditor/ckeditor5-undo/src/undo';
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+
+import buildModelConverter from '../../../src/conversion/buildmodelconverter';
+import buildViewConverter from '../../../src/conversion/buildviewconverter';
+import UIElement from '../../../src/view/uielement';
+
+class MyUIElement extends UIElement {
+	render( domDocument ) {
+		const root = super.render( domDocument );
+
+		root.setAttribute( 'contenteditable', 'false' );
+		root.style.backgroundColor = 'red';
+		root.innerHTML = 'This is UIElement';
+
+		return root;
+	}
+}
+
+class UIElementTestPlugin extends Plugin {
+	init() {
+		const editor = this.editor;
+		const document = editor.document;
+		const editing = editor.editing;
+		const data = editor.data;
+		const schema = document.schema;
+
+		schema.registerItem( 'span', '$inline' );
+
+		buildModelConverter().for( data.modelToView, editing.modelToView )
+			.fromElement( 'span' )
+			.toElement( () => new MyUIElement( 'span' ) );
+
+		buildViewConverter().for( data.viewToModel )
+			.fromElement( 'span' )
+			.toElement( 'span' );
+	}
+}
+
+ClassicEditor.create( document.querySelector( '#editor' ), {
+	plugins: [ Enter, Typing, Paragraph, Undo, UIElementTestPlugin ],
+	toolbar: [ 'undo', 'redo' ]
+} );

+ 1 - 0
packages/ckeditor5-engine/tests/view/manual/uielement.md

@@ -0,0 +1 @@
+### UIElement handling