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

Commit

Permalink
Merge pull request #184 from ckeditor/t/ckeditor5/1838
Browse files Browse the repository at this point in the history
Feature: Added an editor instance reference to the native editable DOM element under the `ckeditorInstance` property. Closes ckeditor/ckeditor5#1838. 

Implemented the `EditorUI#setEditableElement()` method. 
Deprecated the `EditorUI#_editableElements` property.
  • Loading branch information
jodator authored Jun 28, 2019
2 parents ebcbd01 + 8830ae4 commit fa94600
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 15 deletions.
57 changes: 52 additions & 5 deletions src/editor/editorui.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';

import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
import mix from '@ckeditor/ckeditor5-utils/src/mix';
import log from '@ckeditor/ckeditor5-utils/src/log';

/**
* A class providing the minimal interface that is required to successfully bootstrap any editor UI.
Expand Down Expand Up @@ -54,10 +55,10 @@ export default class EditorUI {
/**
* Stores all editable elements used by the editor instance.
*
* @protected
* @private
* @member {Map.<String,HTMLElement>}
*/
this._editableElements = new Map();
this._editableElementsMap = new Map();

// Informs UI components that should be refreshed after layout change.
this.listenTo( editor.editing.view.document, 'layoutChanged', () => this.update() );
Expand Down Expand Up @@ -100,7 +101,29 @@ export default class EditorUI {

this.focusTracker.destroy();

this._editableElements = new Map();
// Clean–up the references to the CKEditor instance stored in the native editable DOM elements.
for ( const domElement of this._editableElementsMap.values() ) {
domElement.ckeditorInstance = null;
}

this._editableElementsMap = new Map();
}

/**
* Store the native DOM editable element used by the editor under
* a unique name.
*
* @param {String} rootName The unique name of the editable element.
* @param {HTMLElement} domElement The native DOM editable element.
*/
setEditableElement( rootName, domElement ) {
this._editableElementsMap.set( rootName, domElement );

// Put a reference to the CKEditor instance in the editable native DOM element.
// It helps 3rd–party software (browser extensions, other libraries) access and recognize
// CKEditor 5 instances (editing roots) and use their API (there is no global editor
// instance registry).
domElement.ckeditorInstance = this.editor;
}

/**
Expand All @@ -110,7 +133,7 @@ export default class EditorUI {
* @returns {HTMLElement|undefined}
*/
getEditableElement( rootName = 'main' ) {
return this._editableElements.get( rootName );
return this._editableElementsMap.get( rootName );
}

/**
Expand All @@ -119,7 +142,31 @@ export default class EditorUI {
* @returns {Iterable.<String>}
*/
getEditableElementsNames() {
return this._editableElements.keys();
return this._editableElementsMap.keys();
}

