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

Commit e05b8b1

Browse files
author
Piotr Jasiun
authored
Merge pull request #1255 from ckeditor/t/1254
Other: UIElement custom `render()` method can be now provided without using inheritance. Closes #1254. BREAKING CHANGE: Introduced new method of creating custom UIElements.
2 parents 925156e + 081fc04 commit e05b8b1

File tree

7 files changed

+109
-48
lines changed

7 files changed

+109
-48
lines changed

src/view/uielement.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,31 @@ export default class UIElement extends Element {
6868
/**
6969
* Renders this {@link module:engine/view/uielement~UIElement} to DOM. This method is called by
7070
* {@link module:engine/view/domconverter~DomConverter}.
71+
* Do not use inheritance to create custom rendering method, replace `render()` method instead:
72+
*
73+
* const myUIElement = new UIElement( 'span' );
74+
* myUIElement.render = function( domDocument ) {
75+
* const domElement = this.toDomElement( domDocument );
76+
* domElement.innerHTML = '<b>this is ui element</b>';
77+
*
78+
* return domElement;
79+
* };
7180
*
7281
* @param {Document} domDocument
7382
* @return {HTMLElement}
7483
*/
7584
render( domDocument ) {
85+
return this.toDomElement( domDocument );
86+
}
87+
88+
/**
89+
* Creates DOM element based on this view UIElement.
90+
* Note that each time this method is called new DOM element is created.
91+
*
92+
* @param {Document} domDocument
93+
* @returns {HTMLElement}
94+
*/
95+
toDomElement( domDocument ) {
7696
const domElement = domDocument.createElement( this.name );
7797

7898
for ( const key of this.getAttributeKeys() ) {

tests/view/document/jumpoveruielement.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
2020
describe( 'Document', () => {
2121
let viewDocument, domRoot, domSelection, viewRoot, foo, bar, ui, ui2;
2222

23-
class MyUIElement extends UIElement {
24-
render( domDocument ) {
25-
const element = super.render( domDocument );
26-
element.innerText = this.contents;
23+
function createUIElement( name, contents ) {
24+
const element = new UIElement( name );
2725

28-
return element;
29-
}
26+
element.render = function( domDocument ) {
27+
const domElement = this.toDomElement( domDocument );
28+
domElement.innerText = contents;
29+
30+
return domElement;
31+
};
32+
33+
return element;
3034
}
3135

3236
beforeEach( () => {
@@ -46,11 +50,8 @@ describe( 'Document', () => {
4650

4751
foo = new ViewText( 'foo' );
4852
bar = new ViewText( 'bar' );
49-
ui = new MyUIElement( 'span' );
50-
ui.contents = 'xxx';
51-
52-
ui2 = new MyUIElement( 'span' );
53-
ui2.contents = 'yyy';
53+
ui = createUIElement( 'span', 'xxx' );
54+
ui2 = createUIElement( 'span', 'yyy' );
5455
} );
5556

5657
afterEach( () => {

tests/view/domconverter/uielement.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ import DomConverter from '../../../src/view/domconverter';
1212
describe( 'DOMConverter UIElement integration', () => {
1313
let converter;
1414

15-
class MyUIElement extends ViewUIElement {
16-
render( domDocument ) {
17-
const root = super.render( domDocument );
15+
function createUIElement( name ) {
16+
const element = new ViewUIElement( name );
17+
18+
element.render = function( domDocument ) {
19+
const root = this.toDomElement( domDocument );
1820
root.innerHTML = '<p><span>foo</span> bar</p>';
1921

2022
return root;
21-
}
23+
};
24+
25+
return element;
2226
}
2327

2428
beforeEach( () => {
@@ -34,15 +38,15 @@ describe( 'DOMConverter UIElement integration', () => {
3438
} );
3539

3640
it( 'should create DOM structure from UIElement', () => {
37-
const myElement = new MyUIElement( 'div' );
41+
const myElement = createUIElement( 'div' );
3842
const domElement = converter.viewToDom( myElement, document );
3943

4044
expect( domElement ).to.be.instanceOf( HTMLElement );
4145
expect( domElement.innerHTML ).to.equal( '<p><span>foo</span> bar</p>' );
4246
} );
4347

4448
it( 'should create DOM structure that all is mapped to single UIElement', () => {
45-
const myElement = new MyUIElement( 'div' );
49+
const myElement = createUIElement( 'div' );
4650
const domElement = converter.viewToDom( myElement, document, { bind: true } );
4751
const domParagraph = domElement.childNodes[ 0 ];
4852

@@ -54,14 +58,14 @@ describe( 'DOMConverter UIElement integration', () => {
5458

5559
describe( 'domToView()', () => {
5660
it( 'should return UIElement itself', () => {
57-
const uiElement = new MyUIElement( 'div' );
61+
const uiElement = createUIElement( 'div' );
5862
const domElement = converter.viewToDom( uiElement, document, { bind: true } );
5963

6064
expect( converter.domToView( domElement ) ).to.equal( uiElement );
6165
} );
6266

6367
it( 'should return UIElement for nodes inside', () => {
64-
const uiElement = new MyUIElement( 'div' );
68+
const uiElement = createUIElement( 'div' );
6569
const domElement = converter.viewToDom( uiElement, document, { bind: true } );
6670

6771
const domParagraph = domElement.childNodes[ 0 ];
@@ -76,7 +80,7 @@ describe( 'DOMConverter UIElement integration', () => {
7680

7781
describe( 'domPositionToView()', () => {
7882
it( 'should convert position inside UIElement to position before it', () => {
79-
const uiElement = new MyUIElement( 'h1' );
83+
const uiElement = createUIElement( 'h1' );
8084
const container = new ViewContainer( 'div', null, [ new ViewContainer( 'div' ), uiElement ] );
8185
const domContainer = converter.viewToDom( container, document, { bind: true } );
8286

@@ -87,7 +91,7 @@ describe( 'DOMConverter UIElement integration', () => {
8791
} );
8892

8993
it( 'should convert position inside UIElement children to position before UIElement', () => {
90-
const uiElement = new MyUIElement( 'h1' );
94+
const uiElement = createUIElement( 'h1' );
9195
const container = new ViewContainer( 'div', null, [ new ViewContainer( 'div' ), uiElement ] );
9296
const domContainer = converter.viewToDom( container, document, { bind: true } );
9397

@@ -100,7 +104,7 @@ describe( 'DOMConverter UIElement integration', () => {
100104

101105
describe( 'mapDomToView()', () => {
102106
it( 'should return UIElement for DOM elements inside', () => {
103-
const myElement = new MyUIElement( 'div' );
107+
const myElement = createUIElement( 'div' );
104108
const domElement = converter.viewToDom( myElement, document, { bind: true } );
105109

106110
expect( converter.mapDomToView( domElement ) ).to.equal( myElement );
@@ -115,7 +119,7 @@ describe( 'DOMConverter UIElement integration', () => {
115119

116120
describe( 'findCorrespondingViewText()', () => {
117121
it( 'should return UIElement for DOM text inside', () => {
118-
const myElement = new MyUIElement( 'div' );
122+
const myElement = createUIElement( 'div' );
119123
const domElement = converter.viewToDom( myElement, document, { bind: true } );
120124

121125
const domText = domElement.querySelector( 'span' ).childNodes[ 0 ];
@@ -125,7 +129,7 @@ describe( 'DOMConverter UIElement integration', () => {
125129

126130
describe( 'getParentUIElement()', () => {
127131
it( 'should return UIElement for DOM children', () => {
128-
const uiElement = new MyUIElement( 'div' );
132+
const uiElement = createUIElement( 'div' );
129133
const domElement = converter.viewToDom( uiElement, document, { bind: true } );
130134

131135
const domParagraph = domElement.childNodes[ 0 ];
@@ -136,7 +140,7 @@ describe( 'DOMConverter UIElement integration', () => {
136140
} );
137141

138142
it( 'should return null for element itself', () => {
139-
const uiElement = new MyUIElement( 'div' );
143+
const uiElement = createUIElement( 'div' );
140144
const domElement = converter.viewToDom( uiElement, document, { bind: true } );
141145

142146
expect( converter.getParentUIElement( domElement ) ).to.be.null;

tests/view/manual/uielement.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,32 @@ import UIElement from '../../../src/view/uielement';
1717
import Position from '../../../src/view/position';
1818
import writer from '../../../src/view/writer';
1919

20-
class EndingUIElement extends UIElement {
21-
render( domDocument ) {
22-
const root = super.render( domDocument );
20+
function createEndingUIElement() {
21+
const element = new UIElement( 'span' );
2322

23+
element.render = function( domDocument ) {
24+
const root = this.toDomElement( domDocument );
2425
root.classList.add( 'ui-element' );
2526
root.innerHTML = 'END OF PARAGRAPH';
2627

2728
return root;
28-
}
29+
};
30+
31+
return element;
2932
}
3033

31-
class MiddleUIElement extends UIElement {
32-
render( domDocument ) {
33-
const root = super.render( domDocument );
34+
function createMiddleUIElement() {
35+
const element = new UIElement( 'span' );
3436

37+
element.render = function( domDocument ) {
38+
const root = this.toDomElement( domDocument );
3539
root.classList.add( 'ui-element' );
3640
root.innerHTML = 'X';
3741

3842
return root;
39-
}
43+
};
44+
45+
return element;
4046
}
4147

4248
class UIElementTestPlugin extends Plugin {
@@ -47,7 +53,7 @@ class UIElementTestPlugin extends Plugin {
4753
// Add some UIElement to each paragraph.
4854
editing.modelToView.on( 'insert:paragraph', ( evt, data, consumable, conversionApi ) => {
4955
const viewP = conversionApi.mapper.toViewElement( data.item );
50-
viewP.appendChildren( new EndingUIElement( 'span' ) );
56+
viewP.appendChildren( createEndingUIElement() );
5157
}, { priority: 'lowest' } );
5258
}
5359
}
@@ -65,10 +71,10 @@ ClassicEditor
6571
const viewText1 = viewRoot.getChild( 0 ).getChild( 0 );
6672
const viewText2 = viewRoot.getChild( 1 ).getChild( 0 );
6773

68-
writer.insert( new Position( viewText1, 20 ), new MiddleUIElement( 'span' ) );
69-
writer.insert( new Position( viewText1, 20 ), new MiddleUIElement( 'span' ) );
70-
writer.insert( new Position( viewText2, 0 ), new MiddleUIElement( 'span' ) );
71-
writer.insert( new Position( viewText2, 6 ), new MiddleUIElement( 'span' ) );
74+
writer.insert( new Position( viewText1, 20 ), createMiddleUIElement() );
75+
writer.insert( new Position( viewText1, 20 ), createMiddleUIElement() );
76+
writer.insert( new Position( viewText2, 0 ), createMiddleUIElement() );
77+
writer.insert( new Position( viewText2, 6 ), createMiddleUIElement() );
7278

7379
editor.editing.view.render();
7480
} )

tests/view/observer/domeventobserver.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,20 +163,24 @@ describe( 'DomEventObserver', () => {
163163
describe( 'integration with UIElement', () => {
164164
let domRoot, domEvent, evtSpy, uiElement;
165165

166-
class MyUIElement extends UIElement {
167-
render( domDocument ) {
168-
const root = super.render( domDocument );
166+
function createUIElement( name ) {
167+
const element = new UIElement( name );
168+
169+
element.render = function( domDocument ) {
170+
const root = this.toDomElement( domDocument );
169171
root.innerHTML = '<span>foo bar</span>';
170172

171173
return root;
172-
}
174+
};
175+
176+
return element;
173177
}
174178

175179
beforeEach( () => {
176180
domRoot = document.createElement( 'div' );
177181
const viewRoot = createViewRoot( viewDocument );
178182
viewDocument.attachDomRoot( domRoot );
179-
uiElement = new MyUIElement( 'p' );
183+
uiElement = createUIElement( 'p' );
180184
viewRoot.appendChildren( uiElement );
181185
viewDocument.render();
182186

tests/view/observer/mutationobserver.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,17 +420,21 @@ describe( 'MutationObserver', () => {
420420
} );
421421

422422
describe( 'UIElement integration', () => {
423-
class MyUIElement extends UIElement {
424-
render( domDocument ) {
425-
const root = super.render( domDocument );
423+
function createUIElement( name ) {
424+
const element = new UIElement( name );
425+
426+
element.render = function( domDocument ) {
427+
const root = this.toDomElement( domDocument );
426428
root.innerHTML = 'foo bar';
427429

428430
return root;
429-
}
431+
};
432+
433+
return element;
430434
}
431435

432436
beforeEach( () => {
433-
const uiElement = new MyUIElement( 'div' );
437+
const uiElement = createUIElement( 'div' );
434438
viewRoot.appendChildren( uiElement );
435439

436440
viewDocument.render();

tests/view/uielement.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,27 @@ describe( 'UIElement', () => {
122122
expect( domElement.getAttribute( key ) ).to.equal( uiElement.getAttribute( key ) );
123123
}
124124
} );
125+
126+
it( 'should allow to change render() method', () => {
127+
uiElement.render = function( domDocument ) {
128+
return domDocument.createElement( 'b' );
129+
};
130+
131+
expect( uiElement.render( document ).tagName.toLowerCase() ).to.equal( 'b' );
132+
} );
133+
134+
it( 'should allow to add new elements inside', () => {
135+
uiElement.render = function( domDocument ) {
136+
const element = this.toDomElement( domDocument );
137+
const text = domDocument.createTextNode( 'foo bar' );
138+
element.appendChild( text );
139+
140+
return element;
141+
};
142+
143+
const rendered = uiElement.render( document );
144+
expect( rendered.tagName.toLowerCase() ).to.equal( 'span' );
145+
expect( rendered.textContent ).to.equal( 'foo bar' );
146+
} );
125147
} );
126148
} );

0 commit comments

Comments
 (0)