Преглед изворни кода

Merge pull request #7654 from ckeditor/i/7579

Other (image): Allow to configure `ImageResize` in a more granular way. For example, by combining `ImageResizeEditing` with `ImageResizeHandles` or `ImageResizeButtons` to resize image by handles or by image toolbar UI components (dropdown or standalone buttons) respectively. Closes #7579.

Docs (image): Add a new section to feature documentation for the `Image` plugin about disabling image resize handles.

Internal (image): Separate `ImageResizeHandles` plugin from the `ImageResizeEditing`.

Tests (image): Refactor unit tests for `ImageResizeHandles` and `ImageResizeEditing`.
Maciej пре 5 година
родитељ
комит
3396d4e4c0

+ 1 - 1
packages/ckeditor5-image/docs/_snippets/features/image-resizeuidropdown.html → packages/ckeditor5-image/docs/_snippets/features/image-resize-buttons-dropdown.html

@@ -1,4 +1,4 @@
-<div id="snippet-image-resizeui-dropdown">
+<div id="snippet-image-resize-buttons-dropdown">
 	<h3>Resize me using the dropdown!</h3>
 	<h3>Resize me using the dropdown!</h3>
 
 
 	<figure class="image">
 	<figure class="image">

+ 1 - 1
packages/ckeditor5-image/docs/_snippets/features/image-resizeuidropdown.js → packages/ckeditor5-image/docs/_snippets/features/image-resize-buttons-dropdown.js

@@ -8,7 +8,7 @@
 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';
 
 
 ClassicEditor
 ClassicEditor
