Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Introduced EditorConfig#initialData. Made config param optional #31

Merged
merged 6 commits into from
Mar 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 46 additions & 37 deletions src/ballooneditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ElementApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/elementap
import attachToForm from '@ckeditor/ckeditor5-core/src/editor/utils/attachtoform';
import mix from '@ckeditor/ckeditor5-utils/src/mix';
import { isElement } from 'lodash-es';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';

/**
* The {@glink builds/guides/overview#balloon-editor balloon editor} implementation (Medium-like editor).
Expand Down Expand Up @@ -106,9 +107,11 @@ export default class BalloonEditor extends Editor {
}

/**
* Creates a balloon editor instance.
* Creates a `BalloonEditor` instance.
*
* Creating an instance when using a {@glink builds/index CKEditor build}:
* There are two general ways how the editor can be initialized.
*
* You can initialize the editor using an existing DOM element:
*
* BalloonEditor
* .create( document.querySelector( '#editor' ) )
Expand All @@ -119,63 +122,60 @@ export default class BalloonEditor extends Editor {
* console.error( err.stack );
* } );
*
* Creating an instance when using CKEditor from source (make sure to specify the list of plugins to load and the toolbar):
* The element's content will be used as the editor data and the element will become the editable element.
*
* import BalloonEditor from '@ckeditor/ckeditor5-editor-balloon/src/ballooneditor';
* 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 ...
* Alternatively, you can initialize the editor by passing the initial data directly as a `String`.
* In this case, the editor will render an element that must be inserted into the DOM for the editor to work properly:
*
* BalloonEditor
* .create( document.querySelector( '#editor' ), {
* plugins: [ Essentials, Bold, Italic, ... ],
* toolbar: [ 'bold', 'italic', ... ]
* } )
* .create( '<p>Hello world!</p>' )
* .then( editor => {
* console.log( 'Editor was initialized', editor );
*
* // Initial data was provided so the editor UI element needs to be added manually to the DOM.
* document.body.appendChild( editor.ui.element );
* } )
* .catch( err => {
* console.error( err.stack );
* } );
*
* Creating an instance when using initial data instead of a DOM element:
* This lets you dynamically append the editor to your web page whenever it is convenient for you. You may use this method if your
* web page content is generated on the client-side and the DOM structure is not ready at the moment when you initialize the editor.
*
* import BalloonEditor from '@ckeditor/ckeditor5-editor-balloon/src/ballooneditor';
* 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 ...
* You can also mix those two ways by providing a DOM element to be used and passing the initial data through the config:
*
* BalloonEditor
* .create( '<p>Hello world!</p>', {
* plugins: [ Essentials, Bold, Italic, ... ],
* toolbar: [ 'bold', 'italic', ... ]
* .create( document.querySelector( '#editor' ), {
* initialData: '<h2>Initial data</h2><p>Foo bar.</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 );
* } );
*
* This method can be used to initialize the editor on an existing element with specified content in case if your integration
* makes it difficult to set the content of the source element.
*
* Note that an error will be thrown if you pass initial data both as the first parameter and also in the config.
*
* See also the {@link module:core/editor/editorconfig~EditorConfig editor configuration documentation} to learn more about
* customizing plugins, toolbar and other.
*
* @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.
* or the editor's initial data.
*
* If a source element is passed, then its contents will be automatically
* {@link module:editor-balloon/ballooneditor~BalloonEditor#setData loaded} to the editor on startup and the element
* itself will be used as the editor's editable element.
* If a DOM element is passed, its content will be automatically loaded to the editor upon initialization.
* Moreover, the editor data will be set back to the original element once the editor is destroyed.
*
* If data is provided, then `editor.element` will be created automatically and needs to be added
* to the DOM manually.
* @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-balloon/ballooneditor~BalloonEditor} instance.
* If the initial data is passed, a detached editor will be created. In this case you need to insert it into the DOM manually.
* It is available under {@link module:editor-balloon/ballooneditorui~BalloonEditorUI#element `editor.ui.element`} property.
*
* @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
* @returns {Promise} A promise resolved once the editor is ready. The promise resolves with the created editor instance.
*/
static create( sourceElementOrData, config ) {
static create( sourceElementOrData, config = {} ) {
return new Promise( resolve => {
const editor = new this( sourceElementOrData, config );

Expand All @@ -185,9 +185,14 @@ export default class BalloonEditor extends Editor {
editor.ui.init();
} )
.then( () => {
const initialData = isElement( sourceElementOrData ) ?
getDataFromElement( sourceElementOrData ) :
sourceElementOrData;
if ( !isElement( sourceElementOrData ) && config.initialData ) {
throw new CKEditorError(
'editor-create-initial-data: ' +
'EditorConfig#initialData cannot be used together with initial data passed in Editor#create()'
);
}

const initialData = config.initialData || getInitialData( sourceElementOrData );

return editor.data.init( initialData );
} )
Expand All @@ -200,3 +205,7 @@ export default class BalloonEditor extends Editor {

mix( BalloonEditor, DataApiMixin );
mix( BalloonEditor, ElementApiMixin );

function getInitialData( sourceElementOrData ) {
return isElement( sourceElementOrData ) ? getDataFromElement( sourceElementOrData ) : sourceElementOrData;
}
51 changes: 43 additions & 8 deletions tests/ballooneditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,6 @@ describe( 'BalloonEditor', () => {
} );
} );

it( 'allows to pass data to the constructor', () => {
return BalloonEditor.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 BalloonEditor
.create( '<p>Foo.</p>', {
Expand Down Expand Up @@ -165,6 +157,49 @@ describe( 'BalloonEditor', () => {
expect( editor.getData() ).to.equal( '<p><strong>foo</strong> bar</p>' );
} );

it( 'should not require config object', () => {
// Just being safe with `builtinPlugins` static property.
class CustomBalloonEditor extends BalloonEditor {}
CustomBalloonEditor.builtinPlugins = [ Paragraph, Bold ];

return CustomBalloonEditor.create( editorElement )
.then( newEditor => {
expect( newEditor.getData() ).to.equal( '<p><strong>foo</strong> bar</p>' );

return newEditor.destroy();
} );
} );

it( 'allows to pass data to the constructor', () => {
return BalloonEditor.create( '<p>Hello world!</p>', {
plugins: [ Paragraph ]
} ).then( editor => {
expect( editor.getData() ).to.equal( '<p>Hello world!</p>' );

editor.destroy();
} );
} );

it( 'initializes with config.initialData', () => {
return BalloonEditor.create( editorElement, {
initialData: '<p>Hello world!</p>',
plugins: [ Paragraph ]
} ).then( editor => {
expect( editor.getData() ).to.equal( '<p>Hello world!</p>' );

editor.destroy();
} );
} );

it( 'throws if initial data is passed in Editor#create and config.initialData is also used', done => {
BalloonEditor.create( '<p>Hello world!</p>', {
initialData: '<p>I am evil!</p>',
plugins: [ Paragraph ]
} ).catch( () => {
done();
} );
} );

// ckeditor/ckeditor5-editor-classic#53
it( 'creates an instance of a BalloonEditor child class', () => {
// Fun fact: Remove the next 3 lines and you'll get a lovely inf loop due to two
Expand Down
4 changes: 2 additions & 2 deletions tests/manual/ballooneditor-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function initEditor() {
.then( editor => {
counter += 1;
window.editors.push( editor );
container.appendChild( editor.element );
container.appendChild( editor.ui.element );
} )
.catch( err => {
console.error( err.stack );
Expand All @@ -32,7 +32,7 @@ function destroyEditors() {
window.editors.forEach( editor => {
editor.destroy()
.then( () => {
editor.element.remove();
editor.ui.element.remove();
} );
} );
window.editors = [];
Expand Down