8
0
Просмотр исходного кода

Merge pull request #8019 from ckeditor/i/7890

Other (image): Introduced `ImageInsert` as a standalone plugin that contains `ImageUpload` functionality. Closes #7890.

Internal (image): Reverted the changes in `ImageUpload` plugin that introduced the "Insert image via URL" feature.

Fix (image): Aligned and fixed the styling for the split button in the `ImageInsert` dropdown. Closes #7986. Closes #7927.

Docs (image): Fixed the docs after plugins separation.

MAJOR BREAKING CHANGE (image): In order to use the "insert image via URL" feature you now need to load the `ImageInsert` plugin and use the `imageInsert` button instead of the `imageUpload` button that implemented that functionality previously previously.
Piotrek Koszuliński 5 лет назад
Родитель
Сommit
4671ed10a4
23 измененных файлов с 1001 добавлено и 1005 удалено
  1. 2 0
      packages/ckeditor5-image/docs/_snippets/features/build-image-source.js
  2. 6 1
      packages/ckeditor5-image/docs/_snippets/features/image-insert-via-url.js
  3. 7 3
      packages/ckeditor5-image/docs/features/image.md
  4. 98 0
      packages/ckeditor5-image/src/imageinsert.js
  5. 151 0
      packages/ckeditor5-image/src/imageinsert/imageinsertui.js
  6. 3 3
      packages/ckeditor5-image/src/imageinsert/ui/imageinsertformrowview.js
  7. 10 10
      packages/ckeditor5-image/src/imageinsert/ui/imageinsertpanelview.js
  8. 77 0
      packages/ckeditor5-image/src/imageinsert/utils.js
  9. 0 55
      packages/ckeditor5-image/src/imageupload.js
  10. 24 139
      packages/ckeditor5-image/src/imageupload/imageuploadui.js
  11. 0 69
      packages/ckeditor5-image/src/imageupload/utils.js
  12. 327 0
      packages/ckeditor5-image/tests/imageinsert/imageinsertui.js
  13. 1 1
      packages/ckeditor5-image/tests/imageinsert/ui/imageinsertformrowview.js
  14. 4 4
      packages/ckeditor5-image/tests/imageinsert/ui/imageinsertpanelview.js
  15. 148 0
      packages/ckeditor5-image/tests/imageinsert/utils.js
  16. 109 539
      packages/ckeditor5-image/tests/imageupload/imageuploadui.js
  17. 1 146
      packages/ckeditor5-image/tests/imageupload/utils.js
  18. 1 1
      packages/ckeditor5-image/tests/manual/imageinsertviaurl.html
  19. 4 13
      packages/ckeditor5-image/tests/manual/imageinsertviaurl.js
  20. 18 0
      packages/ckeditor5-image/tests/manual/imageinsertviaurl.md
  21. 0 18
      packages/ckeditor5-image/tests/manual/imageuploadviaurl.md
  22. 9 2
      packages/ckeditor5-image/theme/imageinsert.css
  23. 1 1
      packages/ckeditor5-image/theme/imageinsertformrowview.css

+ 2 - 0
packages/ckeditor5-image/docs/_snippets/features/build-image-source.js

@@ -8,9 +8,11 @@
 import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
 import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
 
 
 import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize';
 import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize';
+import ImageInsert from '@ckeditor/ckeditor5-image/src/imageinsert';
 import LinkImage from '@ckeditor/ckeditor5-link/src/linkimage';
 import LinkImage from '@ckeditor/ckeditor5-link/src/linkimage';
 
 
 ClassicEditor.builtinPlugins.push( ImageResize );
 ClassicEditor.builtinPlugins.push( ImageResize );
+ClassicEditor.builtinPlugins.push( ImageInsert );
 ClassicEditor.builtinPlugins.push( LinkImage );
 ClassicEditor.builtinPlugins.push( LinkImage );
 
 
 window.ClassicEditor = ClassicEditor;
 window.ClassicEditor = ClassicEditor;

+ 6 - 1
packages/ckeditor5-image/docs/_snippets/features/image-insert-via-url.js

@@ -7,10 +7,15 @@
 
 
 import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config.js';
 import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config.js';
 
 
