Browse Source

Merge pull request #38 from ckeditor/t/37

Feature: Editor can now be created with initial data passed to the constructor. Closes #37.

BREAKING CHANGE: The `InlineEditor#element` property was renamed to `InlineEditor#sourceElement`. See ckeditor/ckeditor5-core#64.
Piotrek Koszuliński 7 years ago
parent
commit
a46c6b2478

+ 61 - 13
packages/ckeditor5-editor-inline/src/inlineeditor.js

@@ -17,6 +17,7 @@ import InlineEditorUIView from './inlineeditoruiview';
 import setDataInElement from '@ckeditor/ckeditor5-utils/src/dom/setdatainelement';
 import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
+import isElement from '@ckeditor/ckeditor5-utils/src/lib/lodash/isElement';
 
 /**
  * The {@glink builds/guides/overview#inline-editor inline editor} implementation.
@@ -53,24 +54,34 @@ export default class InlineEditor extends Editor {
 	 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`} method instead.
 	 *
 	 * @protected
-	 * @param {HTMLElement} element The DOM element that will be the source for the created editor
-	 * (on which the editor will be initialized).
+	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
+	 * (on which the editor will be initialized) or initial data for the editor. For more information see
+	 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`}.
 	 * @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
 	 */
-	constructor( element, config ) {
+	constructor( sourceElementOrData, config ) {
 		super( config );
 
-		this.element = element;
-
 		this.data.processor = new HtmlDataProcessor();
 
 		this.model.document.createRoot();
 
-		this.ui = new InlineEditorUI( this, new InlineEditorUIView( this.locale, element ) );
+		if ( isElement( sourceElementOrData ) ) {
+			this.sourceElement = sourceElementOrData;
+		}
+
+		this.ui = new InlineEditorUI( this, new InlineEditorUIView( this.locale, this.sourceElement ) );
 
 		attachToForm( this );
 	}
 
+	/**
+	 * @inheritDoc
+	 */
+	get element() {
+		return this.ui.view.editable.element;
+	}
+
 	/**
 	 * Destroys the editor instance, releasing all resources used by it.
 	 *
@@ -86,11 +97,15 @@ export default class InlineEditor extends Editor {
 		this.ui.destroy();
 
 		return super.destroy()
-			.then( () => setDataInElement( this.element, data ) );
+			.then( () => {
+				if ( this.sourceElement ) {
+					setDataInElement( this.sourceElement, data );
+				}
+			} );
 	}
 
 	/**
-	 * Creates a inline editor instance.
+	 * Creates an inline editor instance.
 	 *
 	 * Creating instance when using {@glink builds/index CKEditor build}:
 	 *
@@ -123,15 +138,42 @@ export default class InlineEditor extends Editor {
 	 *				console.error( err.stack );
 	 *			} );
 	 *
-	 * @param {HTMLElement} element The DOM element that will be the source for the created editor
-	 * (on which the editor will be initialized).
+	 * Creating instance when using initial data instead of a DOM element:
+	 *
+	 *		import InlineEditor from '@ckeditor/ckeditor5-editor-inline/src/inlineeditor';
+	 *		import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
+	 *		import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+	 *		import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+	 *		import ...
+	 *
+	 *		InlineEditor
+	 *			.create( '<p>Hello world!</p>' )
+	 *			.then( editor => {
+	 *				console.log( 'Editor was initialized', editor );
+	 *
+	 *				// Initial data was provided so `editor.element` needs to be added manually to the DOM.
+	 *				document.body.appendChild( editor.element );
+	 *			} )
+	 *			.catch( err => {
+	 *				console.error( err.stack );
+	 *			} );
+	 *
+	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
+	 * (on which the editor will be initialized) or initial data for the editor.
+	 *
+	 * If a source element is passed, then its contents will be automatically
+	 * {@link module:editor-classic/inlineeditor~InlineEditor#setData loaded} to the editor on startup and the element
+	 * itself will be used as the editor's editable element.
+	 *
+	 * If a data is provided, then `editor.element` will be created automatically and needs to be added
+	 * manually to the DOM.
 	 * @param {module:core/editor/editorconfig~EditorConfig} config The editor configuration.
 	 * @returns {Promise} A promise resolved once the editor is ready.
 	 * The promise returns the created {@link module:editor-inline/inlineeditor~InlineEditor} instance.
 	 */
-	static create( element, config ) {
+	static create( sourceElementOrData, config ) {
 		return new Promise( resolve => {
-			const editor = new this( element, config );
+			const editor = new this( sourceElementOrData, config );
 
 			resolve(
 				editor.initPlugins()
@@ -139,7 +181,13 @@ export default class InlineEditor extends Editor {
 						editor.ui.init();
 						editor.fire( 'uiReady' );
 					} )
-					.then( () => editor.data.init( getDataFromElement( element ) ) )
+					.then( () => {
+						const initialData = isElement( sourceElementOrData ) ?
+							getDataFromElement( sourceElementOrData ) :
+							sourceElementOrData;
+
+						return editor.data.init( initialData );
+					} )
 					.then( () => {
 						editor.fire( 'dataReady' );
 						editor.fire( 'ready' );

+ 34 - 2
packages/ckeditor5-editor-inline/tests/inlineeditor.js

@@ -54,11 +54,11 @@ describe( 'InlineEditor', () => {
 		} );
 
 		it( 'has a Data Interface', () => {
-			testUtils.isMixed( InlineEditor, DataApiMixin );
+			expect( testUtils.isMixed( InlineEditor, DataApiMixin ) ).to.be.true;
 		} );
 
 		it( 'has a Element Interface', () => {
-			testUtils.isMixed( InlineEditor, ElementApiMixin );
+			expect( testUtils.isMixed( InlineEditor, ElementApiMixin ) ).to.be.true;
 		} );
 
 		it( 'creates main root element', () => {
@@ -95,6 +95,30 @@ describe( 'InlineEditor', () => {
 				} );
 			} );
 		} );
+
+		it( 'allows to pass data to the constructor', () => {
+			return InlineEditor.create( '<p>Hello world!</p>', {
+				plugins: [ Paragraph ]
+			} ).then( editor => {
+				expect( editor.getData() ).to.equal( '<p>Hello world!</p>' );
+			} );
+		} );
+
+		it( 'should have undefined the #sourceElement if editor was initialized with data', () => {
+			return InlineEditor.create( '<p>Hello world!</p>', {
+				plugins: [ Paragraph ]
+			} ).then( editor => {
+				expect( editor.sourceElement ).to.be.undefined;
+			} );
+		} );
+
+		it( 'editor.element should contain the whole editor (with UI) element', () => {
+			return InlineEditor.create( '<p>Hello world!</p>', {
+				plugins: [ Paragraph ]
+			} ).then( editor => {
+				expect( editor.editing.view.getDomRoot() ).to.equal( editor.element );
+			} );
+		} );
 	} );
 
 	describe( 'create()', () => {
@@ -264,5 +288,13 @@ describe( 'InlineEditor', () => {
 						.to.equal( '<p>a</p><heading>b</heading>' );
 				} );
 		} );
+
+		it( 'should not throw an error if editor was initialized with the data', () => {
+			return InlineEditor
+				.create( '<p>Foo.</p>', {
+					plugins: [ Paragraph, Bold ]
+				} )
+				.then( newEditor => newEditor.destroy() );
+		} );
 	} );
 } );