/**
* Stores all editable elements used by the editor instance.
*
* @protected
* @deprecated
* @member {Map.<String,HTMLElement>}
*/
get _editableElements() {
/**
* The `EditorUI#_editableElements` property has been deprecated and will be removed in the near future.
* Please use {@link #setEditableElement `setEditableElement()`} and {@link #getEditableElement `getEditableElement()`}
* methods instead.
*
* @warning editor-ui-deprecated-editable-elements
* @param {module:core/editor/editorui~EditorUI} editorUI Editor UI instance the property belongs to.
*/
log.warn(
'editor-ui-deprecated-editable-elements: ' +
'The EditorUI#_editableElements property has been deprecated and will be remove in the near future.',
{ editorUI: this } );

return this._editableElementsMap;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/_utils/classictesteditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class ClassicTestEditorUI extends EditorUI {

view.main.add( view.editable );

this._editableElements.set( 'main', view.editable.element );
this.setEditableElement( 'main', view.editable.element );

if ( replacementElement ) {
this._elementReplacer.replace( replacementElement, view.element );
Expand Down
65 changes: 56 additions & 9 deletions tests/editor/editorui.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';

import testUtils from '../_utils/utils';
import log from '@ckeditor/ckeditor5-utils/src/log';

/* global document */

Expand Down Expand Up @@ -85,14 +86,50 @@ describe( 'EditorUI', () => {
} );

it( 'should reset editables array', () => {
ui._editableElements.set( 'foo', {} );
ui._editableElements.set( 'bar', {} );
ui.setEditableElement( 'foo', {} );
ui.setEditableElement( 'bar', {} );

expect( ui._editableElements.size ).to.equal( 2 );
expect( [ ...ui.getEditableElementsNames() ] ).to.deep.equal( [ 'foo', 'bar' ] );

ui.destroy();

expect( ui._editableElements.size ).to.equal( 0 );
expect( [ ...ui.getEditableElementsNames() ] ).to.have.length( 0 );
} );

it( 'removes domElement#ckeditorInstance references from registered root elements', () => {
const fooElement = document.createElement( 'foo' );
const barElement = document.createElement( 'bar' );

ui.setEditableElement( 'foo', fooElement );
ui.setEditableElement( 'bar', barElement );

expect( fooElement.ckeditorInstance ).to.equal( editor );
expect( barElement.ckeditorInstance ).to.equal( editor );

ui.destroy();

expect( fooElement.ckeditorInstance ).to.be.null;
expect( barElement.ckeditorInstance ).to.be.null;
} );
} );

describe( 'setEditableElement()', () => {
it( 'should register the editable element under a name', () => {
const ui = new EditorUI( editor );
const element = document.createElement( 'div' );

ui.setEditableElement( 'main', element );

expect( ui.getEditableElement( 'main' ) ).to.equal( element );
} );

it( 'puts a reference to the editor instance in domElement#ckeditorInstance', () => {
const ui = new EditorUI( editor );
const element = document.createElement( 'div' );

ui.setEditableElement( 'main', element );

expect( element.ckeditorInstance ).to.equal( editor );
} );
} );

Expand All @@ -101,7 +138,7 @@ describe( 'EditorUI', () => {
const ui = new EditorUI( editor );
const editableMock = { name: 'main', element: document.createElement( 'div' ) };

ui._editableElements.set( editableMock.name, editableMock.element );
ui.setEditableElement( editableMock.name, editableMock.element );

expect( ui.getEditableElement() ).to.equal( editableMock.element );
} );
Expand All @@ -111,8 +148,8 @@ describe( 'EditorUI', () => {
const editableMock1 = { name: 'root1', element: document.createElement( 'div' ) };
const editableMock2 = { name: 'root2', element: document.createElement( 'p' ) };

ui._editableElements.set( editableMock1.name, editableMock1.element );
ui._editableElements.set( editableMock2.name, editableMock2.element );
ui.setEditableElement( editableMock1.name, editableMock1.element );
ui.setEditableElement( editableMock2.name, editableMock2.element );

expect( ui.getEditableElement( 'root1' ) ).to.equal( editableMock1.element );
expect( ui.getEditableElement( 'root2' ) ).to.equal( editableMock2.element );
Expand All @@ -131,8 +168,8 @@ describe( 'EditorUI', () => {
const editableMock1 = { name: 'main', element: document.createElement( 'div' ) };
const editableMock2 = { name: 'root2', element: document.createElement( 'p' ) };

ui._editableElements.set( editableMock1.name, editableMock1.element );
ui._editableElements.set( editableMock2.name, editableMock2.element );
ui.setEditableElement( editableMock1.name, editableMock1.element );
ui.setEditableElement( editableMock2.name, editableMock2.element );

const names = ui.getEditableElementsNames();
expect( names[ Symbol.iterator ] ).to.instanceof( Function );
Expand All @@ -145,4 +182,14 @@ describe( 'EditorUI', () => {
expect( ui.getEditableElementsNames() ).to.be.empty;
} );
} );

describe( '_editableElements()', () => {
it( 'should warn about deprecation', () => {
const ui = new EditorUI( editor );
const stub = testUtils.sinon.stub( log, 'warn' );

expect( ui._editableElements ).to.be.instanceOf( Map );
sinon.assert.calledWithMatch( stub, 'editor-ui-deprecated-editable-elements' );
} );
} );
} );

0 comments on commit fa94600

Please sign in to comment.