diff --git a/docs/framework/guides/document-editor.md b/docs/framework/guides/document-editor.md index 47f1cb8..cc097ca 100644 --- a/docs/framework/guides/document-editor.md +++ b/docs/framework/guides/document-editor.md @@ -46,7 +46,7 @@ DecoupledEditor } ); ``` -You may have noticed that you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorwithui~EditorWithUI#event:uiReady `Editor#uiReady`} event. The toolbar element can be found under `editor.ui.view.toolbar.element`. +You may have noticed that you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorui~EditorUI#event:ready `EditorUI#ready`} event. The toolbar element can be found under `editor.ui.view.toolbar.element`. Document editor supports the Easy Image plugin provided by [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services/) out of the box. Please refer to the {@link features/image-upload#easy-image documentation} to learn more. diff --git a/src/decouplededitor.js b/src/decouplededitor.js index b59218f..3d99b93 100644 --- a/src/decouplededitor.js +++ b/src/decouplededitor.js @@ -76,15 +76,6 @@ export default class DecoupledEditor extends Editor { this.ui = new DecoupledEditorUI( this, new DecoupledEditorUIView( this.locale, this.sourceElement ) ); } - /** - * @inheritDoc - */ - get element() { - // This editor has no single "main UI element". Its editable and toolbar are exposed separately and need - // to be added to the DOM manually by the consumer. - return null; - } - /** * Destroys the editor instance, releasing all resources used by it. * @@ -198,7 +189,6 @@ export default class DecoupledEditor extends Editor { editor.initPlugins() .then( () => { editor.ui.init(); - editor.fire( 'uiReady' ); } ) .then( () => { const initialData = isElement( sourceElementOrData ) ? diff --git a/src/decouplededitorui.js b/src/decouplededitorui.js index b23df4a..2d8ec87 100644 --- a/src/decouplededitorui.js +++ b/src/decouplededitorui.js @@ -18,10 +18,21 @@ import normalizeToolbarConfig from '@ckeditor/ckeditor5-ui/src/toolbar/normalize */ export default class DecoupledEditorUI extends EditorUI { /** - * @inheritDoc + * Creates an instance of the decoupled editor UI class. + * + * @param {module:core/editor/editor~Editor} editor The editor instance. + * @param {module:ui/editorui/editoruiview~EditorUIView} view The view of the UI. */ constructor( editor, view ) { - super( editor, view ); + super( editor ); + + /** + * The main (top–most) view of the editor UI. + * + * @private + * @member {module:ui/editorui/editoruiview~EditorUIView} #_view + */ + this._view = view; /** * A normalized `config.toolbar` object. @@ -32,6 +43,16 @@ export default class DecoupledEditorUI extends EditorUI { this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) ); } + /** + * The main (top–most) view of the editor UI. + * + * @readonly + * @member {module:ui/editorui/editoruiview~EditorUIView} #view + */ + get view() { + return this._view; + } + /** * Initializes the UI. */ @@ -45,10 +66,13 @@ export default class DecoupledEditorUI extends EditorUI { const editingRoot = editor.editing.view.document.getRoot(); view.editable.bind( 'isReadOnly' ).to( editingRoot ); view.editable.bind( 'isFocused' ).to( editor.editing.view.document ); - editor.editing.view.attachDomRoot( view.editableElement ); + editor.editing.view.attachDomRoot( view.editable.element ); view.editable.name = editingRoot.rootName; - this.focusTracker.add( this.view.editableElement ); + this._editableElements.set( view.editable.name, view.editable.element ); + + this.focusTracker.add( view.editable.element ); + this.view.toolbar.fillFromConfig( this._toolbarConfig.items, this.componentFactory ); enableToolbarKeyboardFocus( { @@ -57,5 +81,16 @@ export default class DecoupledEditorUI extends EditorUI { originKeystrokeHandler: editor.keystrokes, toolbar: this.view.toolbar } ); + + this.fire( 'ready' ); + } + + /** + * @inheritDoc + */ + destroy() { + this._view.destroy(); + + super.destroy(); } } diff --git a/src/decouplededitoruiview.js b/src/decouplededitoruiview.js index 03e7283..f0813cb 100644 --- a/src/decouplededitoruiview.js +++ b/src/decouplededitoruiview.js @@ -70,11 +70,4 @@ export default class DecoupledEditorUIView extends EditorUIView { this.registerChild( [ this.toolbar, this.editable ] ); } - - /** - * @inheritDoc - */ - get editableElement() { - return this.editable.element; - } } diff --git a/tests/decouplededitor.js b/tests/decouplededitor.js index 7c163f0..c480d6c 100644 --- a/tests/decouplededitor.js +++ b/tests/decouplededitor.js @@ -18,6 +18,8 @@ import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin import RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; +import log from '@ckeditor/ckeditor5-utils/src/log'; + import { describeMemoryUsage, testMemoryUsage } from '@ckeditor/ckeditor5-core/tests/_utils/memory'; import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; @@ -28,6 +30,10 @@ describe( 'DecoupledEditor', () => { testUtils.createSinonSandbox(); + beforeEach( () => { + testUtils.sinon.stub( log, 'warn' ).callsFake( () => {} ); + } ); + describe( 'constructor()', () => { beforeEach( () => { editor = new DecoupledEditor(); @@ -41,10 +47,6 @@ describe( 'DecoupledEditor', () => { expect( testUtils.isMixed( DecoupledEditor, DataApiMixin ) ).to.be.true; } ); - it( 'implements the EditorWithUI interface', () => { - expect( editor.element ).to.be.null; - } ); - it( 'creates main root element', () => { expect( editor.model.document.getRoot( 'main' ) ).to.instanceof( RootElement ); } ); @@ -191,7 +193,7 @@ describe( 'DecoupledEditor', () => { class EventWatcher extends Plugin { init() { this.editor.on( 'pluginsReady', spy ); - this.editor.on( 'uiReady', spy ); + this.editor.ui.on( 'ready', spy ); this.editor.on( 'dataReady', spy ); this.editor.on( 'ready', spy ); } @@ -202,7 +204,7 @@ describe( 'DecoupledEditor', () => { plugins: [ EventWatcher ] } ) .then( newEditor => { - expect( fired ).to.deep.equal( [ 'pluginsReady', 'uiReady', 'dataReady', 'ready' ] ); + expect( fired ).to.deep.equal( [ 'pluginsReady', 'ready', 'dataReady', 'ready' ] ); return newEditor.destroy(); } ); @@ -230,12 +232,12 @@ describe( 'DecoupledEditor', () => { } ); } ); - it( 'fires uiReady once UI is rendered', () => { + it( 'fires ready once UI is rendered', () => { let isReady; class EventWatcher extends Plugin { init() { - this.editor.on( 'uiReady', () => { + this.editor.ui.on( 'ready', () => { isReady = this.editor.ui.view.isRendered; } ); } diff --git a/tests/decouplededitorui.js b/tests/decouplededitorui.js index 4ca0ea0..f72870f 100644 --- a/tests/decouplededitorui.js +++ b/tests/decouplededitorui.js @@ -17,7 +17,7 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; import utils from '@ckeditor/ckeditor5-utils/tests/_utils/utils'; describe( 'DecoupledEditorUI', () => { - let editor, view, ui; + let editor, view, ui, viewElement; testUtils.createSinonSandbox(); @@ -31,6 +31,7 @@ describe( 'DecoupledEditorUI', () => { ui = editor.ui; view = ui.view; + viewElement = view.element; } ); } ); @@ -150,6 +151,26 @@ describe( 'DecoupledEditorUI', () => { } ); } ); } ); + + describe( 'element()', () => { + it( 'returns correct element instance', () => { + expect( ui.element ).to.equal( viewElement ); + } ); + } ); + + describe( 'getEditableElement()', () => { + it( 'returns editable element (default)', () => { + expect( ui.getEditableElement() ).to.equal( view.editable.element ); + } ); + + it( 'returns editable element (root name passed)', () => { + expect( ui.getEditableElement( 'main' ) ).to.equal( view.editable.element ); + } ); + + it( 'returns undefined if editable with the given name is absent', () => { + expect( ui.getEditableElement( 'absent' ) ).to.be.undefined; + } ); + } ); } ); function viewCreator( name ) { @@ -188,7 +209,6 @@ class VirtualDecoupledTestEditor extends VirtualTestEditor { editor.initPlugins() .then( () => { editor.ui.init(); - editor.fire( 'uiReady' ); editor.fire( 'dataReady' ); editor.fire( 'ready' ); } ) diff --git a/tests/decouplededitoruiview.js b/tests/decouplededitoruiview.js index fa3b33a..b6d5ef4 100644 --- a/tests/decouplededitoruiview.js +++ b/tests/decouplededitoruiview.js @@ -10,9 +10,13 @@ import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview'; import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview'; import Locale from '@ckeditor/ckeditor5-utils/src/locale'; +import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; + describe( 'DecoupledEditorUIView', () => { let locale, view; + testUtils.createSinonSandbox(); + beforeEach( () => { locale = new Locale( 'en' ); view = new DecoupledEditorUIView( locale ); @@ -121,12 +125,4 @@ describe( 'DecoupledEditorUIView', () => { view.editable.element.remove(); } ); } ); - - describe( 'editableElement', () => { - it( 'returns editable\'s view element', () => { - view.render(); - expect( view.editableElement.getAttribute( 'contentEditable' ) ).to.equal( 'true' ); - view.destroy(); - } ); - } ); } );