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

Changed: Make ImageStyleUI independent of ImageStyleEditing.

Maciej Gołaszewski 8 лет назад
Родитель
Сommit
a1f08430eb

+ 2 - 202
packages/ckeditor5-image/src/imagestyle/imagestyleediting.js

@@ -11,12 +11,7 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import ImageStyleCommand from './imagestylecommand';
 import ImageEditing from '../image/imageediting';
 import { viewToModelStyleAttribute, modelToViewStyleAttribute } from './converters';
-import log from '@ckeditor/ckeditor5-utils/src/log';
-
-import fullWidthIcon from '@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg';
-import leftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg';
-import centerIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg';
-import rightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg';
+import { normalizeImageStyles } from './utils';
 
 /**
  * The image style engine plugin. It sets the default configuration, creates converters and registers
@@ -52,7 +47,7 @@ export default class ImageStyleEditing extends Plugin {
 		editor.config.define( 'image.styles', [ 'imageStyleFull', 'imageStyleSide' ] );
 
 		// Get configuration.
-		const styles = this.imageStyles;
+		const styles = normalizeImageStyles( editor.config.get( 'image.styles' ) );
 
 		// Allow imageStyle attribute in image.
 		// We could call it 'style' but https://github.com/ckeditor/ckeditor5-engine/issues/559.
@@ -71,201 +66,6 @@ export default class ImageStyleEditing extends Plugin {
 			editor.commands.add( style.name, new ImageStyleCommand( editor, style ) );
 		}
 	}
-
-	/**
-	 * Returns {@link module:image/image~ImageConfig#styles} array with items normalized in the
-	 * {@link module:image/imagestyle/imagestyleediting~ImageStyleFormat} format, translated
-	 * `title` and a complete `icon` markup for each style.
-	 *
-	 * @readonly
-	 * @type {Array.<module:image/imagestyle/imagestyleediting~ImageStyleFormat>}
-	 */
-	get imageStyles() {
-		// Return cached value if there is one to improve the performance.
-		if ( this._cachedImageStyles ) {
-			return this._cachedImageStyles;
-		}
-
-		const styles = [];
-		const editor = this.editor;
-		const titles = this.localizedDefaultStylesTitles;
-		const configuredStyles = editor.config.get( 'image.styles' );
-
-		for ( let style of configuredStyles ) {
-			style = normalizeStyle( style );
-
-			// Localize the titles of the styles, if a title corresponds with
-			// a localized default provided by the plugin.
-			if ( titles[ style.title ] ) {
-				style.title = titles[ style.title ];
-			}
-
-			// Don't override the user-defined styles array, clone it instead.
-			styles.push( style );
-		}
-
-		this._cachedImageStyles = styles;
-
-		return styles;
-	}
-
-	/**
-	 * Returns the default localized style titles provided by the plugin e.g. ready to
-	 * use in the {@link #imageStyles}.
-	 *
-	 * The following localized titles corresponding with
-	 * {@link module:image/imagestyle/imagestyleediting~ImageStyleEditing.defaultStyles} are available:
-	 *
-	 * * `'Full size image'`,
-	 * * `'Side image'`,
-	 * * `'Left aligned image'`,
-	 * * `'Centered image'`,
-	 * * `'Right aligned image'`
-	 *
-	 * @readonly
-	 * @type {Object.<String,String>}
-	 */
-	get localizedDefaultStylesTitles() {
-		const t = this.editor.t;
-
-		return {
-			'Full size image': t( 'Full size image' ),
-			'Side image': t( 'Side image' ),
-			'Left aligned image': t( 'Left aligned image' ),
-			'Centered image': t( 'Centered image' ),
-			'Right aligned image': t( 'Right aligned image' ),
-		};
-	}
-}
-
-/**
- * Default image styles provided by the plugin, which can be referred in the
- * {@link module:image/image~ImageConfig#styles} config.
- *
- * Among them, 2 default semantic content styles are available:
- *
- * * `imageStyleFull` is a full–width image without any CSS class,
- * * `imageStyleSide` is a side image styled with the `image-style-side` CSS class
- *
- * There are also 3 styles focused on formatting:
- *
- * * `imageStyleAlignLeft` aligns the image to the left using the `image-style-align-left` class,
- * * `imageStyleAlignCenter` centers the image to the left using the `image-style-align-center` class,
- * * `imageStyleAlignRight` aligns the image to the right using the `image-style-align-right` class,
- *
- * @member {Object.<String,Object>}
- */
-ImageStyleEditing.defaultStyles = {
-	// This option is equal to situation when no style is applied.
-	imageStyleFull: {
-		name: 'imageStyleFull',
-		title: 'Full size image',
-		icon: fullWidthIcon,
-		isDefault: true
-	},
-
-	// This represents side image.
-	imageStyleSide: {
-		name: 'imageStyleSide',
-		title: 'Side image',
-		icon: rightIcon,
-		className: 'image-style-side'
-	},
-
-	// This style represents an imaged aligned to the left.
-	imageStyleAlignLeft: {
-		name: 'imageStyleAlignLeft',
-		title: 'Left aligned image',
-		icon: leftIcon,
-		className: 'image-style-align-left'
-	},
-
-	// This style represents a centered imaged.
-	imageStyleAlignCenter: {
-		name: 'imageStyleAlignCenter',
-		title: 'Centered image',
-		icon: centerIcon,
-		className: 'image-style-align-center'
-	},
-
-	// This style represents an imaged aligned to the right.
-	imageStyleAlignRight: {
-		name: 'imageStyleAlignRight',
-		title: 'Right aligned image',
-		icon: rightIcon,
-		className: 'image-style-align-right'
-	}
-};
-
-/**
- * Default image style icons provided by the plugin, which can be referred in the
- * {@link module:image/image~ImageConfig#styles} config.
- *
- * There are 4 icons available: `'full'`, `'left'`, `'center'` and `'right'`.
- *
- * @member {Object.<String, String>}
- */
-ImageStyleEditing.defaultIcons = {
-	full: fullWidthIcon,
-	left: leftIcon,
-	right: rightIcon,
-	center: centerIcon,
-};
-
-// Normalizes an image style provided in the {@link module:image/image~ImageConfig#styles}
-// and returns it in a {@link module:image/imagestyle/imagestyleediting~ImageStyleFormat}.
-//
-// @private
-// @param {Object} style
-// @returns {@link module:image/imagestyle/imagestyleediting~ImageStyleFormat}
-function normalizeStyle( style ) {
-	const defaultStyles = ImageStyleEditing.defaultStyles;
-	const defaultIcons = ImageStyleEditing.defaultIcons;
-
-	// Just the name of the style has been passed.
-	if ( typeof style == 'string' ) {
-		// If it's one of the defaults, just use it.
-		// Clone the style to avoid overriding defaults.
-		if ( defaultStyles[ style ] ) {
-			style = Object.assign( {}, defaultStyles[ style ] );
-		}
-		// If it's just a name but none of the defaults, warn because probably it's a mistake.
-		else {
-			log.warn(
-				'image-style-not-found: There is no such image style of given name.',
-				{ name: style }
-			);
-
-			// Normalize the style anyway to prevent errors.
-			style = {
-				name: style
-			};
-		}
-	}
-
-	// If an object style has been passed and if the name matches one of the defaults,
-	// extend it with defaults – the user wants to customize a default style.
-	// Note: Don't override the user–defined style object, clone it instead.
-	else if ( defaultStyles[ style.name ] ) {
-		const defaultStyle = defaultStyles[ style.name ];
-		const extendedStyle = Object.assign( {}, style );
-
-		for ( const prop in defaultStyle ) {
-			if ( !style.hasOwnProperty( prop ) ) {
-				extendedStyle[ prop ] = defaultStyle[ prop ];
-			}
-		}
-
-		style = extendedStyle;
-	}
-
-	// If an icon is defined as a string and correspond with a name
-	// in default icons, use the default icon provided by the plugin.
-	if ( typeof style.icon == 'string' && defaultIcons[ style.icon ] ) {
-		style.icon = defaultIcons[ style.icon ];
-	}
-
-	return style;
 }
 
 /**

+ 52 - 3
packages/ckeditor5-image/src/imagestyle/imagestyleui.js

@@ -8,9 +8,10 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import ImageStyleEditing from './imagestyleediting';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 
+import { normalizeImageStyles } from './utils';
+
 import '../../theme/imagestyle.css';
 
 /**
@@ -19,14 +20,43 @@ import '../../theme/imagestyle.css';
  * @extends module:core/plugin~Plugin
  */
 export default class ImageStyleUI extends Plugin {
+	/**
+	 * Returns the default localized style titles provided by the plugin e.g. ready to
+	 * use in the {@link #imageStyles}.
+	 *
+	 * The following localized titles corresponding with
+	 * {@link module:image/imagestyle/imagestyleediting~defaultStyles} are available:
+	 *
+	 * * `'Full size image'`,
+	 * * `'Side image'`,
+	 * * `'Left aligned image'`,
+	 * * `'Centered image'`,
+	 * * `'Right aligned image'`
+	 *
+	 * @returns {Object.<String,String>}
+	 */
+	get localizedDefaultStylesTitles() {
+		const t = this.editor.t;
+
+		return {
+			'Full size image': t( 'Full size image' ),
+			'Side image': t( 'Side image' ),
+			'Left aligned image': t( 'Left aligned image' ),
+			'Centered image': t( 'Centered image' ),
+			'Right aligned image': t( 'Right aligned image' )
+		};
+	}
+
 	/**
 	 * @inheritDoc
 	 */
 	init() {
 		const editor = this.editor;
-		const styles = editor.plugins.get( ImageStyleEditing ).imageStyles;
+		const configuredStyles = editor.config.get( 'image.styles' );
 
-		for ( const style of styles ) {
+		const translatedStyles = translateStyles( normalizeImageStyles( configuredStyles ), this.localizedDefaultStylesTitles );
+
+		for ( const style of translatedStyles ) {
 			this._createButton( style );
 		}
 	}
@@ -59,3 +89,22 @@ export default class ImageStyleUI extends Plugin {
 		} );
 	}
 }