+const toolbarItems = [ ...ClassicEditor.defaultConfig.toolbar.items ];
+
+toolbarItems.splice( toolbarItems.indexOf( 'imageUpload' ), 1, 'imageInsert' );
+
 ClassicEditor
 ClassicEditor
 	.create( document.querySelector( '#snippet-image-insert-via-url' ), {
 	.create( document.querySelector( '#snippet-image-insert-via-url' ), {
 		removePlugins: [ 'ImageToolbar', 'ImageCaption', 'ImageStyle', 'ImageResize', 'LinkImage' ],
 		removePlugins: [ 'ImageToolbar', 'ImageCaption', 'ImageStyle', 'ImageResize', 'LinkImage' ],
 		toolbar: {
 		toolbar: {
+			items: toolbarItems,
 			viewportTopOffset: window.getViewportTopOffsetConfig()
 			viewportTopOffset: window.getViewportTopOffsetConfig()
 		},
 		},
 		image: {
 		image: {
@@ -24,7 +29,7 @@ ClassicEditor
 		cloudServices: CS_CONFIG
 		cloudServices: CS_CONFIG
 	} )
 	} )
 	.then( editor => {
 	.then( editor => {
-		window.imageViaUrl = editor;
+		window.editorInsertImageViaUrl = editor;
 	} )
 	} )
 	.catch( err => {
 	.catch( err => {
 		console.error( err );
 		console.error( err );

+ 7 - 3
packages/ckeditor5-image/docs/features/image.md

@@ -13,6 +13,7 @@ The [`@ckeditor/ckeditor5-image`](https://www.npmjs.com/package/@ckeditor/ckedit
 * {@link module:image/imagestyle~ImageStyle} adds support for [image styles](#image-styles).
 * {@link module:image/imagestyle~ImageStyle} adds support for [image styles](#image-styles).
 * {@link module:image/imagetextalternative~ImageTextAlternative} adds support for adding text alternative.
 * {@link module:image/imagetextalternative~ImageTextAlternative} adds support for adding text alternative.
 * {@link module:image/imageupload~ImageUpload} adds support for {@link features/image-upload uploading dropped or pasted images}.
 * {@link module:image/imageupload~ImageUpload} adds support for {@link features/image-upload uploading dropped or pasted images}.
+* {@link module:image/imageinsert~ImageInsert} adds support for [inserting images via URL](#inserting-images-via-source-url) and other custom integrations.
 * {@link module:image/imageresize~ImageResize} adds support for [resizing images](#resizing-images).
 * {@link module:image/imageresize~ImageResize} adds support for [resizing images](#resizing-images).
 * {@link module:link/linkimage~LinkImage} adds support for [linking images](#linking-images).
 * {@link module:link/linkimage~LinkImage} adds support for [linking images](#linking-images).
 
 
@@ -86,12 +87,15 @@ See the {@link features/image-upload Image upload} guide.
 
 
 Besides the ability to insert images by uploading them directly from your disk or via CKFinder, you can also configure CKEditor 5 to allow inserting images via source URL.
 Besides the ability to insert images by uploading them directly from your disk or via CKFinder, you can also configure CKEditor 5 to allow inserting images via source URL.
 
 
-In order to enable this option, configure {@link module:image/imageupload~ImageUploadPanelConfig#items `image.upload.panel.items`} like below:
+In order to enable this option, install the `ImageInsert` plugin and configure {@link module:image/imageupload~ImageUploadPanelConfig#items `image.upload.panel.items`} like below:
 
 
 ```js
 ```js
+import ImageInsert from '@ckeditor/ckeditor5-image/src/imageinsert';
+
 ClassicEditor
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 	.create( document.querySelector( '#editor' ), {
-		// ...
+		plugins: [ ... , ImageInsert ],
+		toolbar: [ ... , 'imageInsert' ],
 		image: {
 		image: {
 			// ...
 			// ...
 			upload: {
 			upload: {
@@ -103,7 +107,7 @@ ClassicEditor
 	} )
 	} )
 ```
 ```
 
 
-This will extend the standalone **Insert image** button in the toolbar by adding a dropdown panel with the new feature. To open the panel and add the image URL, click the arrow next to the image button. Check the demo below to insert a new image via URL or update an existing image by selecting it, opening the dropdown panel and pasting a new URL.
+This will add a new **Insert image** dropdown in the toolbar. To open the panel and add the image URL, click the arrow next to the image button. Check the demo below to insert a new image via URL or update an existing image by selecting it, opening the dropdown panel and pasting a new URL.
 
 
 {@snippet features/image-insert-via-url}
 {@snippet features/image-insert-via-url}
 
 

+ 98 - 0
packages/ckeditor5-image/src/imageinsert.js

@@ -0,0 +1,98 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module image/imageinsert
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ImageUpload from './imageupload';
+import ImageInsertUI from './imageinsert/imageinsertui';
+
+/**
+ * The image insert plugin.
+ *
+ * For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature}
+ * and {@glink features/image#inserting-images-via-source-url Insert images via source URL} documentation.
+ *
+ * This plugin does not do anything directly, but it loads a set of specific plugins
+ * to enable image uploading or inserting via implemented integrations:
+ *
+ * * {@link module:image/imageupload~ImageUpload}
+ * * {@link module:image/imageinsert/imageinsertui~ImageInsertUI},
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class ImageInsert extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'ImageInsert';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ ImageUpload, ImageInsertUI ];
+	}
+}
+
+/**
+ * Image insert panel view configuration.
+ * **NOTE:** Currently the panel settings are configurable through the `image.upload` property.
+ *
+ * @protected
+ * @member {module:image/imageupload~ImageUploadPanelConfig} module:image/imageupload~ImageUploadConfig#panel
+ */
+
+/**
+ * The configuration of the image insert dropdown panel view. Used by the image insert feature in the `@ckeditor/ckeditor5-image` package.
+ *
+ *		ClassicEditor
+ *			.create( editorElement, {
+ * 				image: {
+ * 					upload: {
+ * 						panel: ... // panel settings for "imageInsert" view goes here
+ * 					}
+ * 				}
+ *			} )
+ *			.then( ... )
+ *			.catch( ... );
+ *
+ * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
+ *
+ * @protected
+ * @interface module:image/imageupload~ImageUploadPanelConfig
+ */
+
+/**
+ * The list of {@link module:image/imageinsert~ImageInsert} integrations.
+ *
+ * The option accepts string tokens.
+ * * for predefined integrations, we have two special strings: `insertImageViaUrl` and `openCKFinder`.
+ * The former adds the **Insert image via URL** feature, while the latter adds the built-in **CKFinder** integration.
+ * * for custom integrations, each string should be a name of the component registered in the
+ * {@link module:ui/componentfactory~ComponentFactory component factory}.
+ * If you have a plugin `PluginX` that registers `pluginXButton` component, then the integration token
+ * in that case should be `pluginXButton`.
+ *
+ *		// Add `insertImageViaUrl`, `openCKFinder` and custom `pluginXButton` integrations.
+ *		const imageUploadConfig = {
+ *			upload: {
+ *				panel: {
+ *					items: [
+ *						'insertImageViaUrl',
+ *						'openCKFinder',
+ *						'pluginXButton'
+ *					]
+ *				}
+ *			}
+ *		};
+ *
+ * @member {Array.<String>} module:image/imageupload~ImageUploadPanelConfig#items
+ * @default [ 'insertImageViaUrl' ]
+ */

+ 151 - 0
packages/ckeditor5-image/src/imageinsert/imageinsertui.js

@@ -0,0 +1,151 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module image/imageinsert/imageinsertui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ImageInsertPanelView from './ui/imageinsertpanelview';
+import { prepareIntegrations } from './utils';
+
+import { isImage } from '../image/utils';
+
+/**
+ * The image insert dropdown plugin.
+ *
+ * For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature}
+ * and {@glink features/image#inserting-images-via-source-url Insert images via source URL} documentation.
+ *
+ * Adds the `'imageInsert'` dropdown to the {@link module:ui/componentfactory~ComponentFactory UI component factory}.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class ImageInsertUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'ImageInsertUI';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+
+		editor.ui.componentFactory.add( 'imageInsert', locale => {
+			return this._createDropdownView( locale );
+		} );
+	}
+
+	/**
+	 * Creates the dropdown view.
+	 *
+	 * @param {module:utils/locale~Locale} locale The localization services instance.
+	 *
+	 * @private
+	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
+	 */
+	_createDropdownView( locale ) {
+		const editor = this.editor;
+		const imageInsertView = new ImageInsertPanelView( locale, prepareIntegrations( editor ) );
+		const command = editor.commands.get( 'imageUpload' );
+
+		const dropdownView = imageInsertView.dropdownView;
+		const splitButtonView = dropdownView.buttonView;
+
+		splitButtonView.actionView = editor.ui.componentFactory.create( 'imageUpload' );
+		// After we replaced action button with `imageUpload` component,
+		// we have lost a proper styling and some minor visual quirks have appeared.
+		// Brining back original split button classes helps fix the button styling
+		// See https://github.com/ckeditor/ckeditor5/issues/7986.
+		splitButtonView.actionView.extendTemplate( {
+			attributes: {
+				class: 'ck ck-button ck-splitbutton__action'
+			}
+		} );
+
+		return this._setUpDropdown( dropdownView, imageInsertView, command );
+	}
+
+	/**
+	 * Sets up the dropdown view.
+	 *
+	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdownView.
+	 * @param {module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView} imageInsertView An imageInsertView.
+	 * @param {module:core/command~Command} command An imageInsert command
+	 *
+	 * @private
+	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
+	 */
+	_setUpDropdown( dropdownView, imageInsertView, command ) {
+		const editor = this.editor;
+		const t = editor.t;
+		const insertButtonView = imageInsertView.insertButtonView;
+		const insertImageViaUrlForm = imageInsertView.getIntegration( 'insertImageViaUrl' );
+		const panelView = dropdownView.panelView;
+
+		dropdownView.bind( 'isEnabled' ).to( command );
+
+		// Defer the children injection to improve initial performance.
+		// See https://github.com/ckeditor/ckeditor5/pull/8019#discussion_r484069652.
+		dropdownView.buttonView.once( 'open', () => {
+			panelView.children.add( imageInsertView );
+		} );
+
+		dropdownView.on( 'change:isOpen', () => {
+			const selectedElement = editor.model.document.selection.getSelectedElement();
+
+			if ( dropdownView.isOpen ) {
+				imageInsertView.focus();
+
+				if ( isImage( selectedElement ) ) {
+					imageInsertView.imageURLInputValue = selectedElement.getAttribute( 'src' );
+					insertButtonView.label = t( 'Update' );
+					insertImageViaUrlForm.label = t( 'Update image URL' );
+				} else {
+					imageInsertView.imageURLInputValue = '';
+					insertButtonView.label = t( 'Insert' );
+					insertImageViaUrlForm.label = t( 'Insert image via URL' );
+				}
+			}
+		} );
+
+		imageInsertView.delegate( 'submit', 'cancel' ).to( dropdownView );
+		this.delegate( 'cancel' ).to( dropdownView );
+
+		dropdownView.on( 'submit', () => {
+			closePanel();
+			onSubmit();
+		} );
+
+		dropdownView.on( 'cancel', () => {
+			closePanel();
+		} );
+
+		function onSubmit() {
+			const selectedElement = editor.model.document.selection.getSelectedElement();
+
+			if ( isImage( selectedElement ) ) {
+				editor.model.change( writer => {
+					writer.setAttribute( 'src', imageInsertView.imageURLInputValue, selectedElement );
+					writer.removeAttribute( 'srcset', selectedElement );
+					writer.removeAttribute( 'sizes', selectedElement );
+				} );
+			} else {
+				editor.execute( 'imageInsert', { source: imageInsertView.imageURLInputValue } );
+			}
+		}
+
+		function closePanel() {
+			editor.editing.view.focus();
+			dropdownView.isOpen = false;
+		}
+
+		return dropdownView;
+	}
+}

+ 3 - 3
packages/ckeditor5-image/src/imageupload/ui/imageuploadformrowview.js → packages/ckeditor5-image/src/imageinsert/ui/imageinsertformrowview.js

@@ -4,16 +4,16 @@
  */
  */
 
 
 /**
 /**
- * @module image/imageupload/ui/imageuploadformrowview
+ * @module image/imageinsert/ui/imageinsertformrowview
  */
  */
 
 
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 
 
-import '../../../theme/imageuploadformrowview.css';
+import '../../../theme/imageinsertformrowview.css';
 
 
 /**
 /**
  * The class representing a single row in a complex form,
  * The class representing a single row in a complex form,
- * used by {@link module:image/imageupload/ui/imageuploadpanelview~ImageUploadPanelView}.
+ * used by {@link module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView}.
  *
  *
  * **Note**: For now this class is private. When more use cases appear (beyond `ckeditor5-table` and `ckeditor5-image`),
  * **Note**: For now this class is private. When more use cases appear (beyond `ckeditor5-table` and `ckeditor5-image`),
  * it will become a component in `ckeditor5-ui`.
  * it will become a component in `ckeditor5-ui`.

+ 10 - 10
packages/ckeditor5-image/src/imageupload/ui/imageuploadpanelview.js → packages/ckeditor5-image/src/imageinsert/ui/imageinsertpanelview.js

@@ -4,14 +4,14 @@
  */
  */
 
 
 /**
 /**
- * @module image/imageupload/ui/imageuploadpanelview
+ * @module image/imageinsert/ui/imageinsertpanelview
  */
  */
 
 
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 
 
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview';
 import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview';
-import ImageUploadFormRowView from './imageuploadformrowview';
+import ImageInsertFormRowView from './imageinsertformrowview';
 import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
 import { createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
 
 
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
@@ -25,18 +25,18 @@ import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
 import checkIcon from '@ckeditor/ckeditor5-core/theme/icons/check.svg';
 import checkIcon from '@ckeditor/ckeditor5-core/theme/icons/check.svg';
 import cancelIcon from '@ckeditor/ckeditor5-core/theme/icons/cancel.svg';
 import cancelIcon from '@ckeditor/ckeditor5-core/theme/icons/cancel.svg';
 
 
-import '../../../theme/imageupload.css';
+import '../../../theme/imageinsert.css';
 
 
 /**
 /**
  * The insert an image via URL view controller class.
  * The insert an image via URL view controller class.
  *
  *
- * See {@link module:image/imageupload/ui/imageuploadpanelview~ImageUploadPanelView}.
+ * See {@link module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView}.
  *
  *
  * @extends module:ui/view~View
  * @extends module:ui/view~View
  */
  */
-export default class ImageUploadPanelView extends View {
+export default class ImageInsertPanelView extends View {
 	/**
 	/**
-	 * Creates a view for the dropdown panel of {@link module:image/imageupload/imageuploadui~ImageUploadUI}.
+	 * Creates a view for the dropdown panel of {@link module:image/imageinsert/imageinsertui~ImageInsertUI}.
 	 *
 	 *
 	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
 	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
 	 * @param {Object} [integrations] An integrations object that contains
 	 * @param {Object} [integrations] An integrations object that contains
@@ -151,7 +151,7 @@ export default class ImageUploadPanelView extends View {
 			attributes: {
 			attributes: {
 				class: [
 				class: [
 					'ck',
 					'ck',
-					'ck-image-upload-form'
+					'ck-image-insert-form'
 				],
 				],
 
 
 				tabindex: '-1'
 				tabindex: '-1'
@@ -159,12 +159,12 @@ export default class ImageUploadPanelView extends View {
 
 
 			children: [
 			children: [
 				...this._integrations,
 				...this._integrations,
-				new ImageUploadFormRowView( locale, {
+				new ImageInsertFormRowView( locale, {
 					children: [
 					children: [
 						this.insertButtonView,
 						this.insertButtonView,
 						this.cancelButtonView
 						this.cancelButtonView
 					],
 					],
-					class: 'ck-image-upload-form__action-row'
+					class: 'ck-image-insert-form__action-row'
 				} )
 				} )
 			]
 			]
 		} );
 		} );
@@ -247,7 +247,7 @@ export default class ImageUploadPanelView extends View {
 
 
 		panelView.extendTemplate( {
 		panelView.extendTemplate( {
 			attributes: {
 			attributes: {
-				class: 'ck-image-upload__panel'
+				class: 'ck-image-insert__panel'
 			}
 			}
 		} );
 		} );
 
 

+ 77 - 0
packages/ckeditor5-image/src/imageinsert/utils.js

@@ -0,0 +1,77 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module image/imageinsert/utils
+ */
+
+import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
+import { createLabeledInputText } from '@ckeditor/ckeditor5-ui/src/labeledfield/utils';
+
+/**
+ * Creates integrations object that will be passed to the
+ * {@link module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView}.
+ *
+ * @param {module:core/editor/editor~Editor} editor Editor instance.
+ *
+ * @returns {Object.<String, module:ui/view~View>} Integrations object.
+ */
+export function prepareIntegrations( editor ) {
+	const panelItems = editor.config.get( 'image.upload.panel.items' );
+	const imageInsertUIPlugin = editor.plugins.get( 'ImageInsertUI' );
+
+	const PREDEFINED_INTEGRATIONS = {
+		'insertImageViaUrl': createLabeledInputView( editor.locale )
+	};
+
+	if ( !panelItems ) {
+		return PREDEFINED_INTEGRATIONS;
+	}
+
+	// Prepares ckfinder component for the `openCKFinder` integration token.
+	if ( panelItems.find( item => item === 'openCKFinder' ) && editor.ui.componentFactory.has( 'ckfinder' ) ) {
+		const ckFinderButton = editor.ui.componentFactory.create( 'ckfinder' );
+		ckFinderButton.set( {
+			withText: true,
+			class: 'ck-image-insert__ck-finder-button'
+		} );
+
+		// We want to close the dropdown panel view when user clicks the ckFinderButton.
+		ckFinderButton.delegate( 'execute' ).to( imageInsertUIPlugin, 'cancel' );
+
+		PREDEFINED_INTEGRATIONS.openCKFinder = ckFinderButton;
+	}
+
+	// Creates integrations object of valid views to pass it to the ImageInsertPanelView.
+	return panelItems.reduce( ( object, key ) => {
+		if ( PREDEFINED_INTEGRATIONS[ key ] ) {
+			object[ key ] = PREDEFINED_INTEGRATIONS[ key ];
+		} else if ( editor.ui.componentFactory.has( key ) ) {
+			object[ key ] = editor.ui.componentFactory.create( key );
+		}
+
+		return object;
+	}, {} );
+}
+
+/**
+ * Creates labeled field view.
+ *
+ * @param {module:utils/locale~Locale} locale The localization services instance.
+ *
+ * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
+ */
+export function createLabeledInputView( locale ) {
+	const t = locale.t;
+	const labeledInputView = new LabeledFieldView( locale, createLabeledInputText );
+
+	labeledInputView.set( {
+		label: t( 'Insert image via URL' )
+	} );
+	labeledInputView.fieldView.placeholder = 'https://example.com/src/image.png';
+	labeledInputView.infoText = t( 'Paste the image source URL.' );
+
+	return labeledInputView;
+}

+ 0 - 55
packages/ckeditor5-image/src/imageupload.js

@@ -83,58 +83,3 @@ export default class ImageUpload extends Plugin {
  * @member {Array.<String>} module:image/imageupload~ImageUploadConfig#types
  * @member {Array.<String>} module:image/imageupload~ImageUploadConfig#types
  * @default [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]
  * @default [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]
  */
  */
-
-/**
- * The image upload panel view configuration.
- *
- * @protected
- * @member {module:image/imageupload~ImageUploadPanelConfig} module:image/imageupload~ImageUploadConfig#panel
- */
-
-/**
- * The configuration of the image upload dropdown panel view. Used by the image upload feature in the `@ckeditor/ckeditor5-image` package.
- *
- *		ClassicEditor
- *			.create( editorElement, {
- * 				image: {
- * 					upload: {
- * 						panel: ... // Panel settings go here.
- * 					}
- * 				}
- *			} )
- *			.then( ... )
- *			.catch( ... );
- *
- * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
- *
- * @protected
- * @interface module:image/imageupload~ImageUploadPanelConfig
- */
-
-/**
- * The list of {@link module:image/imageupload~ImageUpload} integrations.
- *
- * This option accepts string tokens:
- * * For predefined integrations, there are two special strings: `insertImageViaUrl` and `openCKFinder`.
- * The former adds the {@glink features/image#inserting-images-via-source-url Insert image via URL} feature, while the latter adds the
- * built-in {@glink features/image-upload/ckfinder CKFinder} integration.
- * * For custom integrations, each string should be a name of the already registered component.
- * If you have a plugin `PluginX` that registers the `pluginXButton` component, then in that case the integration token
- * should be `pluginXButton`.
- *
- *		// Add `insertImageViaUrl`, `openCKFinder` and custom `pluginXButton` integrations.
- *		const imageUploadConfig = {
- *			upload: {
- *				panel: {
- *					items: [
- *						'insertImageViaUrl',
- *						'openCKFinder',
- *						'pluginXButton'
- *					]
- *				}
- *			}
- *		};
- *
- * @member {Array.<String>} module:image/imageupload~ImageUploadPanelConfig#items
- * @default [ 'insertImageViaUrl' ]
- */

+ 24 - 139
packages/ckeditor5-image/src/imageupload/imageuploadui.js

@@ -8,21 +8,17 @@
  */
  */
 
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import ImageUploadPanelView from './ui/imageuploadpanelview';
-
 import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview';
 import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview';
-import { createImageTypeRegExp, prepareIntegrations } from './utils';
+import { createImageTypeRegExp } from './utils';
 
 
 import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
 import imageIcon from '@ckeditor/ckeditor5-core/theme/icons/image.svg';
 
 
-import { isImage } from '../image/utils';
-
 /**
 /**
  * The image upload button plugin.
  * The image upload button plugin.
  *
  *
  * For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature} documentation.
  * For a detailed overview, check the {@glink features/image-upload/image-upload Image upload feature} documentation.
  *
  *
- * Adds the `'imageUpload'` dropdown to the {@link module:ui/componentfactory~ComponentFactory UI component factory}.
+ * Adds the `'imageUpload'` button to the {@link module:ui/componentfactory~ComponentFactory UI component factory}.
  *
  *
  * @extends module:core/plugin~Plugin
  * @extends module:core/plugin~Plugin
  */
  */
@@ -39,148 +35,37 @@ export default class ImageUploadUI extends Plugin {
 	 */
 	 */
 	init() {
 	init() {
 		const editor = this.editor;
 		const editor = this.editor;
-		const isImageUploadPanelViewEnabled = !!editor.config.get( 'image.upload.panel.items' );
+		const t = editor.t;
 
 
+		// Setup `imageUpload` button.
 		editor.ui.componentFactory.add( 'imageUpload', locale => {
 		editor.ui.componentFactory.add( 'imageUpload', locale => {
-			if ( isImageUploadPanelViewEnabled ) {
-				return this._createDropdownView( locale );
-			} else {
-				return this._createFileDialogButtonView( locale );
-			}
-		} );
-	}
+			const view = new FileDialogButtonView( locale );
+			const command = editor.commands.get( 'imageUpload' );
+			const imageTypes = editor.config.get( 'image.upload.types' );
+			const imageTypesRegExp = createImageTypeRegExp( imageTypes );
 
 
-	/**
-	 * Sets up the dropdown view.
-	 *
-	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdownView.
-	 * @param {module:image/imageupload/ui/imageuploadpanelview~ImageUploadPanelView} imageUploadView An imageUploadView.
-	 * @param {module:core/command~Command} command An imageUpload command
-	 *
-	 * @private
-	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
-	 */
-	_setUpDropdown( dropdownView, imageUploadView, command ) {
-		const editor = this.editor;
-		const t = editor.t;
-		const insertButtonView = imageUploadView.insertButtonView;
-		const insertImageViaUrlForm = imageUploadView.getIntegration( 'insertImageViaUrl' );
+			view.set( {
+				acceptedType: imageTypes.map( type => `image/${ type }` ).join( ',' ),
+				allowMultipleFiles: true
+			} );
 
 
-		dropdownView.bind( 'isEnabled' ).to( command );
+			view.buttonView.set( {
+				label: t( 'Insert image' ),
+				icon: imageIcon,
+				tooltip: true
+			} );
 
 
-		dropdownView.on( 'change:isOpen', () => {
-			const selectedElement = editor.model.document.selection.getSelectedElement();
+			view.buttonView.bind( 'isEnabled' ).to( command );
 
 
-			if ( dropdownView.isOpen ) {
-				imageUploadView.focus();
+			view.on( 'done', ( evt, files ) => {
+				const imagesToUpload = Array.from( files ).filter( file => imageTypesRegExp.test( file.type ) );
 
 
-				if ( isImage( selectedElement ) ) {
-					imageUploadView.imageURLInputValue = selectedElement.getAttribute( 'src' );
-					insertButtonView.label = t( 'Update' );
-					insertImageViaUrlForm.label = t( 'Update image URL' );
-				} else {
-					imageUploadView.imageURLInputValue = '';
-					insertButtonView.label = t( 'Insert' );
-					insertImageViaUrlForm.label = t( 'Insert image via URL' );
+				if ( imagesToUpload.length ) {
+					editor.execute( 'imageUpload', { file: imagesToUpload } );
 				}
 				}
-			}
-		} );
-
-		imageUploadView.delegate( 'submit', 'cancel' ).to( dropdownView );
-		this.delegate( 'cancel' ).to( dropdownView );
-
-		dropdownView.on( 'submit', () => {
-			closePanel();
-			onSubmit();
-		} );
-
-		dropdownView.on( 'cancel', () => {
-			closePanel();
-		} );
-
-		function onSubmit() {
-			const selectedElement = editor.model.document.selection.getSelectedElement();
-
-			if ( isImage( selectedElement ) ) {
-				editor.model.change( writer => {
-					writer.setAttribute( 'src', imageUploadView.imageURLInputValue, selectedElement );
-					writer.removeAttribute( 'srcset', selectedElement );
-					writer.removeAttribute( 'sizes', selectedElement );
-				} );
-			} else {
-				editor.execute( 'imageInsert', { source: imageUploadView.imageURLInputValue } );
-			}
-		}
-
-		function closePanel() {
-			editor.editing.view.focus();
-			dropdownView.isOpen = false;
-		}
-
-		return dropdownView;
-	}
+			} );
 
 
-	/**
-	 * Creates the dropdown view.
-	 *
-	 * @param {module:utils/locale~Locale} locale The localization services instance.
-	 *
-	 * @private
-	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
-	 */
-	_createDropdownView( locale ) {
-		const editor = this.editor;
-		const imageUploadView = new ImageUploadPanelView( locale, prepareIntegrations( editor ) );
-		const command = editor.commands.get( 'imageUpload' );
-
-		const dropdownView = imageUploadView.dropdownView;
-		const panelView = dropdownView.panelView;
-		const splitButtonView = dropdownView.buttonView;
-
-		splitButtonView.actionView = this._createFileDialogButtonView( locale );
-
-		panelView.children.add( imageUploadView );
-
-		return this._setUpDropdown( dropdownView, imageUploadView, command );
-	}
-
-	/**
-	 * Creates and sets up the file dialog button view.
-	 *
-	 * @param {module:utils/locale~Locale} locale The localization services instance.
-	 *
-	 * @private
-	 * @returns {module:upload/ui/filedialogbuttonview~FileDialogButtonView}
-	 */
-	_createFileDialogButtonView( locale ) {
-		const editor = this.editor;
-		const t = locale.t;
-		const imageTypes = editor.config.get( 'image.upload.types' );
-		const fileDialogButtonView = new FileDialogButtonView( locale );
-		const imageTypesRegExp = createImageTypeRegExp( imageTypes );
-		const command = editor.commands.get( 'imageUpload' );
-
-		fileDialogButtonView.set( {
-			acceptedType: imageTypes.map( type => `image/${ type }` ).join( ',' ),
-			allowMultipleFiles: true
-		} );
-
-		fileDialogButtonView.buttonView.set( {
-			label: t( 'Insert image' ),
-			icon: imageIcon,
-			tooltip: true
+			return view;
 		} );
 		} );
-
-		fileDialogButtonView.buttonView.bind( 'isEnabled' ).to( command );
-
-		fileDialogButtonView.on( 'done', ( evt, files ) => {
-			const imagesToUpload = Array.from( files ).filter( file => imageTypesRegExp.test( file.type ) );
-
-			if ( imagesToUpload.length ) {
-				editor.execute( 'imageUpload', { file: imagesToUpload } );
-			}
-		} );
-
-		return fileDialogButtonView;
 	}
 	}
 }
 }

+ 0 - 69
packages/ckeditor5-image/src/imageupload/utils.js

@@ -9,9 +9,6 @@
 
 
 /* global fetch, File */
 /* global fetch, File */
 
 
-import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
-import { createLabeledInputText } from '@ckeditor/ckeditor5-ui/src/labeledfield/utils';
-
 /**
 /**
  * Creates a regular expression used to test for image files.
  * Creates a regular expression used to test for image files.
  *
  *
@@ -85,69 +82,3 @@ function getImageMimeType( blob, src ) {
 		return 'image/jpeg';
 		return 'image/jpeg';
 	}
 	}
 }
 }
-
-/**
- * Creates an integrations object that will be passed to the
- * {@link module:image/imageupload/ui/imageuploadpanelview~ImageUploadPanelView}.
- *
- * @param {module:core/editor/editor~Editor} editor The editor instance.
- *
- * @returns {Object.<String, module:ui/view~View>} The integrations object.
- */
-export function prepareIntegrations( editor ) {
-	const panelItems = editor.config.get( 'image.upload.panel.items' );
-	const imageUploadUIPlugin = editor.plugins.get( 'ImageUploadUI' );
-
-	const PREDEFINED_INTEGRATIONS = {
-		'insertImageViaUrl': createLabeledInputView( editor.locale )
-	};
-
-	if ( !panelItems ) {
-		return PREDEFINED_INTEGRATIONS;
-	}
-
-	// Prepares ckfinder component for the `openCKFinder` integration token.
-	if ( panelItems.find( item => item === 'openCKFinder' ) && editor.ui.componentFactory.has( 'ckfinder' ) ) {
-		const ckFinderButton = editor.ui.componentFactory.create( 'ckfinder' );
-		ckFinderButton.set( {
-			withText: true,
-			class: 'ck-image-upload__ck-finder-button'
-		} );
-
-		// We want to close the dropdown panel view when user clicks the ckFinderButton.
-		ckFinderButton.delegate( 'execute' ).to( imageUploadUIPlugin, 'cancel' );
-
-		PREDEFINED_INTEGRATIONS.openCKFinder = ckFinderButton;
-	}
-
-	// Creates integrations object of valid views to pass it to the ImageUploadPanelView.
-	return panelItems.reduce( ( object, key ) => {
-		if ( PREDEFINED_INTEGRATIONS[ key ] ) {
-			object[ key ] = PREDEFINED_INTEGRATIONS[ key ];
-		} else if ( editor.ui.componentFactory.has( key ) ) {
-			object[ key ] = editor.ui.componentFactory.create( key );
-		}
-
-		return object;
-	}, {} );
-}
-
-/**
- * Creates a labeled field view.
- *
- * @param {module:utils/locale~Locale} locale The localization services instance.
- *
- * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
- */
-export function createLabeledInputView( locale ) {
-	const t = locale.t;
-	const labeledInputView = new LabeledFieldView( locale, createLabeledInputText );
-
-	labeledInputView.set( {
-		label: t( 'Insert image via URL' )
-	} );
-	labeledInputView.fieldView.placeholder = 'https://example.com/src/image.png';
-	labeledInputView.infoText = t( 'Paste the image source URL.' );
-
-	return labeledInputView;
-}

+ 327 - 0
packages/ckeditor5-image/tests/imageinsert/imageinsertui.js

@@ -0,0 +1,327 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Image from '../../src/image';
+import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
+import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview';
+import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
+import ImageInsert from '../../src/imageinsert';
+import ImageInsertUI from '../../src/imageinsert/imageinsertui';
+import ImageInsertPanelView from '../../src/imageinsert/ui/imageinsertpanelview';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
+import EventInfo from '@ckeditor/ckeditor5-utils/src/eventinfo';
+import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
+import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
+import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import { UploadAdapterMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
+
+describe( 'ImageInsertUI', () => {
+	let editor, editorElement, fileRepository, dropdown;
+
+	class UploadAdapterPluginMock extends Plugin {
+		init() {
+			fileRepository = this.editor.plugins.get( FileRepository );
+			fileRepository.createUploadAdapter = loader => {
+				return new UploadAdapterMock( loader );
+			};
+		}
+	}
+
+	describe( 'dropdown', () => {
+		beforeEach( () => {
+			editorElement = document.createElement( 'div' );
+			document.body.appendChild( editorElement );
+
+			return ClassicEditor
+				.create( editorElement, {
+					plugins: [ Paragraph, Image, ImageInsert, ImageInsertUI, FileRepository, UploadAdapterPluginMock, Clipboard ],
+					toolbar: [ 'imageInsert' ],
+					image: {
+						upload: {
+							panel: {
+								items: [
+									'insertImageViaUrl'
+								]
+							}
+						}
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					dropdown = editor.ui.view.toolbar.children.first.children.first;
+
+					// Hide all notifications (prevent alert() calls).
+					const notification = editor.plugins.get( Notification );
+					notification.on( 'show', evt => evt.stop() );
+				} );
+		} );
+
+		afterEach( () => {
+			editorElement.remove();
+
+			return editor.destroy();
+		} );
+
+		it( 'should register the "imageInsert" dropdown', () => {
+			const button = editor.ui.componentFactory.create( 'imageInsert' );
+
+			expect( button ).to.be.instanceOf( DropdownView );
+		} );
+
+		it( 'should not insert panel view children until dropdown is not open for the first time', () => {
+			expect( dropdown.panelView.children.length ).to.equal( 0 );
+
+			dropdown.buttonView.fire( 'open' );
+
+			expect( dropdown.panelView.children.length ).to.equal( 1 );
+			expect( dropdown.panelView.children.first ).to.be.instanceOf( ImageInsertPanelView );
+		} );
+
+		describe( 'dropdown action button', () => {
+			it( 'should be an instance of FileDialogButtonView', () => {
+				const dropdown = editor.ui.componentFactory.create( 'imageInsert' );
+
+				expect( dropdown.buttonView.actionView ).to.be.instanceOf( FileDialogButtonView );
+			} );
+		} );
+
+		describe( 'dropdown panel buttons', () => {
+			it( 'should have "Update" label on submit button when URL input is already filled', () => {
+				const viewDocument = editor.editing.view.document;
+
+				editor.setData( '<figure><img src="/assets/sample.png" /></figure>' );
+
+				editor.editing.view.change( writer => {
+					writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
+				} );
+
+				const img = viewDocument.selection.getSelectedElement();
+
+				const data = fakeEventData();
+				const eventInfo = new EventInfo( img, 'click' );
+				const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
+
+				viewDocument.fire( 'click', domEventDataMock );
+
+				dropdown.buttonView.fire( 'open' );
+
+				const inputValue = dropdown.panelView.children.first.imageURLInputValue;
+
+				expect( inputValue ).to.equal( '/assets/sample.png' );
+				expect( dropdown.panelView.children.first.insertButtonView.label ).to.equal( 'Update' );
+			} );
+
+			it( 'should have "Insert" label on submit button on uploading a new image', () => {
+				const viewDocument = editor.editing.view.document;
+
+				editor.setData( '<p>test</p>' );
+
+				editor.editing.view.change( writer => {
+					writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'end' );
+				} );
+
+				const el = viewDocument.selection.getSelectedElement();
+
+				const data = fakeEventData();
+				const eventInfo = new EventInfo( el, 'click' );
+				const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
+
+				viewDocument.fire( 'click', domEventDataMock );
+
+				dropdown.buttonView.fire( 'open' );
+
+				const inputValue = dropdown.panelView.children.first.imageURLInputValue;
+
+				expect( dropdown.isOpen ).to.be.true;
+				expect( inputValue ).to.equal( '' );
+				expect( dropdown.panelView.children.first.insertButtonView.label ).to.equal( 'Insert' );
+			} );
+		} );
+
+		describe( 'dropdown panel integrations', () => {
+			describe( 'insert image via URL form', () => {
+				it( 'should have "Insert image via URL" label on inserting new image', () => {
+					const viewDocument = editor.editing.view.document;
+
+					editor.setData( '<p>test</p>' );
+
+					editor.editing.view.change( writer => {
+						writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'end' );
+					} );
+
+					const el = viewDocument.selection.getSelectedElement();
+
+					const data = fakeEventData();
+					const eventInfo = new EventInfo( el, 'click' );
+					const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
+
+					viewDocument.fire( 'click', domEventDataMock );
+
+					dropdown.buttonView.fire( 'open' );
+
+					const inputValue = dropdown.panelView.children.first.imageURLInputValue;
+
+					const insertImageViaUrlForm = dropdown.panelView.children.first.getIntegration( 'insertImageViaUrl' );
+
+					expect( dropdown.isOpen ).to.be.true;
+					expect( inputValue ).to.equal( '' );
+					expect( insertImageViaUrlForm.label ).to.equal( 'Insert image via URL' );
+				} );
+
+				it( 'should have "Update image URL" label on updating the image source URL', () => {
+					const viewDocument = editor.editing.view.document;
+
+					editor.setData( '<figure><img src="/assets/sample.png" /></figure>' );
+
+					editor.editing.view.change( writer => {
+						writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
+					} );
+
+					const el = viewDocument.selection.getSelectedElement();
+
+					const data = fakeEventData();
+					const eventInfo = new EventInfo( el, 'click' );
+					const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
+
+					viewDocument.fire( 'click', domEventDataMock );
+
+					dropdown.buttonView.fire( 'open' );
+
+					const inputValue = dropdown.panelView.children.first.imageURLInputValue;
+					const insertImageViaUrlForm = dropdown.panelView.children.first.getIntegration( 'insertImageViaUrl' );
+
+					expect( dropdown.isOpen ).to.be.true;
+					expect( inputValue ).to.equal( '/assets/sample.png' );
+					expect( insertImageViaUrlForm.label ).to.equal( 'Update image URL' );
+				} );
+			} );
+		} );
+
+		it( 'should remove all attributes from model except "src" when updating the image source URL', () => {
+			const viewDocument = editor.editing.view.document;
+			const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
+			const submitSpy = sinon.spy();
+
+			dropdown.buttonView.fire( 'open' );
+
+			const insertButtonView = dropdown.panelView.children.first.insertButtonView;
+
+			editor.setData( '<figure><img src="image-url-800w.jpg"' +
+			'srcset="image-url-480w.jpg 480w,image-url-800w.jpg 800w"' +
+			'sizes="(max-width: 600px) 480px,800px"' +
+			'alt="test-image"></figure>' );
+
+			editor.editing.view.change( writer => {
+				writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
+			} );
+
+			const selectedElement = editor.model.document.selection.getSelectedElement();
+
+			expect( selectedElement.getAttribute( 'src' ) ).to.equal( 'image-url-800w.jpg' );
+			expect( selectedElement.hasAttribute( 'srcset' ) ).to.be.true;
+
+			dropdown.panelView.children.first.imageURLInputValue = '/assets/sample3.png';
+
+			dropdown.on( 'submit', submitSpy );
+
+			insertButtonView.fire( 'execute' );
+
+			sinon.assert.notCalled( commandSpy );
+			sinon.assert.calledOnce( submitSpy );
+			expect( dropdown.isOpen ).to.be.false;
+			expect( selectedElement.getAttribute( 'src' ) ).to.equal( '/assets/sample3.png' );
+			expect( selectedElement.hasAttribute( 'srcset' ) ).to.be.false;
+			expect( selectedElement.hasAttribute( 'sizes' ) ).to.be.false;
+		} );
+
+		describe( 'events', () => {
+			it( 'should emit "submit" event when clicking on submit button', () => {
+				const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
+				const submitSpy = sinon.spy();
+
+				dropdown.buttonView.fire( 'open' );
+
+				dropdown.on( 'submit', submitSpy );
+
+				const insertButtonView = dropdown.panelView.children.first.insertButtonView;
+
+				insertButtonView.fire( 'execute' );
+
+				expect( dropdown.isOpen ).to.be.false;
+				sinon.assert.calledOnce( commandSpy );
+				sinon.assert.calledOnce( submitSpy );
+			} );
+
+			it( 'should emit "cancel" event when clicking on cancel button', () => {
+				const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
+				const cancelSpy = sinon.spy();
+
+				dropdown.buttonView.fire( 'open' );
+
+				dropdown.on( 'cancel', cancelSpy );
+
+				const cancelButtonView = dropdown.panelView.children.first.cancelButtonView;
+
+				cancelButtonView.fire( 'execute' );
+
+				expect( dropdown.isOpen ).to.be.false;
+				sinon.assert.notCalled( commandSpy );
+				sinon.assert.calledOnce( cancelSpy );
+			} );
+		} );
+
+		it( 'should inject integrations to the dropdown panel view from the config', async () => {
+			const editor = await ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						CKFinder,
+						Paragraph,
+						Image,
+						ImageInsert,
+						ImageInsertUI,
+						FileRepository,
+						UploadAdapterPluginMock,
+						Clipboard
+					],
+					image: {
+						upload: {
+							panel: {
+								items: [
+									'insertImageViaUrl',
+									'openCKFinder'
+								]
+							}
+						}
+					}
+				} );
+
+			const dropdown = editor.ui.componentFactory.create( 'imageInsert' );
+
+			dropdown.buttonView.fire( 'open' );
+
+			expect( dropdown.panelView.children.first._integrations.length ).to.equal( 2 );
+			expect( dropdown.panelView.children.first._integrations.first ).to.be.instanceOf( LabeledFieldView );
+			expect( dropdown.panelView.children.first._integrations.last ).to.be.instanceOf( ButtonView );
+
+			editor.destroy();
+		} );
+	} );
+} );
+
+function fakeEventData() {
+	return {
+		preventDefault: sinon.spy()
+	};
+}

+ 1 - 1
packages/ckeditor5-image/tests/imageupload/ui/imageuploadformrowview.js → packages/ckeditor5-image/tests/imageinsert/ui/imageinsertformrowview.js

@@ -4,7 +4,7 @@
  */
  */
 
 
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import View from '@ckeditor/ckeditor5-ui/src/view';
-import ImageUploadFormRowView from '../../../src/imageupload/ui/imageuploadformrowview';
+import ImageUploadFormRowView from '../../../src/imageinsert/ui/imageinsertformrowview';
 import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
 import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
 
 
 describe( 'ImageUploadFormRowView', () => {
 describe( 'ImageUploadFormRowView', () => {

+ 4 - 4
packages/ckeditor5-image/tests/imageupload/ui/imageuploadpanelview.js → packages/ckeditor5-image/tests/imageinsert/ui/imageinsertpanelview.js

@@ -8,8 +8,8 @@
 import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
 import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
 import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
 import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
 
 
-import ImageUploadPanelView from '../../../src/imageupload/ui/imageuploadpanelview';
-import ImageUploadFormRowView from '../../../src/imageupload/ui/imageuploadformrowview';
+import ImageUploadPanelView from '../../../src/imageinsert/ui/imageinsertpanelview';
+import ImageUploadFormRowView from '../../../src/imageinsert/ui/imageinsertformrowview';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview';
 import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
@@ -21,7 +21,7 @@ import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler';
 import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
 import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 
 
-import { createLabeledInputView } from '../../../src/imageupload/utils';
+import { createLabeledInputView } from '../../../src/imageinsert/utils';
 
 
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 
@@ -140,7 +140,7 @@ describe( 'ImageUploadPanelView', () => {
 	describe( 'template', () => {
 	describe( 'template', () => {
 		it( 'should create element from the template', () => {
 		it( 'should create element from the template', () => {
 			expect( view.element.classList.contains( 'ck' ) ).to.true;
 			expect( view.element.classList.contains( 'ck' ) ).to.true;
-			expect( view.element.classList.contains( 'ck-image-upload-form' ) ).to.true;
+			expect( view.element.classList.contains( 'ck-image-insert-form' ) ).to.true;
 			expect( view.element.getAttribute( 'tabindex' ) ).to.equal( '-1' );
 			expect( view.element.getAttribute( 'tabindex' ) ).to.equal( '-1' );
 		} );
 		} );
 
 

+ 148 - 0
packages/ckeditor5-image/tests/imageinsert/utils.js

@@ -0,0 +1,148 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import Image from '../../src/image';
+import ImageUploadUI from '../../src/imageinsert/imageinsertui';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Link from '@ckeditor/ckeditor5-link/src/link';
+import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
+import { prepareIntegrations, createLabeledInputView } from '../../src/imageinsert/utils';
+
+describe( 'Upload utils', () => {
+	describe( 'prepareIntegrations()', () => {
+		it( 'should return "insetImageViaUrl" and "openCKFinder" integrations', async () => {
+			const editorElement = document.createElement( 'div' );
+			document.body.appendChild( editorElement );
+
+			const editor = await ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						CKFinder,
+						Paragraph,
+						Image,
+						ImageUploadUI
+					],
+					image: {
+						upload: {
+							panel: {
+								items: [
+									'insertImageViaUrl',
+									'openCKFinder'
+								]
+							}
+						}
+					}
+				} );
+
+			const openCKFinderExtendedView = Object.values( prepareIntegrations( editor ) )[ 1 ];
+
+			expect( openCKFinderExtendedView.class ).contains( 'ck-image-insert__ck-finder-button' );
+			expect( openCKFinderExtendedView.label ).to.equal( 'Insert image or file' );
+			expect( openCKFinderExtendedView.withText ).to.be.true;
+
+			editor.destroy();
+			editorElement.remove();
+		} );
+
+		it( 'should return only "insertImageViaUrl" integration and throw warning ' +
+			'for "image-upload-integrations-invalid-view" error', async () => {
+			const editorElement = document.createElement( 'div' );
+			document.body.appendChild( editorElement );
+
+			const editor = await ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						Paragraph,
+						Image,
+						ImageUploadUI
+					],
+					image: {
+						upload: {
+							panel: {
+								items: [
+									'insertImageViaUrl',
+									'openCKFinder'
+								]
+							}
+						}
+					}
+				} );
+
+			expect( Object.values( prepareIntegrations( editor ) ).length ).to.equal( 1 );
+
+			editor.destroy();
+			editorElement.remove();
+		} );
+
+		it( 'should return only "link" integration', async () => {
+			const editorElement = document.createElement( 'div' );
+			document.body.appendChild( editorElement );
+
+			const editor = await ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						Paragraph,
+						Link,
+						Image,
+						ImageUploadUI
+					],
+					image: {
+						upload: {
+							panel: {
+								items: [
+									'link'
+								]
+							}
+						}
+					}
+				} );
+
+			expect( Object.values( prepareIntegrations( editor ) ).length ).to.equal( 1 );
+			expect( Object.values( prepareIntegrations( editor ) )[ 0 ].label ).to.equal( 'Link' );
+			expect( Object.values( prepareIntegrations( editor ) )[ 0 ] ).to.be.instanceOf( ButtonView );
+
+			editor.destroy();
+			editorElement.remove();
+		} );
+
+		it( 'should return "insertImageViaUrl" integration, when no integrations were configured', async () => {
+			const editorElement = document.createElement( 'div' );
+			document.body.appendChild( editorElement );
+
+			const editor = await ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						Paragraph,
+						Image,
+						ImageUploadUI
+					]
+				} );
+
+			expect( Object.keys( prepareIntegrations( editor ) ).length ).to.equal( 1 );
+
+			editor.destroy();
+			editorElement.remove();
+		} );
+	} );
+
+	describe( 'createLabeledInputView()', () => {
+		describe( 'image URL input view', () => {
+			it( 'should have placeholder', () => {
+				const view = createLabeledInputView( { t: val => val } );
+				expect( view.fieldView.placeholder ).to.equal( 'https://example.com/src/image.png' );
+			} );
+
+			it( 'should have info text', () => {
+				const view = createLabeledInputView( { t: val => val } );
+				expect( view.infoText ).to.match( /^Paste the image source URL/ );
+			} );
+		} );
+	} );
+} );

+ 109 - 539
packages/ckeditor5-image/tests/imageupload/imageuploadui.js

@@ -9,7 +9,6 @@ import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'
 
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Image from '../../src/image';
 import Image from '../../src/image';
-import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
 import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview';
 import FileDialogButtonView from '@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview';
 import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
 import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
 import ImageUploadUI from '../../src/imageupload/imageuploadui';
 import ImageUploadUI from '../../src/imageupload/imageuploadui';
@@ -17,11 +16,6 @@ import ImageUploadEditing from '../../src/imageupload/imageuploadediting';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
 import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
-import EventInfo from '@ckeditor/ckeditor5-utils/src/eventinfo';
-import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
-import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
-import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
-import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 
 
 import { createNativeFileMock, UploadAdapterMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
 import { createNativeFileMock, UploadAdapterMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
 import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -38,578 +32,154 @@ describe( 'ImageUploadUI', () => {
 		}
 		}
 	}
 	}
 
 
-	describe( 'file dialog button', () => {
-		beforeEach( () => {
-			editorElement = document.createElement( 'div' );
-			document.body.appendChild( editorElement );
-
-			return ClassicEditor
-				.create( editorElement, {
-					plugins: [ Paragraph, Image, ImageUploadEditing, ImageUploadUI, FileRepository, UploadAdapterPluginMock, Clipboard ]
-				} )
-				.then( newEditor => {
-					editor = newEditor;
-					model = editor.model;
-
-					// Hide all notifications (prevent alert() calls).
-					const notification = editor.plugins.get( Notification );
-					notification.on( 'show', evt => evt.stop() );
-				} );
-		} );
-
-		afterEach( () => {
-			editorElement.remove();
-
-			return editor.destroy();
-		} );
-		it( 'should register imageUpload file dialog button', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-
-			expect( button ).to.be.instanceOf( FileDialogButtonView );
-		} );
-
-		it( 'should set proper accepted mime-types for imageUpload button as defined in configuration', () => {
-			editor.config.set( 'image.upload.types', [ 'svg+xml', 'jpeg', 'vnd.microsoft.icon', 'x-xbitmap' ] );
-
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-
-			expect( button.acceptedType ).to.equal( 'image/svg+xml,image/jpeg,image/vnd.microsoft.icon,image/x-xbitmap' );
-		} );
-
-		it( 'should be disabled while ImageUploadCommand is disabled', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const command = editor.commands.get( 'imageUpload' );
-
-			command.isEnabled = true;
-
-			expect( button.buttonView.isEnabled ).to.true;
-
-			command.isEnabled = false;
-
-			expect( button.buttonView.isEnabled ).to.false;
-		} );
-
-		// ckeditor5-upload/#77
-		it( 'should be properly bound with ImageUploadCommand', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const command = editor.commands.get( 'imageUpload' );
-			const spy = sinon.spy();
-
-			button.render();
-
-			button.buttonView.on( 'execute', spy );
-
-			command.isEnabled = false;
-
-			button.buttonView.element.dispatchEvent( new Event( 'click' ) );
-
-			sinon.assert.notCalled( spy );
-		} );
-
-		it( 'should execute imageUpload command', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const files = [ createNativeFileMock() ];
-
-			button.fire( 'done', files );
-			sinon.assert.calledOnce( executeStub );
-			expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-			expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
-		} );
-
-		it( 'should execute imageUpload command with multiple files', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const files = [ createNativeFileMock(), createNativeFileMock(), createNativeFileMock() ];
-
-			button.fire( 'done', files );
-			sinon.assert.calledOnce( executeStub );
-			expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-			expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
-		} );
-
-		it( 'should optimize the insertion position', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const files = [ createNativeFileMock() ];
-
-			setModelData( model, '<paragraph>f[]oo</paragraph>' );
-
-			button.fire( 'done', files );
-
-			const id = fileRepository.getLoader( files[ 0 ] ).id;
-
-			expect( getModelData( model ) ).to.equal(
-				`[<image uploadId="${ id }" uploadStatus="reading"></image>]` +
-			'<paragraph>foo</paragraph>'
-			);
-		} );
-
-		it( 'should correctly insert multiple files', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const files = [ createNativeFileMock(), createNativeFileMock() ];
-
-			setModelData( model, '<paragraph>foo[]</paragraph><paragraph>bar</paragraph>' );
-
-			button.fire( 'done', files );
-
-			const id1 = fileRepository.getLoader( files[ 0 ] ).id;
-			const id2 = fileRepository.getLoader( files[ 1 ] ).id;
-
-			expect( getModelData( model ) ).to.equal(
-				'<paragraph>foo</paragraph>' +
-			`<image uploadId="${ id1 }" uploadStatus="reading"></image>` +
-			`[<image uploadId="${ id2 }" uploadStatus="reading"></image>]` +
-			'<paragraph>bar</paragraph>'
-			);
-		} );
-
-		it( 'should not execute imageUpload if the file is not an image', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const file = {
-				type: 'media/mp3',
-				size: 1024
-			};
-
-			button.fire( 'done', [ file ] );
-			sinon.assert.notCalled( executeStub );
-		} );
-
-		it( 'should work even if the FileList does not support iterators', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const files = {
-				0: createNativeFileMock(),
-				length: 1
-			};
-
-			button.fire( 'done', files );
-			sinon.assert.calledOnce( executeStub );
-			expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-			expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( [ files[ 0 ] ] );
-		} );
-
-		it( 'should add the new image after the selected one, without replacing the selected image', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const files = [ createNativeFileMock() ];
-
-			setModelData( model, '[<image src="/assets/sample.png"></image>]<paragraph>bar</paragraph>' );
-
-			button.fire( 'done', files );
-
-			const id1 = fileRepository.getLoader( files[ 0 ] ).id;
-
-			expect( getModelData( model ) ).to.equal(
-				'<image src="/assets/sample.png"></image>' +
-				`[<image uploadId="${ id1 }" uploadStatus="reading"></image>]` +
-				'<paragraph>bar</paragraph>'
-			);
-		} );
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicEditor
+			.create( editorElement, {
+				plugins: [ Paragraph, Image, ImageUploadEditing, ImageUploadUI, FileRepository, UploadAdapterPluginMock, Clipboard ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+
+				// Hide all notifications (prevent alert() calls).
+				const notification = editor.plugins.get( Notification );
+				notification.on( 'show', evt => evt.stop() );
+			} );
 	} );
 	} );
 
 
-	describe( 'dropdown', () => {
-		beforeEach( () => {
-			editorElement = document.createElement( 'div' );
-			document.body.appendChild( editorElement );
-
-			return ClassicEditor
-				.create( editorElement, {
-					plugins: [ Paragraph, Image, ImageUploadEditing, ImageUploadUI, FileRepository, UploadAdapterPluginMock, Clipboard ],
-					image: {
-						upload: {
-							panel: {
-								items: [
-									'insertImageViaUrl'
-								]
-							}
-						}
-					}
-				} )
-				.then( newEditor => {
-					editor = newEditor;
-					model = editor.model;
+	afterEach( () => {
+		editorElement.remove();
 
 
-					// Hide all notifications (prevent alert() calls).
-					const notification = editor.plugins.get( Notification );
-					notification.on( 'show', evt => evt.stop() );
-				} );
-		} );
-
-		afterEach( () => {
-			editorElement.remove();
+		return editor.destroy();
+	} );
 
 
-			return editor.destroy();
-		} );
-		it( 'should register imageUpload dropdown', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
+	it( 'should register imageUpload button', () => {
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
 
 
-			expect( button ).to.be.instanceOf( DropdownView );
-		} );
+		expect( button ).to.be.instanceOf( FileDialogButtonView );
+	} );
 
 
-		it( 'should set proper accepted mime-types for imageUpload button as defined in configuration', () => {
-			editor.config.set( 'image.upload.types', [ 'svg+xml', 'jpeg', 'vnd.microsoft.icon', 'x-xbitmap' ] );
+	it( 'should set proper accepted mime-types for imageUpload button as defined in configuration', () => {
+		editor.config.set( 'image.upload.types', [ 'svg+xml', 'jpeg', 'vnd.microsoft.icon', 'x-xbitmap' ] );
 
 
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
 
 
-			expect( fileDialogButton.acceptedType ).to.equal( 'image/svg+xml,image/jpeg,image/vnd.microsoft.icon,image/x-xbitmap' );
-		} );
+		expect( button.acceptedType ).to.equal( 'image/svg+xml,image/jpeg,image/vnd.microsoft.icon,image/x-xbitmap' );
+	} );
 
 
-		it( 'should be disabled while ImageUploadCommand is disabled', () => {
-			const button = editor.ui.componentFactory.create( 'imageUpload' );
-			const command = editor.commands.get( 'imageUpload' );
+	it( 'should be disabled while ImageUploadCommand is disabled', () => {
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const command = editor.commands.get( 'imageUpload' );
 
 
-			command.isEnabled = true;
+		command.isEnabled = true;
 
 
-			expect( button.buttonView.isEnabled ).to.true;
+		expect( button.buttonView.isEnabled ).to.true;
 
 
-			command.isEnabled = false;
+		command.isEnabled = false;
 
 
-			expect( button.buttonView.isEnabled ).to.false;
-		} );
+		expect( button.buttonView.isEnabled ).to.false;
+	} );
 
 
-		// ckeditor5-upload/#77
-		it( 'should be properly bound with ImageUploadCommand', () => {
-			const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-			const command = editor.commands.get( 'imageUpload' );
-			const spy = sinon.spy();
+	// ckeditor5-upload/#77
+	it( 'should be properly bound with ImageUploadCommand', () => {
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const command = editor.commands.get( 'imageUpload' );
+		const spy = sinon.spy();
 
 
-			dropdown.render();
+		button.render();
 
 
-			dropdown.buttonView.on( 'execute', spy );
+		button.buttonView.on( 'execute', spy );
 
 
-			command.isEnabled = false;
+		command.isEnabled = false;
 
 
-			dropdown.buttonView.element.dispatchEvent( new Event( 'click' ) );
+		button.buttonView.element.dispatchEvent( new Event( 'click' ) );
 
 
-			sinon.assert.notCalled( spy );
-		} );
+		sinon.assert.notCalled( spy );
+	} );
 
 
-		it( 'should execute imageUpload command', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
-			const files = [ createNativeFileMock() ];
+	it( 'should execute imageUpload command', () => {
+		const executeStub = sinon.stub( editor, 'execute' );
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const files = [ createNativeFileMock() ];
 
 
-			fileDialogButton.fire( 'done', files );
-			sinon.assert.calledOnce( executeStub );
-			expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-			expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
-		} );
+		button.fire( 'done', files );
+		sinon.assert.calledOnce( executeStub );
+		expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
+		expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
+	} );
 
 
-		it( 'should execute imageUpload command with multiple files', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
-			const files = [ createNativeFileMock(), createNativeFileMock(), createNativeFileMock() ];
+	it( 'should execute imageUpload command with multiple files', () => {
+		const executeStub = sinon.stub( editor, 'execute' );
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const files = [ createNativeFileMock(), createNativeFileMock(), createNativeFileMock() ];
 
 
-			fileDialogButton.fire( 'done', files );
-			sinon.assert.calledOnce( executeStub );
-			expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-			expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
-		} );
+		button.fire( 'done', files );
+		sinon.assert.calledOnce( executeStub );
+		expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
+		expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( files );
+	} );
 
 
-		it( 'should optimize the insertion position', () => {
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
-			const files = [ createNativeFileMock() ];
+	it( 'should optimize the insertion position', () => {
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const files = [ createNativeFileMock() ];
 
 
-			setModelData( model, '<paragraph>f[]oo</paragraph>' );
+		setModelData( model, '<paragraph>f[]oo</paragraph>' );
 
 
-			fileDialogButton.fire( 'done', files );
+		button.fire( 'done', files );
 
 
-			const id = fileRepository.getLoader( files[ 0 ] ).id;
+		const id = fileRepository.getLoader( files[ 0 ] ).id;
 
 
-			expect( getModelData( model ) ).to.equal(
-				`[<image uploadId="${ id }" uploadStatus="reading"></image>]` +
+		expect( getModelData( model ) ).to.equal(
+			`[<image uploadId="${ id }" uploadStatus="reading"></image>]` +
 			'<paragraph>foo</paragraph>'
 			'<paragraph>foo</paragraph>'
-			);
-		} );
+		);
+	} );
 
 
-		it( 'should correctly insert multiple files', () => {
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
-			const files = [ createNativeFileMock(), createNativeFileMock() ];
+	it( 'should correctly insert multiple files', () => {
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const files = [ createNativeFileMock(), createNativeFileMock() ];
 
 
-			setModelData( model, '<paragraph>foo[]</paragraph><paragraph>bar</paragraph>' );
+		setModelData( model, '<paragraph>foo[]</paragraph><paragraph>bar</paragraph>' );
 
 
-			fileDialogButton.fire( 'done', files );
+		button.fire( 'done', files );
 
 
-			const id1 = fileRepository.getLoader( files[ 0 ] ).id;
-			const id2 = fileRepository.getLoader( files[ 1 ] ).id;
+		const id1 = fileRepository.getLoader( files[ 0 ] ).id;
+		const id2 = fileRepository.getLoader( files[ 1 ] ).id;
 
 
-			expect( getModelData( model ) ).to.equal(
-				'<paragraph>foo</paragraph>' +
+		expect( getModelData( model ) ).to.equal(
+			'<paragraph>foo</paragraph>' +
 			`<image uploadId="${ id1 }" uploadStatus="reading"></image>` +
 			`<image uploadId="${ id1 }" uploadStatus="reading"></image>` +
 			`[<image uploadId="${ id2 }" uploadStatus="reading"></image>]` +
 			`[<image uploadId="${ id2 }" uploadStatus="reading"></image>]` +
 			'<paragraph>bar</paragraph>'
 			'<paragraph>bar</paragraph>'
-			);
-		} );
-
-		it( 'should not execute imageUpload if the file is not an image', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
-			const file = {
-				type: 'media/mp3',
-				size: 1024
-			};
-
-			fileDialogButton.fire( 'done', [ file ] );
-			sinon.assert.notCalled( executeStub );
-		} );
-
-		it( 'should work even if the FileList does not support iterators', () => {
-			const executeStub = sinon.stub( editor, 'execute' );
-			const plugin = editor.plugins.get( 'ImageUploadUI' );
-			const fileDialogButton = plugin._createFileDialogButtonView( editor.locale );
-			const files = {
-				0: createNativeFileMock(),
-				length: 1
-			};
-
-			fileDialogButton.fire( 'done', files );
-			sinon.assert.calledOnce( executeStub );
-			expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
-			expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( [ files[ 0 ] ] );
-		} );
-
-		describe( 'dropdown action button', () => {
-			it( 'should be an instance of FileDialogButtonView', () => {
-				const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-
-				expect( dropdown.buttonView.actionView ).to.be.instanceOf( FileDialogButtonView );
-			} );
-		} );
-
-		describe( 'dropdown panel buttons', () => {
-			it( 'should have "Update" label on submit button when URL input is already filled', () => {
-				const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-				const viewDocument = editor.editing.view.document;
-
-				editor.setData( '<figure><img src="/assets/sample.png" /></figure>' );
-
-				editor.editing.view.change( writer => {
-					writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
-				} );
-
-				const img = viewDocument.selection.getSelectedElement();
-
-				const data = fakeEventData();
-				const eventInfo = new EventInfo( img, 'click' );
-				const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
-
-				viewDocument.fire( 'click', domEventDataMock );
-
-				dropdown.isOpen = true;
-
-				const inputValue = dropdown.panelView.children.first.imageURLInputValue;
-
-				expect( dropdown.isOpen ).to.be.true;
-				expect( inputValue ).to.equal( '/assets/sample.png' );
-				expect( dropdown.panelView.children.first.insertButtonView.label ).to.equal( 'Update' );
-			} );
-
-			it( 'should have "Insert" label on submit button on uploading a new image', () => {
-				const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-				const viewDocument = editor.editing.view.document;
-
-				editor.setData( '<p>test</p>' );
-
-				editor.editing.view.change( writer => {
-					writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'end' );
-				} );
-
-				const el = viewDocument.selection.getSelectedElement();
-
-				const data = fakeEventData();
-				const eventInfo = new EventInfo( el, 'click' );
-				const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
-
-				viewDocument.fire( 'click', domEventDataMock );
-
-				dropdown.isOpen = true;
-
-				const inputValue = dropdown.panelView.children.first.imageURLInputValue;
-
-				expect( dropdown.isOpen ).to.be.true;
-				expect( inputValue ).to.equal( '' );
-				expect( dropdown.panelView.children.first.insertButtonView.label ).to.equal( 'Insert' );
-			} );
-		} );
-
-		describe( 'dropdown panel integrations', () => {
-			describe( 'insert image via URL form', () => {
-				it( 'should have "Insert image via URL" label on inserting new image', () => {
-					const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-					const viewDocument = editor.editing.view.document;
-
-					editor.setData( '<p>test</p>' );
-
-					editor.editing.view.change( writer => {
-						writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'end' );
-					} );
-
-					const el = viewDocument.selection.getSelectedElement();
-
-					const data = fakeEventData();
-					const eventInfo = new EventInfo( el, 'click' );
-					const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
-
-					viewDocument.fire( 'click', domEventDataMock );
-
-					dropdown.isOpen = true;
-
-					const inputValue = dropdown.panelView.children.first.imageURLInputValue;
-
-					const insertImageViaUrlForm = dropdown.panelView.children.first.getIntegration( 'insertImageViaUrl' );
-
-					expect( dropdown.isOpen ).to.be.true;
-					expect( inputValue ).to.equal( '' );
-					expect( insertImageViaUrlForm.label ).to.equal( 'Insert image via URL' );
-				} );
-
-				it( 'should have "Update image URL" label on updating the image source URL', () => {
-					const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-					const viewDocument = editor.editing.view.document;
-
-					editor.setData( '<figure><img src="/assets/sample.png" /></figure>' );
-
-					editor.editing.view.change( writer => {
-						writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
-					} );
-
-					const el = viewDocument.selection.getSelectedElement();
-
-					const data = fakeEventData();
-					const eventInfo = new EventInfo( el, 'click' );
-					const domEventDataMock = new DomEventData( viewDocument, eventInfo, data );
-
-					viewDocument.fire( 'click', domEventDataMock );
-
-					dropdown.isOpen = true;
-
-					const inputValue = dropdown.panelView.children.first.imageURLInputValue;
-					const insertImageViaUrlForm = dropdown.panelView.children.first.getIntegration( 'insertImageViaUrl' );
-
-					expect( dropdown.isOpen ).to.be.true;
-					expect( inputValue ).to.equal( '/assets/sample.png' );
-					expect( insertImageViaUrlForm.label ).to.equal( 'Update image URL' );
-				} );
-			} );
-		} );
-
-		it( 'should remove all attributes from model except "src" when updating the image source URL', () => {
-			const viewDocument = editor.editing.view.document;
-			const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-			const insertButtonView = dropdown.panelView.children.first.insertButtonView;
-			const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
-			const submitSpy = sinon.spy();
-
-			dropdown.isOpen = true;
-
-			editor.setData( '<figure><img src="image-url-800w.jpg"' +
-			'srcset="image-url-480w.jpg 480w,image-url-800w.jpg 800w"' +
-			'sizes="(max-width: 600px) 480px,800px"' +
-			'alt="test-image"></figure>' );
-
-			editor.editing.view.change( writer => {
-				writer.setSelection( viewDocument.getRoot().getChild( 0 ), 'on' );
-			} );
-
-			const selectedElement = editor.model.document.selection.getSelectedElement();
-
-			expect( selectedElement.getAttribute( 'src' ) ).to.equal( 'image-url-800w.jpg' );
-			expect( selectedElement.hasAttribute( 'srcset' ) ).to.be.true;
-
-			dropdown.panelView.children.first.imageURLInputValue = '/assets/sample3.png';
-
-			dropdown.on( 'submit', submitSpy );
-
-			insertButtonView.fire( 'execute' );
-
-			sinon.assert.notCalled( commandSpy );
-			sinon.assert.calledOnce( submitSpy );
-			expect( dropdown.isOpen ).to.be.false;
-			expect( selectedElement.getAttribute( 'src' ) ).to.equal( '/assets/sample3.png' );
-			expect( selectedElement.hasAttribute( 'srcset' ) ).to.be.false;
-			expect( selectedElement.hasAttribute( 'sizes' ) ).to.be.false;
-		} );
-
-		describe( 'events', () => {
-			it( 'should emit "submit" event when clicking on submit button', () => {
-				const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-				const insertButtonView = dropdown.panelView.children.first.insertButtonView;
-				const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
-				const submitSpy = sinon.spy();
-
-				dropdown.isOpen = true;
-
-				dropdown.on( 'submit', submitSpy );
-
-				insertButtonView.fire( 'execute' );
-
-				expect( dropdown.isOpen ).to.be.false;
-				sinon.assert.calledOnce( commandSpy );
-				sinon.assert.calledOnce( submitSpy );
-			} );
-
-			it( 'should emit "cancel" event when clicking on cancel button', () => {
-				const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-				const cancelButtonView = dropdown.panelView.children.first.cancelButtonView;
-				const commandSpy = sinon.spy( editor.commands.get( 'imageInsert' ), 'execute' );
-				const cancelSpy = sinon.spy();
-
-				dropdown.isOpen = true;
+		);
+	} );
 
 
-				dropdown.on( 'cancel', cancelSpy );
+	it( 'should not execute imageUpload if the file is not an image', () => {
+		const executeStub = sinon.stub( editor, 'execute' );
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const file = {
+			type: 'media/mp3',
+			size: 1024
+		};
 
 
-				cancelButtonView.fire( 'execute' );
+		button.fire( 'done', [ file ] );
+		sinon.assert.notCalled( executeStub );
+	} );
 
 
-				expect( dropdown.isOpen ).to.be.false;
-				sinon.assert.notCalled( commandSpy );
-				sinon.assert.calledOnce( cancelSpy );
-			} );
-		} );
-
-		it( 'should inject integrations to the dropdown panel view from the config', async () => {
-			const editor = await ClassicEditor
-				.create( editorElement, {
-					plugins: [
-						CKFinder,
-						Paragraph,
-						Image,
-						ImageUploadEditing,
-						ImageUploadUI,
-						FileRepository,
-						UploadAdapterPluginMock,
-						Clipboard
-					],
-					image: {
-						upload: {
-							panel: {
-								items: [
-									'insertImageViaUrl',
-									'openCKFinder'
-								]
-							}
-						}
-					}
-				} );
-
-			const dropdown = editor.ui.componentFactory.create( 'imageUpload' );
-
-			expect( dropdown.panelView.children.first._integrations.length ).to.equal( 2 );
-			expect( dropdown.panelView.children.first._integrations.first ).to.be.instanceOf( LabeledFieldView );
-			expect( dropdown.panelView.children.first._integrations.last ).to.be.instanceOf( ButtonView );
-
-			editor.destroy();
-		} );
+	it( 'should work even if the FileList does not support iterators', () => {
+		const executeStub = sinon.stub( editor, 'execute' );
+		const button = editor.ui.componentFactory.create( 'imageUpload' );
+		const files = {
+			0: createNativeFileMock(),
+			length: 1
+		};
+
+		button.fire( 'done', files );
+		sinon.assert.calledOnce( executeStub );
+		expect( executeStub.firstCall.args[ 0 ] ).to.equal( 'imageUpload' );
+		expect( executeStub.firstCall.args[ 1 ].file ).to.deep.equal( [ files[ 0 ] ] );
 	} );
 	} );
 } );
 } );