-	.create( document.querySelector( '#snippet-image-resizeui-dropdown' ), {
+	.create( document.querySelector( '#snippet-image-resize-buttons-dropdown' ), {
 		removePlugins: [ 'LinkImage' ],
 		removePlugins: [ 'LinkImage' ],
 		toolbar: {
 		toolbar: {
 			viewportTopOffset: window.getViewportTopOffsetConfig()
 			viewportTopOffset: window.getViewportTopOffsetConfig()

+ 1 - 1
packages/ckeditor5-image/docs/_snippets/features/image-resizeui.html → packages/ckeditor5-image/docs/_snippets/features/image-resize-buttons.html

@@ -1,4 +1,4 @@
-<div id="snippet-image-resizeui">
+<div id="snippet-image-resize-buttons">
 	<h3>Resize me using image toolbar buttons!</h3>
 	<h3>Resize me using image toolbar buttons!</h3>
 
 
 	<figure class="image">
 	<figure class="image">

+ 1 - 1
packages/ckeditor5-image/docs/_snippets/features/image-resizeui.js → packages/ckeditor5-image/docs/_snippets/features/image-resize-buttons.js

@@ -8,7 +8,7 @@
 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';
 
 
 ClassicEditor
 ClassicEditor
-	.create( document.querySelector( '#snippet-image-resizeui' ), {
+	.create( document.querySelector( '#snippet-image-resize-buttons' ), {
 		removePlugins: [ 'LinkImage' ],
 		removePlugins: [ 'LinkImage' ],
 		toolbar: {
 		toolbar: {
 			viewportTopOffset: window.getViewportTopOffsetConfig()
 			viewportTopOffset: window.getViewportTopOffsetConfig()

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

@@ -226,7 +226,7 @@ The plugin also gives you an ability to change the size of the image through the
 
 
 ### Methods to resize images
 ### Methods to resize images
 
 
-The editor offers three ways to resize images. One of them (resize handles) is
+The editor offers different ways to resize images either by using resize handles or by using dedicated UI components.
 
 
 #### Using handles
 #### Using handles
 
 
@@ -234,6 +234,41 @@ In this case, the user is able to resize images via dragging square handles disp
 
 
 {@snippet features/image-resize}
 {@snippet features/image-resize}
 
 
+You can configure the editor for resizing images by handles in two different ways:
+
+- By installing the {@link module:image/imageresize~ImageResize} plugin, which contains **all** needed features (`ImageResizeEditing`, `ImageResizeHandles`, `ImageResizeButtons`).
+
+```js
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ Image, ImageResize, ... ],
+		...
+	} )
+	.then( ... )
+	.catch( ... );
+```
+
+- Or by installing the combination of {@link module:image/imageresize/imageresizeediting~ImageResizeEditing} and {@link module:image/imageresize/imageresizehandles~ImageResizeHandles} plugins.
+
+```js
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageResizeEditing from '@ckeditor/ckeditor5-image/src/imageresize/imageresizeediting';
+import ImageResizeHandles from '@ckeditor/ckeditor5-image/src/imageresize/imageresizehandles';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ Image, ImageResizeEditing, ImageResizeHandles, ... ],
+		...
+	} )
+	.then( ... )
+	.catch( ... );
+```
+
+Both ways enable resize handles by default.
+
 #### Using the dropdown
 #### Using the dropdown
 
 
 In this case, the user is able to choose from a set of predefined options. These options can be displayed in the image toolbar in form of a dropdown.
 In this case, the user is able to choose from a set of predefined options. These options can be displayed in the image toolbar in form of a dropdown.
@@ -263,7 +298,7 @@ const imageConfiguration = {
 }
 }
 ```
 ```
 
 
-{@snippet features/image-resizeuidropdown}
+{@snippet features/image-resize-buttons-dropdown}
 
 
 #### Using standalone buttons
 #### Using standalone buttons
 
 
@@ -299,7 +334,50 @@ const imageConfiguration = {
 }
 }
 ```
 ```
 
 
-{@snippet features/image-resizeui}
+{@snippet features/image-resize-buttons}
+
+### Disabling image resize handles
+
+If, for some reason, you want to configure the editor in such a way that images can be resized only by buttons you can do so by omitting the {@link module:image/imageresize/imageresizehandles~ImageResizeHandles `ImageResizeHandles`} plugin. As a result, plugins setup should look like this: `plugins: [ 'ImageResizeEditing', 'ImageResizeButtons', ... ]` as opposed to `plugins: [ 'ImageResize', ... ]`. It will enable resizing image feature only by means of the chosen UI ([dropdown](#using-the-dropdown) or [standalone buttons](#using-standalone-buttons)) in the image toolbar.
+
+```js
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar';
+import ImageResizeEditing from '@ckeditor/ckeditor5-image/src/imageresize/imageresizeedititing';
+import ImageResizeButtons from '@ckeditor/ckeditor5-image/src/imageresize/imageresizebuttons';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ Image, ImageResizeEditing, ImageResizeButtons, ImageToolbar, ... ],
+		image: {
+			resizeOptions: [
+			{
+				name: 'imageResize:original',
+				value: null,
+				icon: 'original'
+			},
+			{
+				name: 'imageResize:50',
+				value: '50',
+				icon: 'medium'
+			},
+			{
+				name: 'imageResize:75',
+				value: '75',
+				icon: 'large'
+			}
+		],
+		toolbar: [
+			// ...,
+			'imageResize:50',
+			'imageResize:75',
+			'imageResize:original',
+		]
+		}
+	} )
+	.then( ... )
+	.catch( ... );
+```
 
 
 ### Enabling image resizing
 ### Enabling image resizing
 
 

+ 6 - 5
packages/ckeditor5-image/src/imageresize.js

@@ -8,8 +8,9 @@
  */
  */
 
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import ImageResizeUI from './imageresize/imageresizeui';
+import ImageResizeButtons from './imageresize/imageresizebuttons';
 import ImageResizeEditing from './imageresize/imageresizeediting';
 import ImageResizeEditing from './imageresize/imageresizeediting';
+import ImageResizeHandles from './imageresize/imageresizehandles';
 
 
 import '../theme/imageresize.css';
 import '../theme/imageresize.css';
 
 
@@ -25,7 +26,7 @@ export default class ImageResize extends Plugin {
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
 	static get requires() {
 	static get requires() {
-		return [ ImageResizeEditing, ImageResizeUI ];
+		return [ ImageResizeEditing, ImageResizeHandles, ImageResizeButtons ];
 	}
 	}
 
 
 	/**
 	/**
@@ -127,10 +128,10 @@ export default class ImageResize extends Plugin {
  *
  *
  * **Resizing images using individual buttons**
  * **Resizing images using individual buttons**
  *
  *
- * If you want to have separate buttons for {@link module:image/imageresize/imageresizeui~ImageResizeOption each option},
+ * If you want to have separate buttons for {@link module:image/imageresize/imageresizebuttons~ImageResizeOption each option},
  * pass their names to the {@link module:image/image~ImageConfig#toolbar `config.image.toolbar`} instead. Please keep in mind
  * pass their names to the {@link module:image/image~ImageConfig#toolbar `config.image.toolbar`} instead. Please keep in mind
  * that this time **you must define the additional
  * that this time **you must define the additional
- * {@link module:image/imageresize/imageresizeui~ImageResizeOption `icon` property}**:
+ * {@link module:image/imageresize/imageresizebuttons~ImageResizeOption `icon` property}**:
  *
  *
  *		ClassicEditor
  *		ClassicEditor
  *			.create( editorElement, {
  *			.create( editorElement, {
@@ -196,5 +197,5 @@ export default class ImageResize extends Plugin {
  *			.then( ... )
  *			.then( ... )
  *			.catch( ... );
  *			.catch( ... );
  *
  *
- * @member {Array.<module:image/imageresize/imageresizeui~ImageResizeOption>} module:image/image~ImageConfig#resizeOptions
+ * @member {Array.<module:image/imageresize/imageresizebuttons~ImageResizeOption>} module:image/image~ImageConfig#resizeOptions
  */
  */

+ 13 - 13
packages/ckeditor5-image/src/imageresize/imageresizeui.js → packages/ckeditor5-image/src/imageresize/imageresizebuttons.js

@@ -4,7 +4,7 @@
  */
  */
 
 
 /**
 /**
- * @module image/imageresize/imageresizeui
+ * @module image/imageresize/imageresizebuttons
  */
  */
 
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
@@ -31,13 +31,13 @@ const RESIZE_ICONS = {
 };
 };
 
 
 /**
 /**
- * The `ImageResizeUI` plugin.
+ * The `ImageResizeButtons` plugin.
  *
  *
  * It adds a possibility to resize images using the toolbar dropdown or individual buttons, depending on the plugin configuration.
  * It adds a possibility to resize images using the toolbar dropdown or individual buttons, depending on the plugin configuration.
  *
  *
  * @extends module:core/plugin~Plugin
  * @extends module:core/plugin~Plugin
  */
  */
-export default class ImageResizeUI extends Plugin {
+export default class ImageResizeButtons extends Plugin {
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
@@ -49,7 +49,7 @@ export default class ImageResizeUI extends Plugin {
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
 	static get pluginName() {
 	static get pluginName() {
-		return 'ImageResizeUI';
+		return 'ImageResizeButtons';
 	}
 	}
 
 
 	/**
 	/**
@@ -94,7 +94,7 @@ export default class ImageResizeUI extends Plugin {
 	 * A helper function that creates a standalone button component for the plugin.
 	 * A helper function that creates a standalone button component for the plugin.
 	 *
 	 *
 	 * @private
 	 * @private
-	 * @param {module:image/imageresize/imageresizeui~ImageResizeOption} resizeOption A model of resize option.
+	 * @param {module:image/imageresize/imageresizebuttons~ImageResizeOption} resizeOption A model of resize option.
 	 */
 	 */
 	_registerImageResizeButton( option ) {
 	_registerImageResizeButton( option ) {
 		const editor = this.editor;
 		const editor = this.editor;
@@ -112,13 +112,13 @@ export default class ImageResizeUI extends Plugin {
 				 * buttons, a valid `icon` token must be set for each option.
 				 * buttons, a valid `icon` token must be set for each option.
 				 *
 				 *
 				 * See all valid options described in the
 				 * See all valid options described in the
-				 * {@link module:image/imageresize/imageresizeui~ImageResizeOption plugin configuration}.
+				 * {@link module:image/imageresize/imageresizebuttons~ImageResizeOption plugin configuration}.
 				 *
 				 *
-				 * @error imageresizeui-missing-icon
-				 * @param {module:image/imageresize/imageresizeui~ImageResizeOption} option Invalid image resize option.
+				 * @error imageresizebuttons-missing-icon
+				 * @param {module:image/imageresize/imageresizebuttons~ImageResizeOption} option Invalid image resize option.
 				*/
 				*/
 				throw new CKEditorError(
 				throw new CKEditorError(
-					'imageresizeui-missing-icon: ' +
+					'imageresizebuttons-missing-icon: ' +
 					'The resize option "' + name + '" misses the "icon" property ' +
 					'The resize option "' + name + '" misses the "icon" property ' +
 					'or the property value doesn\'t match any of available icons.',
 					'or the property value doesn\'t match any of available icons.',
 					editor,
 					editor,
@@ -151,7 +151,7 @@ export default class ImageResizeUI extends Plugin {
 	 * the editor configuration.
 	 * the editor configuration.
 	 *
 	 *
 	 * @private
 	 * @private
-	 * @param {Array.<module:image/imageresize/imageresizeui~ImageResizeOption>} options An array of configured options.
+	 * @param {Array.<module:image/imageresize/imageresizebuttons~ImageResizeOption>} options An array of configured options.
 	 */
 	 */
 	_registerImageResizeDropdown( options ) {
 	_registerImageResizeDropdown( options ) {
 		const editor = this.editor;
 		const editor = this.editor;
@@ -202,7 +202,7 @@ export default class ImageResizeUI extends Plugin {
 	 * A helper function for creating an option label value string.
 	 * A helper function for creating an option label value string.
 	 *
 	 *
 	 * @private
 	 * @private
-	 * @param {module:image/imageresize/imageresizeui~ImageResizeOption} option A resize option object.
+	 * @param {module:image/imageresize/imageresizebuttons~ImageResizeOption} option A resize option object.
 	 * @param {Boolean} [forTooltip] An optional flag for creating a tooltip label.
 	 * @param {Boolean} [forTooltip] An optional flag for creating a tooltip label.
 	 * @returns {String} A user-defined label, a label combined from the value and resize unit or the default label
 	 * @returns {String} A user-defined label, a label combined from the value and resize unit or the default label
 	 * for reset options (`Original`).
 	 * for reset options (`Original`).
@@ -231,7 +231,7 @@ export default class ImageResizeUI extends Plugin {
 	 * A helper function that parses resize options and returns list item definitions ready for use in a dropdown.
 	 * A helper function that parses resize options and returns list item definitions ready for use in a dropdown.
 	 *
 	 *
 	 * @private
 	 * @private
-	 * @param {Array.<module:image/imageresize/imageresizeui~ImageResizeOption>} options The resize options.
+	 * @param {Array.<module:image/imageresize/imageresizebuttons~ImageResizeOption>} options The resize options.
 	 * @param {module:image/imageresize/imageresizecommand~ImageResizeCommand} command A resize image command.
 	 * @param {module:image/imageresize/imageresizecommand~ImageResizeCommand} command A resize image command.
 	 * @returns {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>} Dropdown item definitions.
 	 * @returns {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>} Dropdown item definitions.
 	 */
 	 */
@@ -274,7 +274,7 @@ function getIsOnButtonCallback( value ) {
 /**
 /**
  * An image resize option used in the {@link module:image/image~ImageConfig#resizeOptions image resize configuration}.
  * An image resize option used in the {@link module:image/image~ImageConfig#resizeOptions image resize configuration}.
  *
  *
- * @typedef {Object} module:image/imageresize/imageresizeui~ImageResizeOption
+ * @typedef {Object} module:image/imageresize/imageresizebuttons~ImageResizeOption
  * @property {String} name A name of the UI component that changes the image size.
  * @property {String} name A name of the UI component that changes the image size.
  * * If you configure the feature using individual resize buttons, you can refer to this name in the
  * * If you configure the feature using individual resize buttons, you can refer to this name in the
  * {@link module:image/image~ImageConfig#toolbar image toolbar configuration}.
  * {@link module:image/image~ImageConfig#toolbar image toolbar configuration}.

+ 5 - 53
packages/ckeditor5-image/src/imageresize/imageresizeediting.js

@@ -8,25 +8,17 @@
  */
  */
 
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import WidgetResize from '@ckeditor/ckeditor5-widget/src/widgetresize';
 import ImageResizeCommand from './imageresizecommand';
 import ImageResizeCommand from './imageresizecommand';
 
 
 /**
 /**
- * The image resize feature.
+ * The image resize editing feature.
  *
  *
  * It adds a possibility to resize each image using handles or manually by
  * It adds a possibility to resize each image using handles or manually by
- * {@link module:image/imageresize/imageresizeui~ImageResizeUI} buttons.
+ * {@link module:image/imageresize/imageresizebuttons~ImageResizeButtons} buttons.
  *
  *
  * @extends module:core/plugin~Plugin
  * @extends module:core/plugin~Plugin
  */
  */
 export default class ImageResizeEditing extends Plugin {
 export default class ImageResizeEditing extends Plugin {
-	/**
-	 * @inheritDoc
-	 */
-	static get requires() {
-		return [ WidgetResize ];
-	}
-
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
@@ -45,55 +37,15 @@ export default class ImageResizeEditing extends Plugin {
 		this._registerConverters();
 		this._registerConverters();
 
 
 		editor.commands.add( 'imageResize', command );
 		editor.commands.add( 'imageResize', command );
-
-		editor.editing.downcastDispatcher.on( 'insert:image', ( evt, data, conversionApi ) => {
-			const widget = conversionApi.mapper.toViewElement( data.item );
-
-			const resizer = editor.plugins
-				.get( WidgetResize )
-				.attachTo( {
-					unit: editor.config.get( 'image.resizeUnit' ) || '%',
-
-					modelElement: data.item,
-					viewElement: widget,
-					editor,
-
-					getHandleHost( domWidgetElement ) {
-						return domWidgetElement.querySelector( 'img' );
-					},
-					getResizeHost( domWidgetElement ) {
-						return domWidgetElement;
-					},
-					// TODO consider other positions.
-					isCentered() {
-						const imageStyle = data.item.getAttribute( 'imageStyle' );
-
-						return !imageStyle || imageStyle == 'full' || imageStyle == 'alignCenter';
-					},
-
-					onCommit( newValue ) {
-						editor.execute( 'imageResize', { width: newValue } );
-					}
-				} );
-
-			resizer.on( 'updateSize', () => {
-				if ( !widget.hasClass( 'image_resized' ) ) {
-					editor.editing.view.change( writer => {
-						writer.addClass( 'image_resized', widget );
-					} );
-				}
-			} );
-
-			resizer.bind( 'isEnabled' ).to( command );
-		}, { priority: 'low' } );
 	}
 	}
 
 
 	/**
 	/**
 	 * @private
 	 * @private
 	 */
 	 */
 	_registerSchema() {
 	_registerSchema() {
-		this.editor.model.schema.extend( 'image', {
-			allowAttributes: 'width'
+		this.editor.model.schema.extend( 'image', { allowAttributes: 'width' } );
+		this.editor.model.schema.setAttributeProperties( 'width', {
+			isFormatting: true
 		} );
 		} );
 	}
 	}
 
 

+ 86 - 0
packages/ckeditor5-image/src/imageresize/imageresizehandles.js

@@ -0,0 +1,86 @@
+/**
+ * @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/imageresize/imageresizehandles
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import WidgetResize from '@ckeditor/ckeditor5-widget/src/widgetresize';
+
+/**
+ * The image resize by handles feature.
+ *
+ * It adds a possibility to resize each image using handles or manually by
+ * {@link module:image/imageresize/imageresizebuttons~ImageResizeButtons} buttons.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class ImageResizeHandles extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ WidgetResize ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'ImageResizeHandles';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const command = editor.commands.get( 'imageResize' );
+
+		this.bind( 'isEnabled' ).to( command );
+
+		editor.editing.downcastDispatcher.on( 'insert:image', ( evt, data, conversionApi ) => {
+			const widget = conversionApi.mapper.toViewElement( data.item );
+
+			const resizer = editor.plugins
+				.get( WidgetResize )
+				.attachTo( {
+					unit: editor.config.get( 'image.resizeUnit' ) || '%',
+
+					modelElement: data.item,
+					viewElement: widget,
+					editor,
+
+					getHandleHost( domWidgetElement ) {
+						return domWidgetElement.querySelector( 'img' );
+					},
+					getResizeHost( domWidgetElement ) {
+						return domWidgetElement;
+					},
+					// TODO consider other positions.
+					isCentered() {
+						const imageStyle = data.item.getAttribute( 'imageStyle' );
+
+						return !imageStyle || imageStyle == 'full' || imageStyle == 'alignCenter';
+					},
+
+					onCommit( newValue ) {
+						editor.execute( 'imageResize', { width: newValue } );
+					}
+				} );
+
+			resizer.on( 'updateSize', () => {
+				if ( !widget.hasClass( 'image_resized' ) ) {
+					editor.editing.view.change( writer => {
+						writer.addClass( 'image_resized', widget );
+					} );
+				}
+			} );
+
+			resizer.bind( 'isEnabled' ).to( this );
+		}, { priority: 'low' } );
+	}
+}

+ 4 - 3
packages/ckeditor5-image/tests/imageresize/imageresize.js

@@ -4,12 +4,13 @@
  */
  */
 
 
 import ImageResize from '../../src/imageresize';
 import ImageResize from '../../src/imageresize';
-import ImageResizeUI from '../../src/imageresize/imageresizeui';
+import ImageResizeButtons from '../../src/imageresize/imageresizebuttons';
 import ImageResizeEditing from '../../src/imageresize/imageresizeediting';
 import ImageResizeEditing from '../../src/imageresize/imageresizeediting';
+import ImageResizeHandles from '../../src/imageresize/imageresizehandles';
 
 
 describe( 'ImageResize', () => {
 describe( 'ImageResize', () => {
-	it( 'should require "ImageResizeEditing" and "ImageResizeUI"', () => {
-		expect( ImageResize.requires ).to.deep.equal( [ ImageResizeEditing, ImageResizeUI ] );
+	it( 'should require "ImageResizeEditing", "ImageResizeHandles", and "ImageResizeButtons"', () => {
+		expect( ImageResize.requires ).to.deep.equal( [ ImageResizeEditing, ImageResizeHandles, ImageResizeButtons ] );
 	} );
 	} );
 
 
 	it( 'should be named', () => {
 	it( 'should be named', () => {

+ 10 - 10
packages/ckeditor5-image/tests/imageresize/imageresizeui.js → packages/ckeditor5-image/tests/imageresize/imageresizebuttons.js

@@ -11,7 +11,7 @@ import Image from '../../src/image';
 import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
 import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import View from '@ckeditor/ckeditor5-ui/src/view';
-import ImageResizeUI from '../../src/imageresize/imageresizeui';
+import ImageResizeButtons from '../../src/imageresize/imageresizebuttons';
 import ImageStyle from '../../src/imagestyle';
 import ImageStyle from '../../src/imagestyle';
 import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import Table from '@ckeditor/ckeditor5-table/src/table';
 import Table from '@ckeditor/ckeditor5-table/src/table';
@@ -24,7 +24,7 @@ import iconMedium from '@ckeditor/ckeditor5-core/theme/icons/object-size-medium.
 import iconLarge from '@ckeditor/ckeditor5-core/theme/icons/object-size-large.svg';
 import iconLarge from '@ckeditor/ckeditor5-core/theme/icons/object-size-large.svg';
 import iconFull from '@ckeditor/ckeditor5-core/theme/icons/object-size-full.svg';
 import iconFull from '@ckeditor/ckeditor5-core/theme/icons/object-size-full.svg';
 
 
-describe( 'ImageResizeUI', () => {
+describe( 'ImageResizeButtons', () => {
 	let plugin, command, editor, editorElement;
 	let plugin, command, editor, editorElement;
 
 
 	const resizeOptions = [ {
 	const resizeOptions = [ {
@@ -52,14 +52,14 @@ describe( 'ImageResizeUI', () => {
 
 
 		editor = await ClassicTestEditor
 		editor = await ClassicTestEditor
 			.create( editorElement, {
 			.create( editorElement, {
-				plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeUI ],
+				plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeButtons ],
 				image: {
 				image: {
 					resizeOptions
 					resizeOptions
 				}
 				}
 			} );
 			} );
 
 
 		command = editor.commands.get( 'imageResize' );
 		command = editor.commands.get( 'imageResize' );
-		plugin = editor.plugins.get( 'ImageResizeUI' );
+		plugin = editor.plugins.get( 'ImageResizeButtons' );
 	} );
 	} );
 
 
 	afterEach( async () => {
 	afterEach( async () => {
@@ -74,7 +74,7 @@ describe( 'ImageResizeUI', () => {
 
 
 	describe( 'plugin', () => {
 	describe( 'plugin', () => {
 		it( 'should be named', () => {
 		it( 'should be named', () => {
-			expect( ImageResizeUI.pluginName ).to.equal( 'ImageResizeUI' );
+			expect( ImageResizeButtons.pluginName ).to.equal( 'ImageResizeButtons' );
 		} );
 		} );
 	} );
 	} );
 
 
@@ -100,7 +100,7 @@ describe( 'ImageResizeUI', () => {
 		it( 'should not register a dropdown or buttons if no resize options passed', async () => {
 		it( 'should not register a dropdown or buttons if no resize options passed', async () => {
 			const editor = await ClassicTestEditor
 			const editor = await ClassicTestEditor
 				.create( editorElement, {
 				.create( editorElement, {
-					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeUI ],
+					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeButtons ],
 					image: {
 					image: {
 						resizeUnit: 'px'
 						resizeUnit: 'px'
 					}
 					}
@@ -167,7 +167,7 @@ describe( 'ImageResizeUI', () => {
 		beforeEach( async () => {
 		beforeEach( async () => {
 			editor = await ClassicTestEditor
 			editor = await ClassicTestEditor
 				.create( editorElement, {
 				.create( editorElement, {
-					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeUI ],
+					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeButtons ],
 					image: {
 					image: {
 						resizeUnit: '%',
 						resizeUnit: '%',
 						resizeOptions: [ {
 						resizeOptions: [ {
@@ -194,7 +194,7 @@ describe( 'ImageResizeUI', () => {
 					}
 					}
 				} );
 				} );
 
 
-			plugin = editor.plugins.get( 'ImageResizeUI' );
+			plugin = editor.plugins.get( 'ImageResizeButtons' );
 		} );
 		} );
 
 
 		afterEach( async () => {
 		afterEach( async () => {
@@ -226,7 +226,7 @@ describe( 'ImageResizeUI', () => {
 		it( 'should be created with invisible "Resize image: 30%" label when is provided', async () => {
 		it( 'should be created with invisible "Resize image: 30%" label when is provided', async () => {
 			const editor = await ClassicTestEditor
 			const editor = await ClassicTestEditor
 				.create( editorElement, {
 				.create( editorElement, {
-					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeUI ],
+					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeButtons ],
 					image: {
 					image: {
 						resizeUnit: '%',
 						resizeUnit: '%',
 						resizeOptions: [ {
 						resizeOptions: [ {
@@ -299,7 +299,7 @@ describe( 'ImageResizeUI', () => {
 		it( 'should throw the CKEditorError if no `icon` is provided', async () => {
 		it( 'should throw the CKEditorError if no `icon` is provided', async () => {
 			const editor = await ClassicTestEditor
 			const editor = await ClassicTestEditor
 				.create( editorElement, {
 				.create( editorElement, {
-					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeUI ],
+					plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeButtons ],
 					image: {
 					image: {
 						resizeUnit: '%',
 						resizeUnit: '%',
 						resizeOptions: [ {
 						resizeOptions: [ {

+ 21 - 382
packages/ckeditor5-image/tests/imageresize/imageresizeediting.js

@@ -3,63 +3,41 @@
  * 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
  */
  */
 
 
-/* global document, window */
+/* global document */
 
 
 // ClassicTestEditor can't be used, as it doesn't handle the focus, which is needed to test resizer visual cues.
 // ClassicTestEditor can't be used, as it doesn't handle the focus, which is needed to test resizer visual cues.
 import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
 import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
 
 
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+
 import Image from '../../src/image';
 import Image from '../../src/image';
 import ImageResizeEditing from '../../src/imageresize/imageresizeediting';
 import ImageResizeEditing from '../../src/imageresize/imageresizeediting';
 import ImageResizeCommand from '../../src/imageresize/imageresizecommand';
 import ImageResizeCommand from '../../src/imageresize/imageresizecommand';
 import ImageStyle from '../../src/imagestyle';
 import ImageStyle from '../../src/imagestyle';
-import ImageToolbar from '../../src/imagetoolbar';
-import ImageTextAlternative from '../../src/imagetextalternative';
-import Undo from '@ckeditor/ckeditor5-undo/src/undo';
-import Table from '@ckeditor/ckeditor5-table/src/table';
 
 
-import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
 import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
 
-import {
-	focusEditor,
-	resizerMouseSimulator,
-	getWidgetDomParts,
-	getHandleCenterPoint
-} from '@ckeditor/ckeditor5-widget/tests/widgetresize/_utils/utils';
-
-import WidgetResize from '@ckeditor/ckeditor5-widget/src/widgetresize';
+import { focusEditor } from '@ckeditor/ckeditor5-widget/tests/widgetresize/_utils/utils';
 
 
 describe( 'ImageResizeEditing', () => {
 describe( 'ImageResizeEditing', () => {
 	// 100x50 black png image
 	// 100x50 black png image
 	const IMAGE_SRC_FIXTURE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAQAAAAAPLY1AAAAQklEQVR42u3PQREAAAgDoK1/' +
 	const IMAGE_SRC_FIXTURE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAQAAAAAPLY1AAAAQklEQVR42u3PQREAAAgDoK1/' +
 		'aM3g14MGNJMXKiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiJysRFNMgH0RpujAAAAAElFTkSuQmCC';
 		'aM3g14MGNJMXKiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiJysRFNMgH0RpujAAAAAElFTkSuQmCC';
 
 
-	let absoluteContainer, widget, editor, view, viewDocument, editorElement;
-
-	before( () => {
-		// This container is required to position editor element in a reliable element.
-		// @todo ensure whether it's required after migrating the tests.
-		absoluteContainer = document.createElement( 'div' );
-		absoluteContainer.style.top = '50px';
-		absoluteContainer.style.left = '50px';
-		absoluteContainer.style.height = '1000px';
-		absoluteContainer.style.width = '500px';
-		absoluteContainer.style.position = 'absolute';
-		document.body.appendChild( absoluteContainer );
-	} );
+	let editor, editorElement;
 
 
-	after( () => {
-		absoluteContainer.remove();
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
 	} );
 	} );
 
 
-	afterEach( () => {
+	afterEach( async () => {
 		if ( editorElement ) {
 		if ( editorElement ) {
 			editorElement.remove();
 			editorElement.remove();
 		}
 		}
 
 
 		if ( editor ) {
 		if ( editor ) {
-			return editor.destroy();
+			await editor.destroy();
 		}
 		}
 	} );
 	} );
 
 
@@ -68,7 +46,9 @@ describe( 'ImageResizeEditing', () => {
 	} );
 	} );
 
 
 	describe( 'conversion', () => {
 	describe( 'conversion', () => {
-		beforeEach( () => createEditor() );
+		beforeEach( async () => {
+			editor = await createEditor();
+		} );
 
 
 		it( 'upcasts 100px width correctly', () => {
 		it( 'upcasts 100px width correctly', () => {
 			editor.setData( `<figure class="image" style="width:100px;"><img src="${ IMAGE_SRC_FIXTURE }"></figure>` );
 			editor.setData( `<figure class="image" style="width:100px;"><img src="${ IMAGE_SRC_FIXTURE }"></figure>` );
@@ -123,7 +103,9 @@ describe( 'ImageResizeEditing', () => {
 	} );
 	} );
 
 
 	describe( 'schema', () => {
 	describe( 'schema', () => {
-		beforeEach( () => createEditor() );
+		beforeEach( async () => {
+			editor = await createEditor();
+		} );
 
 
 		it( 'allows the width attribute', () => {
 		it( 'allows the width attribute', () => {
 			expect( editor.model.schema.checkAttribute( 'image', 'width' ) ).to.be.true;
 			expect( editor.model.schema.checkAttribute( 'image', 'width' ) ).to.be.true;
@@ -135,370 +117,27 @@ describe( 'ImageResizeEditing', () => {
 	} );
 	} );
 
 
 	describe( 'command', () => {
 	describe( 'command', () => {
-		beforeEach( () => createEditor() );
-
-		it( 'defines the imageResize command', () => {
-			expect( editor.commands.get( 'imageResize' ) ).to.be.instanceOf( ImageResizeCommand );
-		} );
-
-		it( 'uses the command on commit', async () => {
-			const spy = sinon.spy( editor.commands.get( 'imageResize' ), 'execute' );
-
-			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
-			widget = viewDocument.getRoot().getChild( 1 );
-			const domParts = getWidgetDomParts( editor, widget, 'bottom-left' );
-
-			const finalPointerPosition = getHandleCenterPoint( domParts.widget, 'bottom-left' ).moveBy( 10, -10 );
-
-			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, finalPointerPosition );
-
-			expect( spy.calledOnce ).to.be.true;
-			expect( spy.args[ 0 ][ 0 ] ).to.deep.equal( { width: '80px' } );
-		} );
-
-		it( 'disables the resizer if the command is disabled', () => {
-			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
-
-			const resizer = getSelectedImageResizer( editor );
-
-			let isEnabled = false;
-
-			editor.commands.get( 'imageResize' ).on( 'set:isEnabled', evt => {
-				evt.return = isEnabled;
-				evt.stop();
-			}, { priority: 'highest' } );
-
-			editor.commands.get( 'imageResize' ).refresh();
-			expect( resizer.isEnabled ).to.be.false;
-
-			isEnabled = true;
-			editor.commands.get( 'imageResize' ).refresh();
-			expect( resizer.isEnabled ).to.be.true;
-		} );
-
-		it( 'the resizer is disabled from the beginning when the command is disabled when the image is inserted', () => {
-			setData( editor.model, '<paragraph>foo[]</paragraph>' );
-
-			editor.commands.get( 'imageResize' ).on( 'set:isEnabled', evt => {
-				evt.return = false;
-				evt.stop();
-			}, { priority: 'highest' } );
-			editor.commands.get( 'imageResize' ).refresh();
-
-			editor.model.change( writer => {
-				editor.model.insertContent( writer.createElement( 'image', { src: IMAGE_SRC_FIXTURE } ) );
-			} );
-
-			const resizer = getSelectedImageResizer( editor );
-			const resizerWrapper = editor.ui.getEditableElement().querySelector( '.ck-widget__resizer' );
-
-			expect( resizer.isEnabled ).to.be.false;
-			expect( resizerWrapper.style.display ).to.equal( 'none' );
-		} );
-	} );
-
-	it( 'uses percents by default', async () => {
-		const localEditor = await createEditor( {
-			plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeEditing ]
-		} );
-
-		const attachToSpy = sinon.spy( localEditor.plugins.get( WidgetResize ), 'attachTo' );
-
-		setData( localEditor.model, `[<image imageStyle="side" src="${ IMAGE_SRC_FIXTURE }"></image>]` );
-
-		expect( attachToSpy.args[ 0 ][ 0 ] ).to.have.a.property( 'unit', '%' );
-
-		attachToSpy.restore();
-	} );
-
-	describe( 'side image resizing', () => {
-		beforeEach( async () => {
-			await createEditor();
-
-			setData( editor.model, `<paragraph>foo</paragraph>[<image imageStyle="side" src="${ IMAGE_SRC_FIXTURE }"></image>]` );
-
-			widget = viewDocument.getRoot().getChild( 1 );
-		} );
-
-		it( 'doesn\'t flicker at the beginning of the resize', async () => {
-			// (#5189)
-			const resizerPosition = 'bottom-left';
-			const domParts = getWidgetDomParts( editor, widget, resizerPosition );
-			const initialPointerPosition = getHandleCenterPoint( domParts.widget, resizerPosition );
-			const resizeWrapperView = widget.getChild( 2 );
-
-			resizerMouseSimulator.down( editor, domParts.resizeHandle );
-
-			await wait( 40 );
-
-			resizerMouseSimulator.move( editor, domParts.resizeHandle, null, initialPointerPosition );
-
-			expect( resizeWrapperView.getStyle( 'width' ) ).to.equal( '100px' );
-
-			resizerMouseSimulator.up( editor );
-		} );
-
-		it( 'makes no change when clicking the handle without drag', () => {
-			const resizerPosition = 'bottom-left';
-			const expectedWidth = 100;
-			const domParts = getWidgetDomParts( editor, widget, resizerPosition );
-
-			resizerMouseSimulator.down( editor, domParts.resizeHandle );
-
-			expect( getDomWidth( domParts.widget ), 'DOM width check' ).to.be.closeTo( expectedWidth, 2 );
-
-			resizerMouseSimulator.up( editor );
-
-			const modelItem = editor.model.document.getRoot().getChild( 1 );
-
-			expect( modelItem.getAttribute( 'width' ), 'model width attribute' ).to.be.undefined;
-		} );
-	} );
-
-	describe( 'undo integration', () => {
-		beforeEach( async () => {
-			await createEditor();
-
-			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
-
-			widget = viewDocument.getRoot().getChild( 1 );
-		} );
-
-		it( 'has correct border size after undo', async () => {
-			const domParts = getWidgetDomParts( editor, widget, 'bottom-left' );
-			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-left' );
-			const finalPointerPosition = initialPosition.clone().moveBy( 0, 10 );
-			const plugin = editor.plugins.get( 'WidgetResize' );
-
-			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
-				from: initialPosition,
-				to: finalPointerPosition
-			} );
-
-			expect( '120px' ).to.equal( domParts.widget.style.width );
-
-			editor.commands.get( 'undo' ).execute();
-
-			// Toggle _visibleResizer to force synchronous redraw. Otherwise you'd need to wait ~200ms for
-			// throttled redraw to take place, making tests slower.
-			const visibleResizer = plugin._visibleResizer;
-			plugin._visibleResizer = null;
-			plugin._visibleResizer = visibleResizer;
-
-			const resizerWrapper = document.querySelector( '.ck-widget__resizer' );
-			const shadowBoundingRect = resizerWrapper.getBoundingClientRect();
-
-			expect( shadowBoundingRect.width ).to.equal( 100 );
-			expect( shadowBoundingRect.height ).to.equal( 50 );
-		} );
-	} );
-
-	describe( 'table integration', () => {
-		beforeEach( () => createEditor() );
-
-		it( 'works when resizing in a table', () => {
-			setData( editor.model,
-				'<table>' +
-					`<tableRow><tableCell>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]</tableCell></tableRow>` +
-				'</table>'
-			);
-
-			widget = viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 ).getChild( 0 );
-			const model = editor.model.document.getRoot().getChild( 0 ).getChild( 0 ).getChild( 0 ).getChild( 0 );
-
-			const domParts = getWidgetDomParts( editor, widget, 'bottom-right' );
-			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-right' );
-			const finalPointerPosition = initialPosition.clone().moveBy( -40, -20 );
-
-			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
-				from: initialPosition,
-				to: finalPointerPosition
-			} );
-
-			expect( model.getAttribute( 'width' ) ).to.equal( '60px' );
-		} );
-	} );
-
-	describe( 'srcset integration', () => {
-		// The image is 96x96 pixels.
-		const imageBaseUrl = '/assets/sample.png';
-		let model;
-		let images = [];
-
-		before( async () => {
-			images = await Promise.all( [
-				preloadImage( imageBaseUrl ),
-				preloadImage( imageBaseUrl + '?a' ),
-				preloadImage( imageBaseUrl + '?b' ),
-				preloadImage( imageBaseUrl + '?c' )
-			] );
-		} );
-
-		after( () => {
-			for ( const image of images ) {
-				image.remove();
-			}
-		} );
-
-		beforeEach( async () => {
-			await createEditor();
-
-			editor.setData(
-				`<figure class="image">
-					<img src="${ imageBaseUrl }"
-						srcset="${ imageBaseUrl }?a 110w,
-							${ imageBaseUrl }?b 440w,
-							${ imageBaseUrl }?c 1025w"
-						sizes="100vw" width="96">
-				</figure>`
-			);
-
-			widget = viewDocument.getRoot().getChild( 0 );
-			model = editor.model.document.getRoot().getChild( 0 );
-		} );
-
-		it( 'works with images containing srcset', () => {
-			const domParts = getWidgetDomParts( editor, widget, 'bottom-right' );
-			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-right' );
-			const finalPointerPosition = initialPosition.clone().moveBy( -20, -20 );
-
-			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
-				from: initialPosition,
-				to: finalPointerPosition
-			} );
-
-			expect( model.getAttribute( 'width' ) ).to.equal( '76px' );
-		} );
-
-		it( 'retains width after removing srcset', () => {
-			const domParts = getWidgetDomParts( editor, widget, 'bottom-right' );
-			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-right' );
-			const finalPointerPosition = initialPosition.clone().moveBy( -16, -16 );
-
-			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
-				from: initialPosition,
-				to: finalPointerPosition
-			} );
-
-			editor.model.change( writer => {
-				writer.removeAttribute( 'srcset', model );
-			} );
-
-			const expectedHtml = '<figure class="image image_resized" style="width:80px;"><img src="/assets/sample.png"></figure>';
-			expect( editor.getData() ).to.equal( expectedHtml );
-		} );
-
-		async function preloadImage( imageUrl ) {
-			const image = document.createElement( 'img' );
-
-			image.src = imageUrl;
-
-			return new Promise( ( resolve, reject ) => {
-				image.addEventListener( 'load', () => resolve( image ) );
-				image.addEventListener( 'error', () => reject( image ) );
-				document.body.appendChild( image );
-			} );
-		}
-	} );
-
-	describe( 'widget toolbar integration', () => {
-		let widgetToolbarRepository;
-
 		beforeEach( async () => {
 		beforeEach( async () => {
-			await createEditor( {
-				plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeEditing, ImageToolbar, ImageTextAlternative ],
-				image: {
-					toolbar: [ 'imageTextAlternative' ],
-					resizeUnit: 'px'
-				}
-			} );
-
-			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
-
-			widget = viewDocument.getRoot().getChild( 1 );
-
-			widgetToolbarRepository = editor.plugins.get( 'WidgetToolbarRepository' );
-		} );
-
-		it( 'default toolbar visibility', async () => {
-			expect( widgetToolbarRepository.isEnabled ).to.be.true;
-		} );
-
-		it( 'visibility during the resize', async () => {
-			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
-
-			resizerMouseSimulator.down( editor, domResizeHandle );
-
-			expect( widgetToolbarRepository.isEnabled ).to.be.false;
-
-			resizerMouseSimulator.up( editor, domResizeHandle );
-		} );
-
-		it( 'visibility after the resize', async () => {
-			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
-
-			resizerMouseSimulator.down( editor, domResizeHandle );
-			resizerMouseSimulator.up( editor, domResizeHandle );
-
-			expect( widgetToolbarRepository.isEnabled ).to.be.true;
-		} );
-
-		it( 'visibility after the resize was canceled', async () => {
-			const resizer = getSelectedImageResizer( editor );
-			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
-
-			resizerMouseSimulator.down( editor, domResizeHandle );
-
-			resizer.cancel();
-			expect( widgetToolbarRepository.isEnabled ).to.be.true;
+			editor = await createEditor();
 		} );
 		} );
 
 
-		it( 'restores toolbar when clicking the handle without drag', () => {
-			// (https://github.com/ckeditor/ckeditor5-widget/pull/112#pullrequestreview-337725256).
-			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
-
-			resizerMouseSimulator.down( editor, domResizeHandle );
-			resizerMouseSimulator.up( editor, domResizeHandle );
-
-			expect( widgetToolbarRepository.isEnabled ).to.be.true;
+		it( 'defines the imageResize command', () => {
+			expect( editor.commands.get( 'imageResize' ) ).to.be.instanceOf( ImageResizeCommand );
 		} );
 		} );
 	} );
 	} );
 
 
-	function wait( delay ) {
-		return new Promise( resolve => window.setTimeout( () => resolve(), delay ) );
-	}
-
-	function getDomWidth( domElement ) {
-		return new Rect( domElement ).width;
-	}
-
-	function getSelectedImageResizer( editor ) {
-		return editor.plugins.get( 'WidgetResize' )._getResizerByViewElement(
-			editor.editing.view.document.selection.getSelectedElement()
-		);
-	}
-
 	function createEditor( config ) {
 	function createEditor( config ) {
-		editorElement = document.createElement( 'div' );
-
-		absoluteContainer.appendChild( editorElement );
-
 		return ClassicEditor
 		return ClassicEditor
 			.create( editorElement, config || {
 			.create( editorElement, config || {
-				plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeEditing ],
+				plugins: [ Paragraph, Image, ImageStyle, ImageResizeEditing ],
 				image: {
 				image: {
 					resizeUnit: 'px'
 					resizeUnit: 'px'
 				}
 				}
 			} )
 			} )
 			.then( newEditor => {
 			.then( newEditor => {
-				editor = newEditor;
-				view = editor.editing.view;
-				viewDocument = view.document;
-				widget = viewDocument.getRoot().getChild( 1 );
-
-				focusEditor( editor );
+				focusEditor( newEditor );
 
 
-				return editor;
+				return newEditor;
 			} );
 			} );
 	}
 	}
 } );
 } );

+ 418 - 0
packages/ckeditor5-image/tests/imageresize/imageresizehandles.js

@@ -0,0 +1,418 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* global document */
+
+// ClassicTestEditor can't be used, as it doesn't handle the focus, which is needed to test resizer visual cues.
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+
+import Image from '../../src/image';
+import ImageToolbar from '../../src/imagetoolbar';
+import ImageResizeEditing from '../../src/imageresize/imageresizeediting';
+import ImageResizeHandles from '../../src/imageresize/imageresizehandles';
+import ImageTextAlternative from '../../src/imagetextalternative';
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ImageStyle from '../../src/imagestyle';
+import Undo from '@ckeditor/ckeditor5-undo/src/undo';
+import Table from '@ckeditor/ckeditor5-table/src/table';
+
+import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
+import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+import {
+	focusEditor,
+	resizerMouseSimulator,
+	getWidgetDomParts,
+	getHandleCenterPoint
+} from '@ckeditor/ckeditor5-widget/tests/widgetresize/_utils/utils';
+
+import WidgetResize from '@ckeditor/ckeditor5-widget/src/widgetresize';
+
+describe( 'ImageResizeHandles', () => {
+	// 100x50 black png image
+	const IMAGE_SRC_FIXTURE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAQAAAAAPLY1AAAAQklEQVR42u3PQREAAAgDoK1/' +
+		'aM3g14MGNJMXKiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiJysRFNMgH0RpujAAAAAElFTkSuQmCC';
+
+	let widget, editor, view, viewDocument, editorElement;
+
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+	} );
+
+	afterEach( async () => {
+		editorElement.remove();
+
+		if ( editor ) {
+			await editor.destroy();
+		}
+	} );
+
+	it( 'should be named', () => {
+		expect( ImageResizeHandles.pluginName ).to.equal( 'ImageResizeHandles' );
+	} );
+
+	it( 'uses percents by default', async () => {
+		const localEditor = await createEditor( {
+			plugins: [ Image, ImageResizeEditing, ImageResizeHandles ]
+		} );
+
+		const attachToSpy = sinon.spy( localEditor.plugins.get( WidgetResize ), 'attachTo' );
+
+		setData( localEditor.model, `[<image imageStyle="side" src="${ IMAGE_SRC_FIXTURE }"></image>]` );
+
+		expect( attachToSpy.args[ 0 ][ 0 ] ).to.have.a.property( 'unit', '%' );
+
+		attachToSpy.restore();
+
+		await localEditor.destroy();
+	} );
+
+	describe( 'command', () => {
+		beforeEach( async () => {
+			editor = await createEditor();
+		} );
+
+		it( 'uses the command on commit', async () => {
+			const spy = sinon.spy( editor.commands.get( 'imageResize' ), 'execute' );
+
+			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
+			widget = viewDocument.getRoot().getChild( 1 );
+			const domParts = getWidgetDomParts( editor, widget, 'bottom-left' );
+
+			const finalPointerPosition = getHandleCenterPoint( domParts.widget, 'bottom-left' ).moveBy( 10, -10 );
+
+			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, finalPointerPosition );
+
+			expect( spy.calledOnce ).to.be.true;
+			expect( spy.args[ 0 ][ 0 ] ).to.deep.equal( { width: '80px' } );
+		} );
+
+		it( 'disables the resizer if the command is disabled', async () => {
+			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
+
+			const resizer = getSelectedImageResizer( editor );
+
+			let isEnabled = false;
+
+			editor.commands.get( 'imageResize' ).on( 'set:isEnabled', evt => {
+				evt.return = isEnabled;
+				evt.stop();
+			}, { priority: 'highest' } );
+
+			editor.commands.get( 'imageResize' ).refresh();
+			expect( resizer.isEnabled ).to.be.false;
+
+			isEnabled = true;
+			editor.commands.get( 'imageResize' ).refresh();
+			expect( resizer.isEnabled ).to.be.true;
+		} );
+
+		it( 'the resizer is disabled from the beginning when the command is disabled when the image is inserted', async () => {
+			setData( editor.model, '<paragraph>foo[]</paragraph>' );
+
+			editor.commands.get( 'imageResize' ).on( 'set:isEnabled', evt => {
+				evt.return = false;
+				evt.stop();
+			}, { priority: 'highest' } );
+			editor.commands.get( 'imageResize' ).refresh();
+
+			editor.model.change( writer => {
+				editor.model.insertContent( writer.createElement( 'image', { src: IMAGE_SRC_FIXTURE } ) );
+			} );
+
+			const resizer = getSelectedImageResizer( editor );
+			const resizerWrapper = editor.ui.getEditableElement().querySelector( '.ck-widget__resizer' );
+
+			expect( resizer.isEnabled ).to.be.false;
+			expect( resizerWrapper.style.display ).to.equal( 'none' );
+		} );
+	} );
+
+	describe( 'side image resizing', () => {
+		beforeEach( async () => {
+			editor = await createEditor();
+
+			setData( editor.model, `<paragraph>foo</paragraph>[<image imageStyle="side" src="${ IMAGE_SRC_FIXTURE }"></image>]` );
+
+			widget = viewDocument.getRoot().getChild( 1 );
+		} );
+
+		it( 'doesn\'t flicker at the beginning of the resize', async () => {
+			// (#5189)
+			const resizerPosition = 'bottom-left';
+			const domParts = getWidgetDomParts( editor, widget, resizerPosition );
+			const initialPointerPosition = getHandleCenterPoint( domParts.widget, resizerPosition );
+			const resizeWrapperView = widget.getChild( 2 );
+
+			resizerMouseSimulator.down( editor, domParts.resizeHandle );
+
+			resizerMouseSimulator.move( editor, domParts.resizeHandle, null, initialPointerPosition );
+
+			expect( resizeWrapperView.getStyle( 'width' ) ).to.equal( '100px' );
+
+			resizerMouseSimulator.up( editor );
+		} );
+
+		it( 'makes no change when clicking the handle without drag', () => {
+			const resizerPosition = 'bottom-left';
+			const expectedWidth = 100;
+			const domParts = getWidgetDomParts( editor, widget, resizerPosition );
+
+			resizerMouseSimulator.down( editor, domParts.resizeHandle );
+
+			expect( getDomWidth( domParts.widget ), 'DOM width check' ).to.be.closeTo( expectedWidth, 2 );
+
+			resizerMouseSimulator.up( editor );
+
+			const modelItem = editor.model.document.getRoot().getChild( 1 );
+
+			expect( modelItem.getAttribute( 'width' ), 'model width attribute' ).to.be.undefined;
+		} );
+	} );
+
+	describe( 'undo integration', () => {
+		beforeEach( async () => {
+			editor = await createEditor();
+
+			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
+
+			widget = viewDocument.getRoot().getChild( 1 );
+		} );
+
+		it( 'has correct border size after undo', async () => {
+			const domParts = getWidgetDomParts( editor, widget, 'bottom-left' );
+			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-left' );
+			const finalPointerPosition = initialPosition.clone().moveBy( 0, 10 );
+			const plugin = editor.plugins.get( 'WidgetResize' );
+
+			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
+				from: initialPosition,
+				to: finalPointerPosition
+			} );
+
+			expect( domParts.widget.style.width ).to.equal( '120px' );
+
+			editor.commands.get( 'undo' ).execute();
+
+			// Toggle _visibleResizer to force synchronous redraw. Otherwise you'd need to wait ~200ms for
+			// throttled redraw to take place, making tests slower.
+			const visibleResizer = plugin._visibleResizer;
+			plugin._visibleResizer = null;
+			plugin._visibleResizer = visibleResizer;
+
+			const resizerWrapper = document.querySelector( '.ck-widget__resizer' );
+			const shadowBoundingRect = resizerWrapper.getBoundingClientRect();
+
+			expect( shadowBoundingRect.width ).to.equal( 100 );
+			expect( shadowBoundingRect.height ).to.equal( 50 );
+		} );
+	} );
+
+	describe( 'table integration', () => {
+		it( 'works when resizing in a table', async () => {
+			editor = await createEditor();
+
+			setData( editor.model,
+				'<table>' +
+					`<tableRow><tableCell>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]</tableCell></tableRow>` +
+				'</table>'
+			);
+
+			widget = viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 ).getChild( 0 );
+			const model = editor.model.document.getRoot().getChild( 0 ).getChild( 0 ).getChild( 0 ).getChild( 0 );
+
+			const domParts = getWidgetDomParts( editor, widget, 'bottom-right' );
+			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-right' );
+			const finalPointerPosition = initialPosition.clone().moveBy( -40, -20 );
+
+			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
+				from: initialPosition,
+				to: finalPointerPosition
+			} );
+
+			expect( model.getAttribute( 'width' ) ).to.equal( '60px' );
+		} );
+	} );
+
+	describe( 'srcset integration', () => {
+		// The image is 96x96 pixels.
+		const imageBaseUrl = '/assets/sample.png';
+		let model;
+		let images = [];
+
+		before( async () => {
+			images = await Promise.all( [
+				preloadImage( imageBaseUrl ),
+				preloadImage( imageBaseUrl + '?a' ),
+				preloadImage( imageBaseUrl + '?b' ),
+				preloadImage( imageBaseUrl + '?c' )
+			] );
+		} );
+
+		after( () => {
+			for ( const image of images ) {
+				image.remove();
+			}
+		} );
+
+		beforeEach( async () => {
+			editor = await createEditor();
+
+			editor.setData(
+				`<figure class="image">
+					<img src="${ imageBaseUrl }"
+						srcset="${ imageBaseUrl }?a 110w,
+							${ imageBaseUrl }?b 440w,
+							${ imageBaseUrl }?c 1025w"
+						sizes="100vw" width="96">
+				</figure>`
+			);
+
+			widget = viewDocument.getRoot().getChild( 0 );
+			model = editor.model.document.getRoot().getChild( 0 );
+		} );
+
+		it( 'works with images containing srcset', () => {
+			const domParts = getWidgetDomParts( editor, widget, 'bottom-right' );
+			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-right' );
+			const finalPointerPosition = initialPosition.clone().moveBy( -20, -20 );
+
+			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
+				from: initialPosition,
+				to: finalPointerPosition
+			} );
+
+			expect( model.getAttribute( 'width' ) ).to.equal( '76px' );
+		} );
+
+		it( 'retains width after removing srcset', () => {
+			const domParts = getWidgetDomParts( editor, widget, 'bottom-right' );
+			const initialPosition = getHandleCenterPoint( domParts.widget, 'bottom-right' );
+			const finalPointerPosition = initialPosition.clone().moveBy( -16, -16 );
+
+			resizerMouseSimulator.dragTo( editor, domParts.resizeHandle, {
+				from: initialPosition,
+				to: finalPointerPosition
+			} );
+
+			editor.model.change( writer => {
+				writer.removeAttribute( 'srcset', model );
+			} );
+
+			const expectedHtml = '<figure class="image image_resized" style="width:80px;"><img src="/assets/sample.png"></figure>';
+			expect( editor.getData() ).to.equal( expectedHtml );
+		} );
+
+		async function preloadImage( imageUrl ) {
+			const image = document.createElement( 'img' );
+
+			image.src = imageUrl;
+
+			return new Promise( ( resolve, reject ) => {
+				image.addEventListener( 'load', () => resolve( image ) );
+				image.addEventListener( 'error', () => reject( image ) );
+				document.body.appendChild( image );
+			} );
+		}
+	} );
+
+	describe( 'widget toolbar integration', () => {
+		let widgetToolbarRepository;
+
+		beforeEach( async () => {
+			editor = await createEditor( {
+				plugins: [
+					Paragraph,
+					Image,
+					ImageResizeEditing,
+					ImageResizeHandles,
+					ImageToolbar,
+					ImageTextAlternative
+				],
+				image: {
+					toolbar: [ 'imageTextAlternative' ],
+					resizeUnit: 'px'
+				}
+			} );
+
+			setData( editor.model, `<paragraph>foo</paragraph>[<image src="${ IMAGE_SRC_FIXTURE }"></image>]` );
+
+			widget = viewDocument.getRoot().getChild( 1 );
+
+			widgetToolbarRepository = editor.plugins.get( 'WidgetToolbarRepository' );
+		} );
+
+		it( 'default toolbar visibility', async () => {
+			expect( widgetToolbarRepository.isEnabled ).to.be.true;
+		} );
+
+		it( 'visibility during the resize', async () => {
+			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
+
+			resizerMouseSimulator.down( editor, domResizeHandle );
+
+			expect( widgetToolbarRepository.isEnabled ).to.be.false;
+
+			resizerMouseSimulator.up( editor, domResizeHandle );
+		} );
+
+		it( 'visibility after the resize', async () => {
+			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
+
+			resizerMouseSimulator.down( editor, domResizeHandle );
+			resizerMouseSimulator.up( editor, domResizeHandle );
+
+			expect( widgetToolbarRepository.isEnabled ).to.be.true;
+		} );
+
+		it( 'visibility after the resize was canceled', async () => {
+			const resizer = getSelectedImageResizer( editor );
+			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
+
+			resizerMouseSimulator.down( editor, domResizeHandle );
+
+			resizer.cancel();
+			expect( widgetToolbarRepository.isEnabled ).to.be.true;
+		} );
+
+		it( 'restores toolbar when clicking the handle without drag', () => {
+			// (https://github.com/ckeditor/ckeditor5-widget/pull/112#pullrequestreview-337725256).
+			const domResizeHandle = getWidgetDomParts( editor, widget, 'bottom-left' ).resizeHandle;
+
+			resizerMouseSimulator.down( editor, domResizeHandle );
+			resizerMouseSimulator.up( editor, domResizeHandle );
+
+			expect( widgetToolbarRepository.isEnabled ).to.be.true;
+		} );
+	} );
+
+	function getDomWidth( domElement ) {
+		return new Rect( domElement ).width;
+	}
+
+	function getSelectedImageResizer( editor ) {
+		return editor.plugins.get( 'WidgetResize' )._getResizerByViewElement(
+			editor.editing.view.document.selection.getSelectedElement()
+		);
+	}
+
+	function createEditor( config ) {
+		return ClassicEditor.create( editorElement, config || {
+			plugins: [ Image, ImageStyle, Paragraph, Undo, Table, ImageResizeEditing, ImageResizeHandles ],
+			image: {
+				resizeUnit: 'px'
+			}
+		} ).then( newEditor => {
+			view = newEditor.editing.view;
+			viewDocument = view.document;
+
+			focusEditor( newEditor );
+
+			return newEditor;
+		} );
+	}
+} );

+ 0 - 0
packages/ckeditor5-image/tests/manual/imageresizeui.html → packages/ckeditor5-image/tests/manual/imageresizebuttons.html


+ 10 - 3
packages/ckeditor5-image/tests/manual/imageresizeui.js → packages/ckeditor5-image/tests/manual/imageresizebuttons.js

@@ -7,20 +7,23 @@
 
 
 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 ImageResize from '../../src/imageresize';
 import Indent from '@ckeditor/ckeditor5-indent/src/indent';
 import Indent from '@ckeditor/ckeditor5-indent/src/indent';
 import IndentBlock from '@ckeditor/ckeditor5-indent/src/indentblock';
 import IndentBlock from '@ckeditor/ckeditor5-indent/src/indentblock';
 import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
 import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
 
 
+import ImageResizeEditing from '../../src/imageresize/imageresizeediting';
+import ImageResizeButtons from '../../src/imageresize/imageresizebuttons';
+
 import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';
 import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';
 
 
 const commonConfig = {
 const commonConfig = {
 	plugins: [
 	plugins: [
 		ArticlePluginSet,
 		ArticlePluginSet,
-		ImageResize,
 		Indent,
 		Indent,
 		IndentBlock,
 		IndentBlock,
-		EasyImage
+		EasyImage,
+		ImageResizeEditing,
+		ImageResizeButtons
 	],
 	],
 	toolbar: [ 'heading', '|', 'bold', 'italic', 'link',
 	toolbar: [ 'heading', '|', 'bold', 'italic', 'link',
 		'bulletedList', 'numberedList', 'blockQuote', 'insertTable', 'undo', 'redo', 'outdent', 'indent' ],
 		'bulletedList', 'numberedList', 'blockQuote', 'insertTable', 'undo', 'redo', 'outdent', 'indent' ],
@@ -31,6 +34,8 @@ const commonConfig = {
 	cloudServices: CS_CONFIG
 	cloudServices: CS_CONFIG
 };
 };
 
 
+// Editor 1
+
 const imageConfig1 = {
 const imageConfig1 = {
 	resizeUnit: '%',
 	resizeUnit: '%',
 	resizeOptions: [
 	resizeOptions: [
@@ -69,6 +74,8 @@ ClassicEditor
 		console.error( err.stack );
 		console.error( err.stack );
 	} );
 	} );
 
 
+// Editor 2
+
 const imageConfig2 = {
 const imageConfig2 = {
 	resizeUnit: '%',
 	resizeUnit: '%',
 	resizeOptions: [
 	resizeOptions: [

+ 3 - 0
packages/ckeditor5-image/tests/manual/imageresizeui.md → packages/ckeditor5-image/tests/manual/imageresizebuttons.md

@@ -1,6 +1,7 @@
 ## Image Resize UI
 ## Image Resize UI
 
 
 Tests for manual image resizing using the UI in the image toolbar.
 Tests for manual image resizing using the UI in the image toolbar.
+**NOTE**: Both editors have disabled resize handles. The only way to resize the images is to do it by the UI in the image toolbar.
 
 
 ## Dropdown
 ## Dropdown
 
 
@@ -9,6 +10,7 @@ Tests for manual image resizing using the UI in the image toolbar.
 	- The plugin icon should appear only next to the dropdown button.
 	- The plugin icon should appear only next to the dropdown button.
 	- Each option should have a label text corresponding to an option value defined in the configuration.
 	- Each option should have a label text corresponding to an option value defined in the configuration.
 	- The selected option should be "on" when the dropdown is open.
 	- The selected option should be "on" when the dropdown is open.
+	- The editor is using the combination of `ImageResizeEditing` and `ImageResizeButtons`.
 
 
 ## Buttons
 ## Buttons
 
 
@@ -18,3 +20,4 @@ Tests for manual image resizing using the UI in the image toolbar.
 	- No label should be rendered,
 	- No label should be rendered,
 	- The tooltip text and the `aria-label` attribute should be the same (and more verbose).
 	- The tooltip text and the `aria-label` attribute should be the same (and more verbose).
 	- The selected option button should be "on".
 	- The selected option button should be "on".
+	- The editor is using the combination of `ImageResizeEditing` and `ImageResizeButtons`.

+ 1 - 1
packages/ckeditor5-widget/src/widgetresize.js

@@ -87,7 +87,7 @@ export default class WidgetResize extends Plugin {
 			}
 			}
 		};
 		};
 
 
-		const redrawFocusedResizerThrottled = throttle( redrawFocusedResizer, 200 ); // 5fps
+		const redrawFocusedResizerThrottled = throttle( redrawFocusedResizer, 200 );
 
 
 		// Redraws occurring upon a change of visible resizer must not be throttled, as it is crucial for the initial
 		// Redraws occurring upon a change of visible resizer must not be throttled, as it is crucial for the initial
 		// render. Without it the resizer frame would be misaligned with resizing host for a fraction of second.
 		// render. Without it the resizer frame would be misaligned with resizing host for a fraction of second.

+ 8 - 0
packages/ckeditor5-widget/src/widgetresize/resizer.js

@@ -96,6 +96,14 @@ export default class Resizer {
 				event.stop();
 				event.stop();
 			}
 			}
 		}, { priority: 'high' } );
 		}, { priority: 'high' } );
+
+		this.on( 'change:isEnabled', () => {
+			// We should redraw the resize handles when the plugin is enabled again.
+			// Otherwise they won't show up.
+			if ( this.isEnabled ) {
+				this.redraw();
+			}
+		} );
 	}
 	}
 
 
 	/**
 	/**