diff --git a/src/editor/editorui.js b/src/editor/editorui.js new file mode 100644 index 00000000..e2d9e754 --- /dev/null +++ b/src/editor/editorui.js @@ -0,0 +1,92 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +/** + * @module core/editor/editorui + */ + +import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory'; +import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker'; + +import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; +import mix from '@ckeditor/ckeditor5-utils/src/mix'; + +/** + * A class providing the minimal interface that is required to successfully bootstrap any editor UI. + * + * @mixes module:utils/emittermixin~EmitterMixin + */ +export default class EditorUI { + /** + * Creates an instance of the 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 ) { + /** + * The editor that the UI belongs to. + * + * @readonly + * @member {module:core/editor/editor~Editor} #editor + */ + this.editor = editor; + + /** + * The main (top–most) view of the editor UI. + * + * @readonly + * @member {module:ui/editorui/editoruiview~EditorUIView} #view + */ + this.view = view; + + /** + * An instance of the {@link module:ui/componentfactory~ComponentFactory}, a registry used by plugins + * to register factories of specific UI components. + * + * @readonly + * @member {module:ui/componentfactory~ComponentFactory} #componentFactory + */ + this.componentFactory = new ComponentFactory( editor ); + + /** + * Stores the information about the editor UI focus and propagates it so various plugins and components + * are unified as a focus group. + * + * @readonly + * @member {module:utils/focustracker~FocusTracker} #focusTracker + */ + this.focusTracker = new FocusTracker(); + + // Informs UI components that should be refreshed after layout change. + this.listenTo( editor.editing.view.document, 'layoutChanged', () => this.update() ); + } + + /** + * Fires the {@link module:core/editor/editorui~EditorUI#event:update} event. + */ + update() { + this.fire( 'update' ); + } + + /** + * Destroys the UI. + */ + destroy() { + this.stopListening(); + this.view.destroy(); + } + + /** + * Fired whenever the UI (all related components) should be refreshed. + * + * **Note:**: The event is fired after each {@link module:engine/view/document~Document#event:layoutChanged}. + * It can also be fired manually via the {@link module:core/editor/editorui~EditorUI#update} method. + * + * @event update + */ +} + +mix( EditorUI, EmitterMixin ); diff --git a/src/editor/editorui.jsdoc b/src/editor/editorui.jsdoc deleted file mode 100644 index 97f25782..00000000 --- a/src/editor/editorui.jsdoc +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -/** - * @module core/editor/editorui - */ - -/** - * Minimal interface that is required to successfully bootstrap any editor UI. - * - * @interface EditorUI - */ - -/** - * Editor that the UI belongs to. - * - * @readonly - * @member {module:core/editor/editor~Editor} #editor - */ - -/** - * The main (top–most) view of the editor UI. - * - * @readonly - * @member {module:ui/editorui/editoruiview~EditorUIView} #view - */ - -/** - * Instance of the {@link module:ui/componentfactory~ComponentFactory}, a registry used by plugins - * to register factories of specific UI components. - * - * @readonly - * @member {module:ui/componentfactory~ComponentFactory} #componentFactory - */ - -/** - * Keeps information about editor UI focus and propagates it among various plugins and components, - * unifying them in a uniform focus group. - * - * @readonly - * @member {module:utils/focustracker~FocusTracker} #focusTracker - */ diff --git a/tests/_utils-tests/classictesteditor.js b/tests/_utils-tests/classictesteditor.js index c3f79117..b4cd29b4 100644 --- a/tests/_utils-tests/classictesteditor.js +++ b/tests/_utils-tests/classictesteditor.js @@ -11,7 +11,7 @@ import ClassicTestEditor from '../../tests/_utils/classictesteditor'; import Plugin from '../../src/plugin'; import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor'; -import ClassicTestEditorUI from '../../tests/_utils/classictesteditorui'; +import EditorUI from '../../src/editor/editorui'; import BoxedEditorUIView from '@ckeditor/ckeditor5-ui/src/editorui/boxed/boxededitoruiview'; import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview'; @@ -39,7 +39,7 @@ describe( 'ClassicTestEditor', () => { expect( editor ).to.be.instanceof( Editor ); expect( editor.config.get( 'foo' ) ).to.equal( 1 ); expect( editor.element ).to.equal( editorElement ); - expect( editor.ui ).to.be.instanceOf( ClassicTestEditorUI ); + expect( editor.ui ).to.be.instanceOf( EditorUI ); expect( editor.ui.view ).to.be.instanceOf( BoxedEditorUIView ); expect( editor.data.processor ).to.be.instanceof( HtmlDataProcessor ); } ); @@ -82,7 +82,7 @@ describe( 'ClassicTestEditor', () => { it( 'creates and initializes the UI', () => { return ClassicTestEditor.create( editorElement, { foo: 1 } ) .then( editor => { - expect( editor.ui ).to.be.instanceOf( ClassicTestEditorUI ); + expect( editor.ui ).to.be.instanceOf( EditorUI ); expect( editor.ui.view ).to.be.instanceOf( BoxedEditorUIView ); } ); } ); diff --git a/tests/_utils-tests/classictesteditorui.js b/tests/_utils-tests/classictesteditorui.js deleted file mode 100644 index a070c2db..00000000 --- a/tests/_utils-tests/classictesteditorui.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory'; -import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker'; -import ClassicTestEditorUI from '../../tests/_utils/classictesteditorui'; -import View from '@ckeditor/ckeditor5-ui/src/view'; - -describe( 'ClassicTestEditorUI', () => { - let editor, view, ui; - - beforeEach( () => { - editor = {}; - view = new View(); - ui = new ClassicTestEditorUI( editor, view ); - } ); - - describe( 'constructor()', () => { - it( 'sets #editor', () => { - expect( ui.editor ).to.equal( editor ); - } ); - - it( 'sets #view', () => { - expect( ui.view ).to.equal( view ); - } ); - - it( 'creates #componentFactory factory', () => { - expect( ui.componentFactory ).to.be.instanceOf( ComponentFactory ); - } ); - - it( 'creates #focusTracker', () => { - expect( ui.focusTracker ).to.be.instanceOf( FocusTracker ); - } ); - } ); - - describe( 'destroy()', () => { - it( 'destroys the #view', () => { - const spy = sinon.spy( view, 'destroy' ); - - ui.destroy(); - sinon.assert.calledOnce( spy ); - } ); - } ); -} ); diff --git a/tests/_utils/classictesteditor.js b/tests/_utils/classictesteditor.js index 925dafe3..14eaa3c4 100644 --- a/tests/_utils/classictesteditor.js +++ b/tests/_utils/classictesteditor.js @@ -7,7 +7,7 @@ import Editor from '../../src/editor/editor'; import ElementApiMixin from '../../src/editor/utils/elementapimixin'; import DataApiMixin from '../../src/editor/utils/dataapimixin'; import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor'; -import ClassicTestEditorUI from './classictesteditorui'; +import EditorUI from '../../src/editor/editorui'; import BoxedEditorUIView from '@ckeditor/ckeditor5-ui/src/editorui/boxed/boxededitoruiview'; import ElementReplacer from '@ckeditor/ckeditor5-utils/src/elementreplacer'; import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview'; @@ -33,7 +33,7 @@ export default class ClassicTestEditor extends Editor { // Use the HTML data processor in this editor. this.data.processor = new HtmlDataProcessor(); - this.ui = new ClassicTestEditorUI( this, new BoxedEditorUIView( this.locale ) ); + this.ui = new EditorUI( this, new BoxedEditorUIView( this.locale ) ); // Expose properties normally exposed by the ClassicEditorUI. this.ui.view.editable = new InlineEditableUIView( this.ui.view.locale ); diff --git a/tests/_utils/classictesteditorui.js b/tests/_utils/classictesteditorui.js deleted file mode 100644 index 1251f315..00000000 --- a/tests/_utils/classictesteditorui.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.md. - */ - -import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory'; -import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker'; - -/** - * A simplified classic editor UI class. Useful for testing features. - * - * @memberOf tests.core._utils - * @extends ui.View - */ -export default class ClassicTestEditorUI { - /** - * Creates an instance of the test editor UI class. - * - * @param {core.editor.Editor} editor The editor instance. - * @param {ui.editorUI.EditorUIView} view View of the ui. - */ - constructor( editor, view ) { - /** - * Editor that the UI belongs to. - * - * @readonly - * @member {core.editor.Editor} tests.core._utils.ClassicTestEditorUI#editor - */ - this.editor = editor; - - /** - * View of the ui. - * - * @readonly - * @member {ui.editorUI.EditorUIView} tests.core._utils.ClassicTestEditorUI#view - */ - this.view = view; - - /** - * Instance of the {@link ui.ComponentFactory}. - * - * @readonly - * @member {ui.ComponentFactory} tests.core._utils.ClassicTestEditorUI#componentFactory - */ - this.componentFactory = new ComponentFactory( editor ); - - /** - * Keeps information about editor focus. - * - * @member {utils.FocusTracker} tests.core._utils.ClassicTestEditorUI#focusTracker - */ - this.focusTracker = new FocusTracker(); - } - - /** - * Destroys the UI. - * - * @returns {Promise} A Promise resolved when the destruction process is finished. - */ - destroy() { - this.view.destroy(); - } -} diff --git a/tests/editor/editorui.js b/tests/editor/editorui.js new file mode 100644 index 00000000..8b616513 --- /dev/null +++ b/tests/editor/editorui.js @@ -0,0 +1,98 @@ +/** + * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md. + */ + +import EditorUI from '../../src/editor/editorui'; +import Editor from '../../src/editor/editor'; + +import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker'; +import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory'; +import View from '@ckeditor/ckeditor5-ui/src/view'; + +import testUtils from '../_utils/utils'; + +testUtils.createSinonSandbox(); + +describe( 'EditorUI', () => { + let editor, view, ui; + + beforeEach( () => { + editor = new Editor(); + view = new View(); + ui = new EditorUI( editor, view ); + } ); + + afterEach( () => { + return Promise.all( [ + editor.destroy(), + ui.destroy() + ] ); + } ); + + describe( 'constructor()', () => { + it( 'should set #editor', () => { + expect( ui.editor ).to.equal( editor ); + } ); + + it( 'should set #view', () => { + expect( ui.view ).to.equal( view ); + } ); + + it( 'should create #componentFactory factory', () => { + expect( ui.componentFactory ).to.be.instanceOf( ComponentFactory ); + } ); + + it( 'should create #focusTracker', () => { + expect( ui.focusTracker ).to.be.instanceOf( FocusTracker ); + } ); + + it( 'should fire update event after viewDocument#layoutChanged', () => { + const spy = sinon.spy(); + + ui.on( 'update', spy ); + + editor.editing.view.document.fire( 'layoutChanged' ); + + sinon.assert.calledOnce( spy ); + + editor.editing.view.document.fire( 'layoutChanged' ); + + sinon.assert.calledTwice( spy ); + } ); + } ); + + describe( 'update()', () => { + it( 'should fire update event', () => { + const spy = sinon.spy(); + + ui.on( 'update', spy ); + + ui.update(); + + sinon.assert.calledOnce( spy ); + + ui.update(); + + sinon.assert.calledTwice( spy ); + } ); + } ); + + describe( 'destroy()', () => { + it( 'should stop listening', () => { + const spy = sinon.spy( ui, 'stopListening' ); + + ui.destroy(); + + sinon.assert.called( spy ); + } ); + + it( 'should destroy the #view', () => { + const spy = sinon.spy( view, 'destroy' ); + + ui.destroy(); + + sinon.assert.called( spy ); + } ); + } ); +} );