-
-function fakeEventData() {
-	return {
-		preventDefault: sinon.spy()
-	};
-}

+ 1 - 146
packages/ckeditor5-image/tests/imageupload/utils.js

@@ -3,18 +3,7 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
  */
 
 
-/* globals document */
-
-import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
-
-import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
-import Image from '../../src/image';
-import ImageUploadUI from '../../src/imageupload/imageuploadui';
-import ImageUploadEditing from '../../src/imageupload/imageuploadediting';
-import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import Link from '@ckeditor/ckeditor5-link/src/link';
-import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
-import { createImageTypeRegExp, prepareIntegrations, createLabeledInputView } from '../../src/imageupload/utils';
+import { createImageTypeRegExp } from '../../src/imageupload/utils';
 
 
 describe( 'Upload utils', () => {
 describe( 'Upload utils', () => {
 	describe( 'createImageTypeRegExp()', () => {
 	describe( 'createImageTypeRegExp()', () => {
@@ -42,138 +31,4 @@ describe( 'Upload utils', () => {
 			expect( createImageTypeRegExp( [ 'png' ] ).test( 'svg+xml' ) ).to.be.false;
 			expect( createImageTypeRegExp( [ 'png' ] ).test( 'svg+xml' ) ).to.be.false;
 		} );
 		} );
 	} );
 	} );
-
-	describe( 'prepareIntegrations()', () => {
-		it( 'should return "insetImageViaUrl" and "openCKFinder" integrations', async () => {
-			const editorElement = document.createElement( 'div' );
-			document.body.appendChild( editorElement );
-
-			const editor = await ClassicEditor
-				.create( editorElement, {
-					plugins: [
-						CKFinder,
-						Paragraph,
-						Image,
-						ImageUploadEditing,
-						ImageUploadUI
-					],
-					image: {
-						upload: {
-							panel: {
-								items: [
-									'insertImageViaUrl',
-									'openCKFinder'
-								]
-							}
-						}
-					}
-				} );
-
-			const openCKFinderExtendedView = Object.values( prepareIntegrations( editor ) )[ 1 ];
-
-			expect( openCKFinderExtendedView.class ).contains( 'ck-image-upload__ck-finder-button' );
-			expect( openCKFinderExtendedView.label ).to.equal( 'Insert image or file' );
-			expect( openCKFinderExtendedView.withText ).to.be.true;
-
-			editor.destroy();
-			editorElement.remove();
-		} );
-
-		it( 'should return only "insertImageViaUrl" integration and throw warning' +
-			'for "image-upload-integrations-invalid-view" error', async () => {
-			const editorElement = document.createElement( 'div' );
-			document.body.appendChild( editorElement );
-
-			const editor = await ClassicEditor
-				.create( editorElement, {
-					plugins: [
-						Paragraph,
-						Image,
-						ImageUploadEditing,
-						ImageUploadUI
-					],
-					image: {
-						upload: {
-							panel: {
-								items: [
-									'insertImageViaUrl',
-									'openCKFinder'
-								]
-							}
-						}
-					}
-				} );
-
-			expect( Object.values( prepareIntegrations( editor ) ).length ).to.equal( 1 );
-
-			editor.destroy();
-			editorElement.remove();
-		} );
-
-		it( 'should return only "link" integration', async () => {
-			const editorElement = document.createElement( 'div' );
-			document.body.appendChild( editorElement );
-
-			const editor = await ClassicEditor
-				.create( editorElement, {
-					plugins: [
-						Paragraph,
-						Link,
-						Image,
-						ImageUploadEditing,
-						ImageUploadUI
-					],
-					image: {
-						upload: {
-							panel: {
-								items: [
-									'link'
-								]
-							}
-						}
-					}
-				} );
-
-			expect( Object.values( prepareIntegrations( editor ) ).length ).to.equal( 1 );
-			expect( Object.values( prepareIntegrations( editor ) )[ 0 ].label ).to.equal( 'Link' );
-			expect( Object.values( prepareIntegrations( editor ) )[ 0 ] ).to.be.instanceOf( ButtonView );
-
-			editor.destroy();
-			editorElement.remove();
-		} );
-
-		it( 'should return "insertImageViaUrl" integration, when no integrations were configured', async () => {
-			const editorElement = document.createElement( 'div' );
-			document.body.appendChild( editorElement );
-
-			const editor = await ClassicEditor
-				.create( editorElement, {
-					plugins: [
-						Paragraph,
-						Image,
-						ImageUploadEditing,
-						ImageUploadUI
-					]
-				} );
-
-			expect( Object.keys( prepareIntegrations( editor ) ).length ).to.equal( 1 );
-
-			editor.destroy();
-			editorElement.remove();
-		} );
-	} );
-
-	describe( 'createLabeledInputView()', () => {
-		describe( 'image URL input view', () => {
-			it( 'should have placeholder', () => {
-				const view = createLabeledInputView( { t: val => val } );
-				expect( view.fieldView.placeholder ).to.equal( 'https://example.com/src/image.png' );
-			} );
-
-			it( 'should have info text', () => {
-				const view = createLabeledInputView( { t: val => val } );
-				expect( view.infoText ).to.match( /^Paste the image source URL/ );
-			} );
-		} );
-	} );
 } );
 } );