+
+/**
+ * Returns translated `title` from the passed styles array.
+ *
+ * @param {Array.<module:image/imagestyle/imagestyleediting~ImageStyleFormat>} styles
+ * @param titles
+ * @returns {Array.<module:image/imagestyle/imagestyleediting~ImageStyleFormat>}
+ */
+function translateStyles( styles, titles ) {
+	for ( const style of styles ) {
+		// Localize the titles of the styles, if a title corresponds with
+		// a localized default provided by the plugin.
+		if ( titles[ style.title ] ) {
+			style.title = titles[ style.title ];
+		}
+	}
+
+	return styles;
+}

+ 153 - 0
packages/ckeditor5-image/src/imagestyle/utils.js

@@ -0,0 +1,153 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module image/imagestyle/utils
+ */
+
+import log from '@ckeditor/ckeditor5-utils/src/log';
+
+import fullWidthIcon from '@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg';
+import leftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg';
+import centerIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg';
+import rightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg';
+
+/**
+ * Default image styles provided by the plugin, which can be referred in the
+ * {@link module:image/image~ImageConfig#styles} config.
+ *
+ * Among them, 2 default semantic content styles are available:
+ *
+ * * `imageStyleFull` is a full–width image without any CSS class,
+ * * `imageStyleSide` is a side image styled with the `image-style-side` CSS class
+ *
+ * There are also 3 styles focused on formatting:
+ *
+ * * `imageStyleAlignLeft` aligns the image to the left using the `image-style-align-left` class,
+ * * `imageStyleAlignCenter` centers the image to the left using the `image-style-align-center` class,
+ * * `imageStyleAlignRight` aligns the image to the right using the `image-style-align-right` class,
+ *
+ * @member {Object.<String,Object>}
+ */
+const defaultStyles = {
+	// This option is equal to situation when no style is applied.
+	imageStyleFull: {
+		name: 'imageStyleFull',
+		title: 'Full size image',
+		icon: fullWidthIcon,
+		isDefault: true
+	},
+
+	// This represents side image.
+	imageStyleSide: {
+		name: 'imageStyleSide',
+		title: 'Side image',
+		icon: rightIcon,
+		className: 'image-style-side'
+	},
+
+	// This style represents an imaged aligned to the left.
+	imageStyleAlignLeft: {
+		name: 'imageStyleAlignLeft',
+		title: 'Left aligned image',
+		icon: leftIcon,
+		className: 'image-style-align-left'
+	},
+
+	// This style represents a centered imaged.
+	imageStyleAlignCenter: {
+		name: 'imageStyleAlignCenter',
+		title: 'Centered image',
+		icon: centerIcon,
+		className: 'image-style-align-center'
+	},
+
+	// This style represents an imaged aligned to the right.
+	imageStyleAlignRight: {
+		name: 'imageStyleAlignRight',
+		title: 'Right aligned image',
+		icon: rightIcon,
+		className: 'image-style-align-right'
+	}
+};
+
+/**
+ * Default image style icons provided by the plugin, which can be referred in the
+ * {@link module:image/image~ImageConfig#styles} config.
+ *
+ * There are 4 icons available: `'full'`, `'left'`, `'center'` and `'right'`.
+ *
+ * @member {Object.<String, String>}
+ */
+const defaultIcons = {
+	full: fullWidthIcon,
+	left: leftIcon,
+	right: rightIcon,
+	center: centerIcon
+};
+
+/**
+ * Returns {@link module:image/image~ImageConfig#styles} array with items normalized in the
+ * {@link module:image/imagestyle/imagestyleediting~ImageStyleFormat} format and a complete `icon` markup for each style.
+ *
+ * @returns {Array.<module:image/imagestyle/imagestyleediting~ImageStyleFormat>}
+ */
+export function normalizeImageStyles( configuredStyles = [] ) {
+	return configuredStyles
+		.map( _normalizeStyle )
+		.map( style => Object.assign( {}, style ) );
+}
+
+// Normalizes an image style provided in the {@link module:image/image~ImageConfig#styles}
+// and returns it in a {@link module:image/imagestyle/imagestyleediting~ImageStyleFormat}.
+//
+// @param {Object} style
+// @returns {@link module:image/imagestyle/imagestyleediting~ImageStyleFormat}
+function _normalizeStyle( style ) {
+	// Just the name of the style has been passed.
+	if ( typeof style == 'string' ) {
+		// If it's one of the defaults, just use it.
+		// Clone the style to avoid overriding defaults.
+		if ( defaultStyles[ style ] ) {
+			style = Object.assign( {}, defaultStyles[ style ] );
+		}
+		// If it's just a name but none of the defaults, warn because probably it's a mistake.
+		else {
+			log.warn(
+				'image-style-not-found: There is no such image style of given name.',
+				{ name: style }
+			);
+
+			// Normalize the style anyway to prevent errors.
+			style = {
+				name: style
+			};
+		}
+	}
+
+	// If an object style has been passed and if the name matches one of the defaults,
+	// extend it with defaults – the user wants to customize a default style.
+	// Note: Don't override the user–defined style object, clone it instead.
+	else if ( defaultStyles[ style.name ] ) {
+		const defaultStyle = defaultStyles[ style.name ];
+		const extendedStyle = Object.assign( {}, style );
+
+		for ( const prop in defaultStyle ) {
+			if ( !style.hasOwnProperty( prop ) ) {
+				extendedStyle[ prop ] = defaultStyle[ prop ];
+			}
+		}
+
+		style = extendedStyle;
+	}
+
+	// If an icon is defined as a string and correspond with a name
+	// in default icons, use the default icon provided by the plugin.
+	if ( typeof style.icon == 'string' && defaultIcons[ style.icon ] ) {
+		style.icon = defaultIcons[ style.icon ];
+	}
+
+	return style;
+}

