@@ -10,11 +10,13 @@ import View from '@ckeditor/ckeditor5-ui/src/view';
1010import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor' ;
1111import DecoupledEditorUI from '../src/decouplededitorui' ;
1212import EditorUI from '@ckeditor/ckeditor5-core/src/editor/editorui' ;
13+ import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph' ;
1314import DecoupledEditorUIView from '../src/decouplededitoruiview' ;
1415
1516import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard' ;
1617import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils' ;
1718import utils from '@ckeditor/ckeditor5-utils/tests/_utils/utils' ;
19+ import { isElement } from 'lodash-es' ;
1820
1921describe ( 'DecoupledEditorUI' , ( ) => {
2022 let editor , view , ui , viewElement ;
@@ -23,7 +25,7 @@ describe( 'DecoupledEditorUI', () => {
2325
2426 beforeEach ( ( ) => {
2527 return VirtualDecoupledTestEditor
26- . create ( {
28+ . create ( '' , {
2729 toolbar : [ 'foo' , 'bar' ] ,
2830 } )
2931 . then ( newEditor => {
@@ -69,34 +71,75 @@ describe( 'DecoupledEditorUI', () => {
6971 view . editable ,
7072 { isFocused : false } ,
7173 [
72- [ editor . editing . view . document , { isFocused : true } ]
74+ [ ui . focusTracker , { isFocused : true } ]
7375 ] ,
7476 { isFocused : true }
7577 ) ;
7678 } ) ;
7779
78- it ( 'binds view.editable#isReadOnly' , ( ) => {
79- const editable = editor . editing . view . document . getRoot ( ) ;
80+ it ( 'attaches editable UI as view\'s DOM root' , ( ) => {
81+ expect ( editor . editing . view . getDomRoot ( ) ) . to . equal ( view . editable . element ) ;
82+ } ) ;
83+ } ) ;
8084
81- utils . assertBinding (
82- view . editable ,
83- { isReadOnly : false } ,
84- [
85- [ editable , { isReadOnly : true } ]
86- ] ,
87- { isReadOnly : true }
88- ) ;
85+ describe ( 'placeholder' , ( ) => {
86+ it ( 'sets placeholder from editor.config.placeholder' , ( ) => {
87+ return VirtualDecoupledTestEditor
88+ . create ( 'foo' , {
89+ extraPlugins : [ Paragraph ] ,
90+ placeholder : 'placeholder-text' ,
91+ } )
92+ . then ( newEditor => {
93+ const firstChild = newEditor . editing . view . document . getRoot ( ) . getChild ( 0 ) ;
94+
95+ expect ( firstChild . getAttribute ( 'data-placeholder' ) ) . to . equal ( 'placeholder-text' ) ;
96+
97+ return newEditor . destroy ( ) ;
98+ } ) ;
8999 } ) ;
90100
91- it ( 'attaches editable UI as view\'s DOM root' , ( ) => {
92- expect ( editor . editing . view . getDomRoot ( ) ) . to . equal ( view . editable . element ) ;
101+ it ( 'sets placeholder from "placeholder" attribute of a passed element' , ( ) => {
102+ const element = document . createElement ( 'div' ) ;
103+
104+ element . setAttribute ( 'placeholder' , 'placeholder-text' ) ;
105+
106+ return VirtualDecoupledTestEditor
107+ . create ( element , {
108+ extraPlugins : [ Paragraph ]
109+ } )
110+ . then ( newEditor => {
111+ const firstChild = newEditor . editing . view . document . getRoot ( ) . getChild ( 0 ) ;
112+
113+ expect ( firstChild . getAttribute ( 'data-placeholder' ) ) . to . equal ( 'placeholder-text' ) ;
114+
115+ return newEditor . destroy ( ) ;
116+ } ) ;
117+ } ) ;
118+
119+ it ( 'uses editor.config.placeholder rather than "placeholder" attribute of a passed element' , ( ) => {
120+ const element = document . createElement ( 'div' ) ;
121+
122+ element . setAttribute ( 'placeholder' , 'placeholder-text' ) ;
123+
124+ return VirtualDecoupledTestEditor
125+ . create ( element , {
126+ placeholder : 'config takes precedence' ,
127+ extraPlugins : [ Paragraph ]
128+ } )
129+ . then ( newEditor => {
130+ const firstChild = newEditor . editing . view . document . getRoot ( ) . getChild ( 0 ) ;
131+
132+ expect ( firstChild . getAttribute ( 'data-placeholder' ) ) . to . equal ( 'config takes precedence' ) ;
133+
134+ return newEditor . destroy ( ) ;
135+ } ) ;
93136 } ) ;
94137 } ) ;
95138
96139 describe ( 'view.toolbar#items' , ( ) => {
97140 it ( 'are filled with the config.toolbar (specified as an Array)' , ( ) => {
98141 return VirtualDecoupledTestEditor
99- . create ( {
142+ . create ( '' , {
100143 toolbar : [ 'foo' , 'bar' ]
101144 } )
102145 . then ( editor => {
@@ -111,7 +154,7 @@ describe( 'DecoupledEditorUI', () => {
111154
112155 it ( 'are filled with the config.toolbar (specified as an Object)' , ( ) => {
113156 return VirtualDecoupledTestEditor
114- . create ( {
157+ . create ( '' , {
115158 toolbar : {
116159 items : [ 'foo' , 'bar' ] ,
117160 viewportTopOffset : 100
@@ -129,7 +172,7 @@ describe( 'DecoupledEditorUI', () => {
129172 } ) ;
130173
131174 it ( 'initializes keyboard navigation between view#toolbar and view#editable' , ( ) => {
132- return VirtualDecoupledTestEditor . create ( )
175+ return VirtualDecoupledTestEditor . create ( '' )
133176 . then ( editor => {
134177 const ui = editor . ui ;
135178 const view = ui . view ;
@@ -152,6 +195,47 @@ describe( 'DecoupledEditorUI', () => {
152195 } ) ;
153196 } ) ;
154197
198+ describe ( 'destroy()' , ( ) => {
199+ it ( 'detaches the DOM root then destroys the UI view' , ( ) => {
200+ return VirtualDecoupledTestEditor . create ( '' )
201+ . then ( newEditor => {
202+ const destroySpy = sinon . spy ( newEditor . ui . view , 'destroy' ) ;
203+ const detachSpy = sinon . spy ( newEditor . editing . view , 'detachDomRoot' ) ;
204+
205+ return newEditor . destroy ( )
206+ . then ( ( ) => {
207+ sinon . assert . callOrder ( detachSpy , destroySpy ) ;
208+ } ) ;
209+ } ) ;
210+ } ) ;
211+
212+ it ( 'restores the editor element back to its original state' , ( ) => {
213+ const domElement = document . createElement ( 'div' ) ;
214+
215+ domElement . setAttribute ( 'foo' , 'bar' ) ;
216+ domElement . setAttribute ( 'data-baz' , 'qux' ) ;
217+ domElement . classList . add ( 'foo-class' ) ;
218+
219+ return VirtualDecoupledTestEditor . create ( domElement )
220+ . then ( newEditor => {
221+ return newEditor . destroy ( )
222+ . then ( ( ) => {
223+ const attributes = { } ;
224+
225+ for ( const attribute of domElement . attributes ) {
226+ attributes [ attribute . name ] = attribute . value ;
227+ }
228+
229+ expect ( attributes ) . to . deep . equal ( {
230+ foo : 'bar' ,
231+ 'data-baz' : 'qux' ,
232+ class : 'foo-class'
233+ } ) ;
234+ } ) ;
235+ } ) ;
236+ } ) ;
237+ } ) ;
238+
155239 describe ( 'element()' , ( ) => {
156240 it ( 'returns correct element instance' , ( ) => {
157241 expect ( ui . element ) . to . equal ( viewElement ) ;
@@ -185,10 +269,14 @@ function viewCreator( name ) {
185269}
186270
187271class VirtualDecoupledTestEditor extends VirtualTestEditor {
188- constructor ( config ) {
272+ constructor ( sourceElementOrData , config ) {
189273 super ( config ) ;
190274
191- const view = new DecoupledEditorUIView ( this . locale ) ;
275+ if ( isElement ( sourceElementOrData ) ) {
276+ this . sourceElement = sourceElementOrData ;
277+ }
278+
279+ const view = new DecoupledEditorUIView ( this . locale , this . editing . view ) ;
192280 this . ui = new DecoupledEditorUI ( this , view ) ;
193281
194282 this . ui . componentFactory . add ( 'foo' , viewCreator ( 'foo' ) ) ;
@@ -201,14 +289,20 @@ class VirtualDecoupledTestEditor extends VirtualTestEditor {
201289 return super . destroy ( ) ;
202290 }
203291
204- static create ( config ) {
292+ static create ( sourceElementOrData , config ) {
205293 return new Promise ( resolve => {
206- const editor = new this ( config ) ;
294+ const editor = new this ( sourceElementOrData , config ) ;
207295
208296 resolve (
209297 editor . initPlugins ( )
210298 . then ( ( ) => {
211299 editor . ui . init ( ) ;
300+
301+ const initialData = isElement ( sourceElementOrData ) ?
302+ sourceElementOrData . innerHTML :
303+ sourceElementOrData ;
304+
305+ editor . data . init ( initialData ) ;
212306 editor . fire ( 'ready' ) ;
213307 } )
214308 . then ( ( ) => editor )
0 commit comments