+ 1 - 1
packages/ckeditor5-image/tests/manual/imageuploadviaurl.html → packages/ckeditor5-image/tests/manual/imageinsertviaurl.html

@@ -10,7 +10,7 @@
 	}
 	}
 </style>
 </style>
 
 
-<div id="editor2">
+<div id="editor">
 	<h2>Image upload via URL with CKFinder integration</h2>
 	<h2>Image upload via URL with CKFinder integration</h2>
 	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.</p>
 	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.</p>
 </div>
 </div>

+ 4 - 13
packages/ckeditor5-image/tests/manual/imageuploadviaurl.js → packages/ckeditor5-image/tests/manual/imageinsertviaurl.js

@@ -7,14 +7,12 @@
 
 
 import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
 import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
 import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
 import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
-import ImageUpload from '../../src/imageupload';
+import ImageInsert from '../../src/imageinsert';
 import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
 import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder';
 
 
-import { UploadAdapterMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
-
 ClassicEditor
 ClassicEditor
-	.create( document.querySelector( '#editor2' ), {
-		plugins: [ ArticlePluginSet, ImageUpload, CKFinder ],
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ ArticlePluginSet, ImageInsert, CKFinder ],
 		toolbar: [
 		toolbar: [
 			'heading',
 			'heading',
 			'|',
 			'|',
@@ -24,7 +22,7 @@ ClassicEditor
 			'bulletedList',
 			'bulletedList',
 			'numberedList',
 			'numberedList',
 			'blockQuote',
 			'blockQuote',
-			'imageUpload',
+			'imageInsert',
 			'insertTable',
 			'insertTable',
 			'mediaEmbed',
 			'mediaEmbed',
 			'undo',
 			'undo',
@@ -48,13 +46,6 @@ ClassicEditor
 	} )
 	} )
 	.then( editor => {
 	.then( editor => {
 		window.editor = editor;
 		window.editor = editor;
-
-		// Register fake adapter.
-		editor.plugins.get( 'FileRepository' ).createUploadAdapter = loader => {
-			const adapterMock = new UploadAdapterMock( loader );
-
-			return adapterMock;
-		};
 	} )
 	} )
 	.catch( err => {
 	.catch( err => {
 		console.error( err.stack );
 		console.error( err.stack );

+ 18 - 0
packages/ckeditor5-image/tests/manual/imageinsertviaurl.md

@@ -0,0 +1,18 @@
+## Image insert via URL
+
+1. Click on the arrow button in `imageInsert` plugin to reveal the image insert panel.
+1. Paste the URL to the input (eg: `https://ckeditor.com/docs/ckeditor5/latest/assets/img/malta.jpg`).
+1. Click `Insert` button.
+
+## Image replace via URL
+
+1. Click on the image in the editor.
+1. Click on the arrow button in `imageInsert` plugin to reveal the image insert panel.
+1. Edit the value of the input.
+1. Click `Update` button.
+
+## Image insert via integrations
+
+1. In the **Editor 2** click on the arrow button in `imageInsert` plugin to reveal the image insert panel.
+1. Click on the **CKFinder** button.
+1. Choose image and confirm.

+ 0 - 18
packages/ckeditor5-image/tests/manual/imageuploadviaurl.md

@@ -1,18 +0,0 @@
-## Image upload via URL
-
-1. Click on the arrow button in `imageUpload` plugin to reveal the image upload panel.
-1. Paste the URL to the input (eg: `https://ckeditor.com/docs/ckeditor5/latest/assets/img/malta.jpg`).
-1. Click `Insert` button.
-
-## Image replace via URL
-
-1. Click on the image in the editor.
-1. Click on the arrow button in `imageUpload` plugin to reveal the image upload panel.
-1. Edit the value of the input.
-1. Click `Update` button.
-
-## Image upload via integrations
-
-1. In the **Editor 2** click on the arrow button in `imageUpload` plugin to reveal the image upload panel.
-1. Click on the **CKFinder** button.
-1. Choose image and confirm.

+ 9 - 2
packages/ckeditor5-image/theme/imageupload.css → packages/ckeditor5-image/theme/imageinsert.css

@@ -3,14 +3,21 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
  */
 
 
-.ck.ck-image-upload__panel {
+.ck.ck-image-insert__panel {
 	padding: var(--ck-spacing-standard);
 	padding: var(--ck-spacing-standard);
 }
 }
 
 
-.ck.ck-image-upload__ck-finder-button {
+.ck.ck-image-insert__ck-finder-button {
 	display: block;
 	display: block;
 	width: 100%;
 	width: 100%;
 	margin: var(--ck-spacing-standard) auto;
 	margin: var(--ck-spacing-standard) auto;
 	border: 1px solid hsl(0, 0%, 80%);
 	border: 1px solid hsl(0, 0%, 80%);
 	border-radius: var(--ck-border-radius);
 	border-radius: var(--ck-border-radius);
 }
 }
+
+/* https://github.com/ckeditor/ckeditor5/issues/7986 */
+.ck.ck-splitbutton > .ck-file-dialog-button.ck-button {
+	padding: 0;
+	margin: 0;
+	border: none;
+}

+ 1 - 1
packages/ckeditor5-image/theme/imageuploadformrowview.css → packages/ckeditor5-image/theme/imageinsertformrowview.css

@@ -14,7 +14,7 @@
 		flex-grow: 1;
 		flex-grow: 1;
 	}
 	}
 
 
-	&.ck-image-upload-form__action-row {
+	&.ck-image-insert-form__action-row {
 		margin-top: var(--ck-spacing-standard);
 		margin-top: var(--ck-spacing-standard);
 
 
 		& .ck-button-save,
 		& .ck-button-save,