+ 18 - 0
packages/ckeditor5-editor-inline/tests/manual/inlineeditor-data.html

@@ -0,0 +1,18 @@
+<p>
+	<button id="destroyEditors">Destroy editors</button>
+	<button id="initEditor">Init editor</button>
+</p>
+
+<div class="container"></div>
+
+<style>
+	body {
+		width: 10000px;
+		height: 10000px;
+	}
+
+	.container {
+		padding: 20px;
+		width: 500px;
+	}
+</style>

+ 43 - 0
packages/ckeditor5-editor-inline/tests/manual/inlineeditor-data.js

@@ -0,0 +1,43 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console:false, document, window */
+
+import InlineEditor from '../../src/inlineeditor';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+
+window.editors = [];
+const container = document.querySelector( '.container' );
+let counter = 1;
+
+function initEditor() {
+	InlineEditor
+		.create( `<h2>Editor ${ counter }</h2><p>This is an editor instance.</p>`, {
+			plugins: [ ArticlePluginSet ],
+			toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ]
+		} )
+		.then( editor => {
+			counter += 1;
+			window.editors.push( editor );
+			container.appendChild( editor.element );
+		} )
+		.catch( err => {
+			console.error( err.stack );
+		} );
+}
+
+function destroyEditors() {
+	window.editors.forEach( editor => {
+		editor.destroy()
+			.then( () => {
+				editor.element.remove();
+			} );
+	} );
+	window.editors = [];
+	counter = 1;
+}
+
+document.getElementById( 'initEditor' ).addEventListener( 'click', initEditor );
+document.getElementById( 'destroyEditors' ).addEventListener( 'click', destroyEditors );

+ 3 - 0
packages/ckeditor5-editor-inline/tests/manual/inlineeditor-data.md

@@ -0,0 +1,3 @@
+1. Click "Init editor".
+2. New editor instance should be appended to the document with initial data in it. You can create more than one editor.
+3. After clicking "Destroy editor" all editors should be removed from the document.