+ 3 - 191
packages/ckeditor5-image/tests/imagestyle/imagestyleediting.js

@@ -7,22 +7,16 @@ import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtest
 import ImageStyleEditing from '../../src/imagestyle/imagestyleediting';
 import ImageEditing from '../../src/image/imageediting';
 import ImageStyleCommand from '../../src/imagestyle/imagestylecommand';
-import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import log from '@ckeditor/ckeditor5-utils/src/log';
+
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
-import fullWidthIcon from '@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg';
-import leftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg';
-import centerIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg';
-import rightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg';
-
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 testUtils.createSinonSandbox();
 
 describe( 'ImageStyleEditing', () => {
-	let editor, plugin, model, document, viewDocument;
+	let editor, model, document, viewDocument;
 
 	afterEach( () => {
 		editor.destroy();
@@ -263,7 +257,7 @@ describe( 'ImageStyleEditing', () => {
 		} );
 	} );
 
-	describe( 'imageStyles()', () => {
+	describe( 'config', () => {
 		it( 'should fall back to defaults when no image.styles', () => {
 			return VirtualTestEditor
 				.create( {
@@ -309,187 +303,5 @@ describe( 'ImageStyleEditing', () => {
 					expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ { name: 'imageStyleSide' } ] );
 				} );
 		} );
-
-		it( 'should cache the styles', () => {
-			return VirtualTestEditor
-				.create( {
-					plugins: [ ImageStyleEditing ]
-				} )
-				.then( newEditor => {
-					editor = newEditor;
-					plugin = editor.plugins.get( ImageStyleEditing );
-
-					expect( plugin.imageStyles ).to.equal( plugin.imageStyles );
-				} );
-		} );
-
-		describe( 'object format', () => {
-			beforeEach( () => {
-				class TranslationMock extends Plugin {
-					init() {
-						sinon.stub( this.editor, 't' ).returns( 'Default translation' );
-					}
-				}
-
-				return VirtualTestEditor
-					.create( {
-						plugins: [ TranslationMock, ImageStyleEditing ],
-						image: {
-							styles: [
-								// Custom user styles.
-								{ name: 'foo', title: 'foo', icon: 'custom', isDefault: true, className: 'foo-class' },
-								{ name: 'bar', title: 'bar', icon: 'right', className: 'bar-class' },
-								{ name: 'baz', title: 'Side image', icon: 'custom', className: 'baz-class' },
-
-								// Customized default styles.
-								{ name: 'imageStyleFull', icon: 'left', title: 'Custom title' }
-							]
-						}
-					} )
-					.then( newEditor => {
-						editor = newEditor;
-						plugin = editor.plugins.get( ImageStyleEditing );
-					} );
-			} );
-
-			it( 'should pass through if #name not found in default styles', () => {
-				expect( plugin.imageStyles[ 0 ] ).to.deep.equal( {
-					name: 'foo',
-					title: 'foo',
-					icon: 'custom',
-					isDefault: true,
-					className: 'foo-class'
-				} );
-			} );
-
-			it( 'should use one of default icons if #icon matches', () => {
-				expect( plugin.imageStyles[ 1 ].icon ).to.equal( ImageStyleEditing.defaultIcons.right );
-			} );
-
-			it( 'should use one of default translations if #title matches', () => {
-				expect( plugin.imageStyles[ 2 ].title ).to.deep.equal( 'Default translation' );
-			} );
-
-			it( 'should extend one of default styles if #name matches', () => {
-				expect( plugin.imageStyles[ 3 ] ).to.deep.equal( {
-					name: 'imageStyleFull',
-					title: 'Custom title',
-					icon: ImageStyleEditing.defaultIcons.left,
-					isDefault: true
-				} );
-			} );
-		} );
-
-		describe( 'string format', () => {
-			it( 'should use one of default styles if #name matches', () => {
-				return VirtualTestEditor
-					.create( {
-						plugins: [ ImageStyleEditing ],
-						image: {
-							styles: [ 'imageStyleFull' ]
-						}
-					} )
-					.then( newEditor => {
-						editor = newEditor;
-						plugin = editor.plugins.get( ImageStyleEditing );
-						expect( plugin.imageStyles[ 0 ] ).to.deep.equal( ImageStyleEditing.defaultStyles.imageStyleFull );
-					} );
-			} );
-
-			it( 'should warn if a #name not found in default styles', () => {
-				testUtils.sinon.stub( log, 'warn' );
-
-				return VirtualTestEditor
-					.create( {
-						plugins: [ ImageStyleEditing ],
-						image: {
-							styles: [ 'foo' ]
-						}
-					} )
-					.then( newEditor => {
-						editor = newEditor;
-						plugin = editor.plugins.get( ImageStyleEditing );
-
-						expect( plugin.imageStyles[ 0 ] ).to.deep.equal( {
-							name: 'foo'
-						} );
-
-						sinon.assert.calledOnce( log.warn );
-						sinon.assert.calledWithExactly( log.warn,
-							sinon.match( /^image-style-not-found/ ),
-							{ name: 'foo' }
-						);
-					} );
-			} );
-		} );
-	} );
-
-	describe( 'localizedDefaultStylesTitles()', () => {
-		it( 'should return localized titles of default styles', () => {
-			return VirtualTestEditor
-				.create( {
-					plugins: [ ImageStyleEditing ]
-				} )
-				.then( newEditor => {
-					editor = newEditor;
-					plugin = editor.plugins.get( ImageStyleEditing );
-
-					expect( plugin.localizedDefaultStylesTitles ).to.deep.equal( {
-						'Full size image': 'Full size image',
-						'Side image': 'Side image',
-						'Left aligned image': 'Left aligned image',
-						'Centered image': 'Centered image',
-						'Right aligned image': 'Right aligned image'
-					} );
-				} );
-		} );
-	} );
-
-	describe( 'defaultStyles', () => {
-		it( 'should be defined', () => {
-			expect( ImageStyleEditing.defaultStyles ).to.deep.equal( {
-				imageStyleFull: {
-					name: 'imageStyleFull',
-					title: 'Full size image',
-					icon: fullWidthIcon,
-					isDefault: true
-				},
-				imageStyleSide: {
-					name: 'imageStyleSide',
-					title: 'Side image',
-					icon: rightIcon,
-					className: 'image-style-side'
-				},
-				imageStyleAlignLeft: {
-					name: 'imageStyleAlignLeft',
-					title: 'Left aligned image',
-					icon: leftIcon,
-					className: 'image-style-align-left'
-				},
-				imageStyleAlignCenter: {
-					name: 'imageStyleAlignCenter',
-					title: 'Centered image',
-					icon: centerIcon,
-					className: 'image-style-align-center'
-				},
-				imageStyleAlignRight: {
-					name: 'imageStyleAlignRight',
-					title: 'Right aligned image',
-					icon: rightIcon,
-					className: 'image-style-align-right'
-				}
-			} );
-		} );
-	} );
-
-	describe( 'defaultIcons', () => {
-		it( 'should be defined', () => {
-			expect( ImageStyleEditing.defaultIcons ).to.deep.equal( {
-				full: fullWidthIcon,
-				left: leftIcon,
-				right: rightIcon,
-				center: centerIcon,
-			} );
-		} );
 	} );
 } );

+ 55 - 1
packages/ckeditor5-image/tests/imagestyle/imagestyleui.js

@@ -8,9 +8,12 @@ import ImageStyleEditing from '../../src/imagestyle/imagestyleediting';
 import ImageStyleUI from '../../src/imagestyle/imagestyleui';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import global from '@ckeditor/ckeditor5-utils/src/dom/global';
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 
 describe( 'ImageStyleUI', () => {
 	let editor;
+
 	const styles = [
 		{ name: 'style 1', title: 'Style 1 title', icon: 'style1-icon', isDefault: true },
 		{ name: 'style 2', title: 'Style 2 title', icon: 'style2-icon', cssClass: 'style2-class' },
@@ -60,6 +63,35 @@ describe( 'ImageStyleUI', () => {
 		}
 	} );
 
+	it( 'should translate buttons if taken from default styles', () => {
+		const editorElement = global.document.createElement( 'div' );
+
+		global.document.body.appendChild( editorElement );
+
+		class TranslationMock extends Plugin {
+			init() {
+				sinon.stub( this.editor, 't' ).returns( 'Default title' );
+			}
+		}
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ TranslationMock, ImageStyleEditing, ImageStyleUI ],
+				image: {
+					styles: [
+						{ name: 'style 1', title: 'Side image', icon: 'style1-icon', isDefault: true }
+					]
+				}
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+
+				const buttonView = editor.ui.componentFactory.create( 'style 1' );
+
+				expect( buttonView.label ).to.equal( 'Default title' );
+			} );
+	} );
+
 	it( 'should not add buttons to image toolbar if configuration is present', () => {
 		const editorElement = global.document.createElement( 'div' );
 		global.document.body.appendChild( editorElement );
@@ -73,8 +105,30 @@ describe( 'ImageStyleUI', () => {
 				}
 			} )
 			.then( newEditor => {
-				expect( newEditor.config.get( 'image.toolbar' ) ).to.eql( [ 'foo', 'bar' ] );
+				expect( newEditor.config.get( 'image.toolbar' ) ).to.deep.equal( [ 'foo', 'bar' ] );
 				newEditor.destroy();
 			} );
 	} );
