8
0

imageinsertui.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /* globals document */
  6. import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
  7. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  8. import Image from '../../src/image';
  9. import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
  10. import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview';
  11. import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
  12. import ImageInsert from '../../src/imageinsert';
  13. import ImageInsertUI from '../../src/imageinsert/imageinsertui';
  14. import ImageInsertPanelView from '../../src/imageinsert/ui/imageinsertpanelview';
  15. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  16. import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
  17. import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
  18. import EventInfo from '@ckeditor/ckeditor5-utils/src/eventinfo';
  19. import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
  20. import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
  21. import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
  22. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  23. import { UploadAdapterMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
  24. describe( 'ImageInsertUI', () => {
  25. let editor, editorElement, fileRepository, dropdown;
  26. class UploadAdapterPluginMock extends Plugin {
  27. init() {
  28. fileRepository = this.editor.plugins.get( FileRepository );
  29. fileRepository.createUploadAdapter = loader => {
  30. return new UploadAdapterMock( loader );
  31. };
  32. }
  33. }
  34. describe( 'dropdown', () => {
  35. beforeEach( () => {
  36. editorElement = document.createElement( 'div' );
  37. document.body.appendChild( editorElement );
  38. return ClassicEditor
  39. .create( editorElement, {
  40. plugins: [ Paragraph, Image, ImageInsert, ImageInsertUI, FileRepository, UploadAdapterPluginMock, Clipboard ],
  41. toolbar: [ 'imageInsert' ],
  42. image: {
  43. upload: {
  44. panel: {
  45. items: [
  46. 'insertImageViaUrl'
  47. ]
  48. }
  49. }
  50. }
  51. } )
  52. .then( newEditor => {
  53. editor = newEditor;
  54. dropdown = editor.ui.view.toolbar.children.first.children.first;
  55. // Hide all notifications (prevent alert() calls).
  56. const notification = editor.plugins.get( Notification );
  57. notification.on( 'show', evt => evt.stop() );
  58. } );
  59. } );
  60. afterEach( () => {
  61. editorElement.remove();
  62. return editor.destroy();
  63. } );
  64. it( 'should register the "imageInsert" dropdown', () => {
  65. const button = editor.ui.componentFactory.create( 'imageInsert' );
  66. expect( button ).to.be.instanceOf( DropdownView );
  67. } );
  68. it( 'should not insert panel view children until dropdown is not open for the first time', () => {
  69. expect( dropdown.panelView.children.length ).to.equal( 0 );
  70. dropdown.buttonView.fire( 'open' );
  71. expect( dropdown.panelView.children.length ).to.equal( 1 );
  72. expect( dropdown.panelView.children.first ).to.be.instanceOf( ImageInsertPanelView );
  73. } );
  74. describe( 'dropdown action button', () => {
  75. it( 'should be an instance of FileDialogButtonView', () => {
  76. const dropdown = editor.ui.componentFactory.create( 'imageInsert' );
  77. expect( dropdown.buttonView.actionView ).to.be.instanceOf( FileDialogButtonView );
  78. } );
  79. } );
  80. describe( 'dropdown panel buttons', () => {
  81. it( 'should have "Update" label on submit button when URL input is already filled', () => {
  82. const viewDocument = editor.editing.view.document;
  83. editor.setData( '<figure><img src="/assets/sample.png" /></figure>' );
  84. editor.editing.view.change( writer => {
  85. writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
  86. } );
  87. const img = viewDocument.selection.getSelectedElement();
  88. const data = fakeEventData();
  89. const eventInfo = new EventInfo( img, 'click' );
  90. const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
  91. viewDocument.fire( 'click', domEventDataMock );
  92. dropdown.buttonView.fire( 'open' );
  93. const inputValue = dropdown.panelView.children.first.imageURLInputValue;
  94. expect( inputValue ).to.equal( '/assets/sample.png' );
  95. expect( dropdown.panelView.children.first.insertButtonView.label ).to.equal( 'Update' );
  96. } );
  97. it( 'should have "Insert" label on submit button on uploading a new image', () => {
  98. const viewDocument = editor.editing.view.document;
  99. editor.setData( '<p>test</p>' );
  100. editor.editing.view.change( writer => {
  101. writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'end' );
  102. } );
  103. const el = viewDocument.selection.getSelectedElement();
  104. const data = fakeEventData();
  105. const eventInfo = new EventInfo( el, 'click' );
  106. const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
  107. viewDocument.fire( 'click', domEventDataMock );
  108. dropdown.buttonView.fire( 'open' );
  109. const inputValue = dropdown.panelView.children.first.imageURLInputValue;
  110. expect( dropdown.isOpen ).to.be.true;
  111. expect( inputValue ).to.equal( '' );
  112. expect( dropdown.panelView.children.first.insertButtonView.label ).to.equal( 'Insert' );
  113. } );
  114. } );
  115. describe( 'dropdown panel integrations', () => {
  116. describe( 'insert image via URL form', () => {
  117. it( 'should have "Insert image via URL" label on inserting new image', () => {
  118. const viewDocument = editor.editing.view.document;
  119. editor.setData( '<p>test</p>' );
  120. editor.editing.view.change( writer => {
  121. writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'end' );
  122. } );
  123. const el = viewDocument.selection.getSelectedElement();
  124. const data = fakeEventData();
  125. const eventInfo = new EventInfo( el, 'click' );
  126. const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
  127. viewDocument.fire( 'click', domEventDataMock );
  128. dropdown.buttonView.fire( 'open' );
  129. const inputValue = dropdown.panelView.children.first.imageURLInputValue;
  130. const insertImageViaUrlForm = dropdown.panelView.children.first.getIntegration( 'insertImageViaUrl' );
  131. expect( dropdown.isOpen ).to.be.true;
  132. expect( inputValue ).to.equal( '' );
  133. expect( insertImageViaUrlForm.label ).to.equal( 'Insert image via URL' );
  134. } );
  135. it( 'should have "Update image URL" label on updating the image source URL', () => {
  136. const viewDocument = editor.editing.view.document;
  137. editor.setData( '<figure><img src="/assets/sample.png" /></figure>' );
  138. editor.editing.view.change( writer => {
  139. writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
  140. } );
  141. const el = viewDocument.selection.getSelectedElement();
  142. const data = fakeEventData();
  143. const eventInfo = new EventInfo( el, 'click' );
  144. const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
  145. viewDocument.fire( 'click', domEventDataMock );
  146. dropdown.buttonView.fire( 'open' );
  147. const inputValue = dropdown.panelView.children.first.imageURLInputValue;
  148. const insertImageViaUrlForm = dropdown.panelView.children.first.getIntegration( 'insertImageViaUrl' );
  149. expect( dropdown.isOpen ).to.be.true;
  150. expect( inputValue ).to.equal( '/assets/sample.png' );
  151. expect( insertImageViaUrlForm.label ).to.equal( 'Update image URL' );
  152. } );
  153. } );
  154. } );
  155. it( 'should remove all attributes from model except "src" when updating the image source URL', () => {
  156. const viewDocument = editor.editing.view.document;
  157. const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
  158. const submitSpy = sinon.spy();
  159. dropdown.buttonView.fire( 'open' );
  160. const insertButtonView = dropdown.panelView.children.first.insertButtonView;
  161. editor.setData( '<figure><img src="image-url-800w.jpg"' +
  162. 'srcset="image-url-480w.jpg 480w,image-url-800w.jpg 800w"' +
  163. 'sizes="(max-width: 600px) 480px,800px"' +
  164. 'alt="test-image"></figure>' );
  165. editor.editing.view.change( writer => {
  166. writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
  167. } );
  168. const selectedElement = editor.model.document.selection.getSelectedElement();
  169. expect( selectedElement.getAttribute( 'src' ) ).to.equal( 'image-url-800w.jpg' );
  170. expect( selectedElement.hasAttribute( 'srcset' ) ).to.be.true;
  171. dropdown.panelView.children.first.imageURLInputValue = '/assets/sample3.png';
  172. dropdown.on( 'submit', submitSpy );
  173. insertButtonView.fire( 'execute' );
  174. sinon.assert.notCalled( commandSpy );
  175. sinon.assert.calledOnce( submitSpy );
  176. expect( dropdown.isOpen ).to.be.false;
  177. expect( selectedElement.getAttribute( 'src' ) ).to.equal( '/assets/sample3.png' );
  178. expect( selectedElement.hasAttribute( 'srcset' ) ).to.be.false;
  179. expect( selectedElement.hasAttribute( 'sizes' ) ).to.be.false;
  180. } );
  181. describe( 'events', () => {
  182. it( 'should emit "submit" event when clicking on submit button', () => {
  183. const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
  184. const submitSpy = sinon.spy();
  185. dropdown.buttonView.fire( 'open' );
  186. dropdown.on( 'submit', submitSpy );
  187. const insertButtonView = dropdown.panelView.children.first.insertButtonView;
  188. insertButtonView.fire( 'execute' );
  189. expect( dropdown.isOpen ).to.be.false;
  190. sinon.assert.calledOnce( commandSpy );
  191. sinon.assert.calledOnce( submitSpy );
  192. } );
  193. it( 'should emit "cancel" event when clicking on cancel button', () => {
  194. const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
  195. const cancelSpy = sinon.spy();
  196. dropdown.buttonView.fire( 'open' );
  197. dropdown.on( 'cancel', cancelSpy );
  198. const cancelButtonView = dropdown.panelView.children.first.cancelButtonView;
  199. cancelButtonView.fire( 'execute' );
  200. expect( dropdown.isOpen ).to.be.false;
  201. sinon.assert.notCalled( commandSpy );
  202. sinon.assert.calledOnce( cancelSpy );
  203. } );
  204. } );
  205. it( 'should inject integrations to the dropdown panel view from the config', async () => {
  206. const editor = await ClassicEditor
  207. .create( editorElement, {
  208. plugins: [
  209. CKFinder,
  210. Paragraph,
  211. Image,
  212. ImageInsert,
  213. ImageInsertUI,
  214. FileRepository,
  215. UploadAdapterPluginMock,
  216. Clipboard
  217. ],
  218. image: {
  219. upload: {
  220. panel: {
  221. items: [
  222. 'insertImageViaUrl',
  223. 'openCKFinder'
  224. ]
  225. }
  226. }
  227. }
  228. } );
  229. const dropdown = editor.ui.componentFactory.create( 'imageInsert' );
  230. dropdown.buttonView.fire( 'open' );
  231. expect( dropdown.panelView.children.first._integrations.length ).to.equal( 2 );
  232. expect( dropdown.panelView.children.first._integrations.first ).to.be.instanceOf( LabeledFieldView );
  233. expect( dropdown.panelView.children.first._integrations.last ).to.be.instanceOf( ButtonView );
  234. editor.destroy();
  235. } );
  236. } );
  237. } );
  238. function fakeEventData() {
  239. return {
  240. preventDefault: sinon.spy()
  241. };
  242. }