+
+	describe( 'localizedDefaultStylesTitles()', () => {
+		it( 'should return localized titles of default styles', () => {
+			return VirtualTestEditor
+				.create( {
+					plugins: [ ImageStyleUI ]
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					const plugin = editor.plugins.get( ImageStyleUI );
+
+					expect( plugin.localizedDefaultStylesTitles ).to.deep.equal( {
+						'Full size image': 'Full size image',
+						'Side image': 'Side image',
+						'Left aligned image': 'Left aligned image',
+						'Centered image': 'Centered image',
+						'Right aligned image': 'Right aligned image'
+					} );
+				} );
+		} );
+	} );
 } );

+ 111 - 0
packages/ckeditor5-image/tests/imagestyle/utils.js

@@ -0,0 +1,111 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import log from '@ckeditor/ckeditor5-utils/src/log';
+
+import fullWidthIcon from '@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg';
+import leftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg';
+import centerIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg';
+import rightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg';
+
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import { normalizeImageStyles } from '../../src/imagestyle/utils';
+
+testUtils.createSinonSandbox();
+
+describe( 'ImageStyle utils', () => {
+	let imageStyles;
+
+	describe( 'imageStyles()', () => {
+		describe( 'object format', () => {
+			beforeEach( () => {
+				imageStyles = normalizeImageStyles( [
+					{ name: 'foo', title: 'foo', icon: 'custom', isDefault: true, className: 'foo-class' },
+					{ name: 'bar', title: 'bar', icon: 'right', className: 'bar-class' },
+					{ name: 'baz', title: 'Side image', icon: 'custom', className: 'baz-class' },
+
+					// Customized default styles.
+					{ name: 'imageStyleFull', icon: 'left', title: 'Custom title' }
+				] );
+			} );
+
+			it( 'should pass through if #name not found in default styles', () => {
+				expect( imageStyles[ 0 ] ).to.deep.equal( {
+					name: 'foo',
+					title: 'foo',
+					icon: 'custom',
+					isDefault: true,
+					className: 'foo-class'
+				} );
+			} );
+
+			it( 'should use one of default icons if #icon matches', () => {
+				expect( imageStyles[ 1 ].icon ).to.equal( rightIcon );
+			} );
+
+			it( 'should extend one of default styles if #name matches', () => {
+				expect( imageStyles[ 3 ] ).to.deep.equal( {
+					name: 'imageStyleFull',
+					title: 'Custom title',
+					icon: leftIcon,
+					isDefault: true
+				} );
+			} );
+		} );
+
+		describe( 'string format', () => {
+			it( 'should use one of default styles if #name matches', () => {
+				expect( normalizeImageStyles( [ 'imageStyleFull' ] ) ).to.deep.equal( [ {
+					name: 'imageStyleFull',
+					title: 'Full size image',
+					icon: fullWidthIcon,
+					isDefault: true
+				} ] );
+
+				expect( normalizeImageStyles( [ 'imageStyleSide' ] ) ).to.deep.equal( [ {
+					name: 'imageStyleSide',
+					title: 'Side image',
+					icon: rightIcon,
+					className: 'image-style-side'
+				} ] );
+
+				expect( normalizeImageStyles( [ 'imageStyleAlignLeft' ] ) ).to.deep.equal( [ {
+					name: 'imageStyleAlignLeft',
+					title: 'Left aligned image',
+					icon: leftIcon,
+					className: 'image-style-align-left'
+				} ] );
+
+				expect( normalizeImageStyles( [ 'imageStyleAlignCenter' ] ) ).to.deep.equal( [ {
+					name: 'imageStyleAlignCenter',
+					title: 'Centered image',
+					icon: centerIcon,
+					className: 'image-style-align-center'
+				} ] );
+
+				expect( normalizeImageStyles( [ 'imageStyleAlignRight' ] ) ).to.deep.equal( [ {
+					name: 'imageStyleAlignRight',
+					title: 'Right aligned image',
+					icon: rightIcon,
+					className: 'image-style-align-right'
+				} ] );
+			} );
+
+			it( 'should warn if a #name not found in default styles', () => {
+				testUtils.sinon.stub( log, 'warn' );
+
+				expect( normalizeImageStyles( [ 'foo' ] ) ).to.deep.equal( [ {
+					name: 'foo'
+				} ] );
+
+				sinon.assert.calledOnce( log.warn );
+				sinon.assert.calledWithExactly( log.warn,
+					sinon.match( /^image-style-not-found/ ),
+					{ name: 'foo' }
+				);
+			} );
+		} );
+	} );
+} );