From a94d681cbd104440879bb1f3ee343825f63b1197 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 13:07:13 +0200 Subject: [PATCH 01/16] Defined ImageStyleEngine#imageStyles() getter which normalises config.image.styles with translations, ImageStyleEngine.defaultStyles extension and ImageStyleEngine.defaultIcons support. --- src/imagestyle.js | 3 +- src/imagestyle/imagestyleengine.js | 210 +++++++++- tests/imagestyle/imagestyleengine.js | 584 +++++++++++++++++++-------- 3 files changed, 612 insertions(+), 185 deletions(-) diff --git a/src/imagestyle.js b/src/imagestyle.js index 6300d492..b74abd68 100644 --- a/src/imagestyle.js +++ b/src/imagestyle.js @@ -37,7 +37,8 @@ export default class ImageStyle extends Plugin { * @inheritDoc */ init() { - const styles = this.editor.config.get( 'image.styles' ); + const editor = this.editor; + const styles = editor.plugins.get( ImageStyleEngine ).imageStyles; for ( const style of styles ) { this._createButton( style ); diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index f61a6555..a9d76239 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -11,8 +11,11 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import ImageStyleCommand from './imagestylecommand'; import ImageEngine from '../image/imageengine'; import { viewToModelStyleAttribute, modelToViewStyleAttribute } from './converters'; -import fullSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg'; -import sideIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg'; +import log from '@ckeditor/ckeditor5-utils/src/log'; + +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'; /** * The image style engine plugin. It sets the default configuration, creates converters and registers @@ -33,23 +36,16 @@ export default class ImageStyleEngine extends Plugin { */ init() { const editor = this.editor; - const t = editor.t; const doc = editor.document; const schema = doc.schema; const data = editor.data; const editing = editor.editing; // Define default configuration. - editor.config.define( 'image.styles', [ - // This option is equal to situation when no style is applied. - { name: 'imageStyleFull', title: t( 'Full size image' ), icon: fullSizeIcon, value: null }, - - // This represents side image. - { name: 'imageStyleSide', title: t( 'Side image' ), icon: sideIcon, value: 'side', className: 'image-style-side' } - ] ); + editor.config.define( 'image.styles', [ 'imageStyleFull', 'imageStyleSide' ] ); // Get configuration. - const styles = editor.config.get( 'image.styles' ); + const styles = this.imageStyles; // Allow imageStyle attribute in image. // We could call it 'style' but https://github.com/ckeditor/ckeditor5-engine/issues/559. @@ -72,6 +68,194 @@ export default class ImageStyleEngine 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/imagestyleengine~ImageStyleFormat} format, translated + * `title` and a complete `icon` markup for each style. + * + * @readonly + * @type {Array.} + */ + 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._localizedStyleTitles; + 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 ); + } + + return ( this._cachedImageStyles = styles ); + } + + /** + * Returns the default localized style titles provided by the plugin e.g. ready to + * use in the {@link #imageStyles}. + * + * @readonly + * @private + * @type {Object.} + */ + get _localizedStyleTitles() { + 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: + * + * * `imageStyleLeft` aligns the image to the left using the `image-style-align-left` class, + * * `imageStyleCenter` centers the image to the left using the `image-style-align-center` class, + * * `imageStyleRight` aligns the image to the right using the `image-style-align-right` class, + * + * @member {Object.} + */ +ImageStyleEngine.defaultStyles = { + // This option is equal to situation when no style is applied. + imageStyleFull: { + name: 'imageStyleFull', + title: 'Full size image', + icon: centerIcon, + value: null + }, + + // This represents side image. + imageStyleSide: { + name: 'imageStyleSide', + title: 'Side image', + icon: rightIcon, + value: 'side', + className: 'image-style-side' + }, + + // This style represents an imaged aligned to the left. + imageStyleLeft: { + name: 'imageStyleLeft', + title: 'Left aligned image', + icon: leftIcon, + value: 'left', + className: 'image-style-align-left' + }, + + // This style represents a centered imaged. + imageStyleCenter: { + name: 'imageStyleCenter', + title: 'Centered image', + icon: centerIcon, + value: 'side', + className: 'image-style-align-center' + }, + + // This style represents an imaged aligned to the right. + imageStyleRight: { + name: 'imageStyleRight', + title: 'Right aligned image', + icon: rightIcon, + value: 'right', + 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 3 icons available: `'left'`, `'center'` and `'right'`. + * + * @member {Object.} + */ +ImageStyleEngine.defaultIcons = { + 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/imagestyleengine~ImageStyleFormat}. +// +// @private +// @param {Object} style +// @returns {@link module:image/imagestyle/imagestyleengine~ImageStyleFormat} +function normalizeStyle( style ) { + const defaultStyles = ImageStyleEngine.defaultStyles; + const defaultIcons = ImageStyleEngine.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 non of the defaults, warn because probably it's a mistake. + // A which has just a name makes a little sense for the plugin. + 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; } /** @@ -93,7 +277,9 @@ export default class ImageStyleEngine extends Plugin { * * store the style's button in the editor {@link module:ui/componentfactory~ComponentFactory}. * @property {String} value A value used to store this style in the model attribute. * When the value is `null`, the style will be used as the default one. A default style does not apply any CSS class to the view element. - * @property {String} icon An SVG icon source (as XML string) to use when creating the style's button. + * @property {String} icon One of the following to be used when creating the style's button: + * * An SVG icon source (as an XML string), + * * One of {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine.defaultIcons} to use a default icon provided by the plugin. * @property {String} title The style's title. * @property {String} className The CSS class used to represent the style in view. */ diff --git a/tests/imagestyle/imagestyleengine.js b/tests/imagestyle/imagestyleengine.js index 1b59c85d..4a65bb23 100644 --- a/tests/imagestyle/imagestyleengine.js +++ b/tests/imagestyle/imagestyleengine.js @@ -7,241 +7,481 @@ import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtest import ImageStyleEngine from '../../src/imagestyle/imagestyleengine'; import ImageEngine from '../../src/image/imageengine'; 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 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'; + describe( 'ImageStyleEngine', () => { - let editor, document, viewDocument; - - beforeEach( () => { - return VirtualTestEditor - .create( { - plugins: [ ImageStyleEngine ], - image: { - styles: [ - { name: 'fullStyle', title: 'foo', icon: 'object-center', value: null }, - { name: 'sideStyle', title: 'bar', icon: 'object-right', value: 'side', className: 'side-class' }, - { name: 'dummyStyle', title: 'baz', icon: 'object-dummy', value: 'dummy', className: 'dummy-class' } - ] - } - } ) - .then( newEditor => { - editor = newEditor; - document = editor.document; - viewDocument = editor.editing.view; - } ); - } ); + let editor, plugin, document, viewDocument; - it( 'should be loaded', () => { - expect( editor.plugins.get( ImageStyleEngine ) ).to.be.instanceOf( ImageStyleEngine ); + afterEach( () => { + editor.destroy(); } ); - it( 'should load image engine', () => { - expect( editor.plugins.get( ImageEngine ) ).to.be.instanceOf( ImageEngine ); - } ); + describe( 'plugin', () => { + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ], + } ) + .then( newEditor => { + editor = newEditor; + } ); + } ); - it( 'should set schema rules for image style', () => { - const schema = document.schema; + it( 'should be loaded', () => { + expect( editor.plugins.get( ImageStyleEngine ) ).to.be.instanceOf( ImageStyleEngine ); + } ); - expect( schema.check( { name: 'image', attributes: [ 'imageStyle', 'src' ], inside: '$root' } ) ).to.be.true; + it( 'should load image engine', () => { + expect( editor.plugins.get( ImageEngine ) ).to.be.instanceOf( ImageEngine ); + } ); } ); - it( 'should register separate command for each style', () => { - expect( editor.commands.get( 'fullStyle' ) ).to.be.instanceOf( ImageStyleCommand ); - expect( editor.commands.get( 'sideStyle' ) ).to.be.instanceOf( ImageStyleCommand ); - expect( editor.commands.get( 'dummyStyle' ) ).to.be.instanceOf( ImageStyleCommand ); - } ); + describe( 'init', () => { + beforeEach( () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ], + image: { + styles: [ + { name: 'fullStyle', title: 'foo', icon: 'object-center', value: null }, + { name: 'sideStyle', title: 'bar', icon: 'object-right', value: 'side', className: 'side-class' }, + { name: 'dummyStyle', title: 'baz', icon: 'object-dummy', value: 'dummy', className: 'dummy-class' }, + ] + } + } ) + .then( newEditor => { + editor = newEditor; + document = editor.document; + viewDocument = editor.editing.view; + } ); + } ); - it( 'should convert from view to model', () => { - editor.setData( '
' ); + it( 'should define image.styles config', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ] + } ) + .then( newEditor => { + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ 'imageStyleFull', 'imageStyleSide' ] ); + } ); + } ); - expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' + - '' + - '
' ); - } ); + it( 'should set schema rules for image style', () => { + const schema = document.schema; - it( 'should not convert from view to model if class is not defined', () => { - editor.setData( '
' ); + expect( schema.check( { name: 'image', attributes: [ 'imageStyle', 'src' ], inside: '$root' } ) ).to.be.true; + } ); - expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + it( 'should register separate command for each style', () => { + expect( editor.commands.get( 'fullStyle' ) ).to.be.instanceOf( ImageStyleCommand ); + expect( editor.commands.get( 'sideStyle' ) ).to.be.instanceOf( ImageStyleCommand ); + expect( editor.commands.get( 'dummyStyle' ) ).to.be.instanceOf( ImageStyleCommand ); + } ); - it( 'should not convert from view to model when not in image figure', () => { - editor.setData( '
' ); + it( 'should convert from view to model', () => { + editor.setData( '
' ); - expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( '' ); - } ); + expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + + '' + + '
' ); + } ); - it( 'should not convert from view to model if schema prevents it', () => { - document.schema.disallow( { name: 'image', attributes: 'imageStyle' } ); - editor.setData( '
' ); + it( 'should not convert from view to model if class is not defined', () => { + editor.setData( '
' ); - expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); + } ); - it( 'should convert model to view: adding attribute', () => { - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + it( 'should not convert from view to model when not in image figure', () => { + editor.setData( '
' ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( '' ); } ); - expect( editor.getData() ).to.equal( '
' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + it( 'should not convert from view to model if schema prevents it', () => { + document.schema.disallow( { name: 'image', attributes: 'imageStyle' } ); + editor.setData( '
' ); + + expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); + } ); - it( 'should convert model to view: removing attribute', () => { - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + it( 'should convert model to view: adding attribute', () => { + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); + + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'side' ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', null ); + expect( editor.getData() ).to.equal( '
' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); } ); - expect( editor.getData() ).to.equal( '
' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + it( 'should convert model to view: removing attribute', () => { + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); - it( 'should convert model to view: change attribute', () => { - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', null ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + expect( editor.getData() ).to.equal( '
' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); } ); - expect( editor.getData() ).to.equal( '
' ); + it( 'should convert model to view: change attribute', () => { + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); + + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'side' ); + } ); + + expect( editor.getData() ).to.equal( '
' ); - // https://github.com/ckeditor/ckeditor5-image/issues/132 - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); + // https://github.com/ckeditor/ckeditor5-image/issues/132 + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'dummy' ); + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'dummy' ); + } ); + + expect( editor.getData() ).to.equal( '
' ); + + // https://github.com/ckeditor/ckeditor5-image/issues/132 + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); } ); - expect( editor.getData() ).to.equal( '
' ); + it( 'should not convert from model to view if already consumed: adding attribute', () => { + editor.editing.modelToView.on( 'addAttribute:imageStyle', ( evt, data, consumable ) => { + consumable.consume( data.item, 'addAttribute:imageStyle' ); + }, { priority: 'high' } ); - // https://github.com/ckeditor/ckeditor5-image/issues/132 - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); + + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'side' ); + } ); + + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); + } ); + + it( 'should not convert from model to view if already consumed: removing attribute', () => { + editor.editing.modelToView.on( 'removeAttribute:imageStyle', ( evt, data, consumable ) => { + consumable.consume( data.item, 'removeAttribute:imageStyle' ); + }, { priority: 'high' } ); - it( 'should not convert from model to view if already consumed: adding attribute', () => { - editor.editing.modelToView.on( 'addAttribute:imageStyle', ( evt, data, consumable ) => { - consumable.consume( data.item, 'addAttribute:imageStyle' ); - }, { priority: 'high' } ); + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', null ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); } ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + it( 'should not convert from model to view if already consumed: change attribute', () => { + editor.editing.modelToView.on( 'changeAttribute:imageStyle', ( evt, data, consumable ) => { + consumable.consume( data.item, 'changeAttribute:imageStyle' ); + }, { priority: 'high' } ); - it( 'should not convert from model to view if already consumed: removing attribute', () => { - editor.editing.modelToView.on( 'removeAttribute:imageStyle', ( evt, data, consumable ) => { - consumable.consume( data.item, 'removeAttribute:imageStyle' ); - }, { priority: 'high' } ); + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'side' ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', null ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); } ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + it( 'should not convert from model to view if style is not present: adding attribute', () => { + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); + + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'foo' ); + } ); - it( 'should not convert from model to view if already consumed: change attribute', () => { - editor.editing.modelToView.on( 'changeAttribute:imageStyle', ( evt, data, consumable ) => { - consumable.consume( data.item, 'changeAttribute:imageStyle' ); - }, { priority: 'high' } ); + expect( editor.getData() ).to.equal( '
' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); + } ); - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + it( 'should not convert from model to view if style is not present: change attribute', () => { + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', 'foo' ); + } ); + + expect( editor.getData() ).to.equal( '
' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); } ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); + it( 'should not convert from model to view if style is not present: remove attribute', () => { + setModelData( document, '' ); + const image = document.getRoot().getChild( 0 ); + const batch = document.batch(); + + document.enqueueChanges( () => { + batch.setAttribute( image, 'imageStyle', null ); + } ); + + expect( editor.getData() ).to.equal( '
' ); + expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( + '
' + ); + } ); } ); - it( 'should not convert from model to view if style is not present: adding attribute', () => { - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + describe( 'imageStyles()', () => { + it( 'should fall back to defaults when no image.styles', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ] + } ) + .then( newEditor => { + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ 'imageStyleFull', 'imageStyleSide' ] ); + } ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'foo' ); + it( 'should not alter the image.styles config', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ], + image: { + styles: [ + 'imageStyleSide' + ] + } + } ) + .then( newEditor => { + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ 'imageStyleSide' ] ); + } ); } ); - expect( editor.getData() ).to.equal( '
' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + it( 'should not alter object definitions in the image.styles config', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ], + image: { + styles: [ + { name: 'imageStyleSide' } + ] + } + } ) + .then( newEditor => { + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ { name: 'imageStyleSide' } ] ); + } ); + } ); + + it( 'should cache the styles', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ] + } ) + .then( newEditor => { + editor = newEditor; + plugin = editor.plugins.get( ImageStyleEngine ); + + 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, ImageStyleEngine ], + image: { + styles: [ + // Custom user styles. + { name: 'foo', title: 'foo', icon: 'custom', value: null, className: 'foo-class' }, + { name: 'bar', title: 'bar', icon: 'right', value: 'bar', className: 'bar-class' }, + { name: 'baz', title: 'Side image', icon: 'custom', value: 'baz', className: 'baz-class' }, + + // Customized default styles. + { name: 'imageStyleFull', icon: 'left', title: 'Custom title' } + ] + } + } ) + .then( newEditor => { + editor = newEditor; + plugin = editor.plugins.get( ImageStyleEngine ); + } ); + } ); + + it( 'should pass through if #name not found in default styles', () => { + expect( plugin.imageStyles[ 0 ] ).to.deep.equal( { + name: 'foo', + title: 'foo', + icon: 'custom', + value: null, + className: 'foo-class' + } ); + } ); - it( 'should not convert from model to view if style is not present: change attribute', () => { - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + it( 'should use one of default icons if #icon matches', () => { + expect( plugin.imageStyles[ 1 ].icon ).to.equal( ImageStyleEngine.defaultIcons.right ); + } ); + + it( 'should use one of default translations if #title matches', () => { + expect( plugin.imageStyles[ 2 ].title ).to.deep.equal( 'Default translation' ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'foo' ); + it( 'should extend one of default styles if #name matches', () => { + expect( plugin.imageStyles[ 3 ] ).to.deep.equal( { + name: 'imageStyleFull', + title: 'Custom title', + icon: ImageStyleEngine.defaultIcons.left, + value: null + } ); + } ); } ); - expect( editor.getData() ).to.equal( '
' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); - } ); + describe( 'string format', () => { + it( 'should use one of default styles if #name matches', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ], + image: { + styles: [ 'imageStyleFull' ] + } + } ) + .then( newEditor => { + editor = newEditor; + plugin = editor.plugins.get( ImageStyleEngine ); + expect( plugin.imageStyles[ 0 ] ).to.deep.equal( ImageStyleEngine.defaultStyles.imageStyleFull ); + } ); + } ); - it( 'should not convert from model to view if style is not present: remove attribute', () => { - setModelData( document, '' ); - const image = document.getRoot().getChild( 0 ); - const batch = document.batch(); + it( 'should warn if a #name not found in default styles', () => { + sinon.stub( log, 'warn' ); + + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ], + image: { + styles: [ 'foo' ] + } + } ) + .then( newEditor => { + editor = newEditor; + plugin = editor.plugins.get( ImageStyleEngine ); + + 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' } + ); + } ); + } ); + } ); + } ); - document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', null ); + describe( 'defaultStyles', () => { + it( 'should be defined', () => { + expect( ImageStyleEngine.defaultStyles ).to.deep.equal( { + imageStyleFull: { + name: 'imageStyleFull', + title: 'Full size image', + icon: centerIcon, + value: null + }, + imageStyleSide: { + name: 'imageStyleSide', + title: 'Side image', + icon: rightIcon, + value: 'side', + className: 'image-style-side' + }, + imageStyleLeft: { + name: 'imageStyleLeft', + title: 'Left aligned image', + icon: leftIcon, + value: 'left', + className: 'image-style-align-left' + }, + imageStyleCenter: { + name: 'imageStyleCenter', + title: 'Centered image', + icon: centerIcon, + value: 'side', + className: 'image-style-align-center' + }, + imageStyleRight: { + name: 'imageStyleRight', + title: 'Right aligned image', + icon: rightIcon, + value: 'right', + className: 'image-style-align-right' + } + } ); } ); + } ); - expect( editor.getData() ).to.equal( '
' ); - expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( - '
' - ); + describe( 'defaultIcons', () => { + it( 'should be defined', () => { + expect( ImageStyleEngine.defaultIcons ).to.deep.equal( { + left: leftIcon, + right: rightIcon, + center: centerIcon, + } ); + } ); } ); } ); From e0bdb7632ca8d8a39b82cbc0edc0a67167b2f619 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 13:37:40 +0200 Subject: [PATCH 02/16] Made ImageStyleEngine.localizedDefaultStylesTitles a public getter. --- src/imagestyle/imagestyleengine.js | 5 ++--- tests/imagestyle/imagestyleengine.js | 29 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index a9d76239..677394dd 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -85,7 +85,7 @@ export default class ImageStyleEngine extends Plugin { const styles = []; const editor = this.editor; - const titles = this._localizedStyleTitles; + const titles = this.localizedDefaultStylesTitles; const configuredStyles = editor.config.get( 'image.styles' ); for ( let style of configuredStyles ) { @@ -109,10 +109,9 @@ export default class ImageStyleEngine extends Plugin { * use in the {@link #imageStyles}. * * @readonly - * @private * @type {Object.} */ - get _localizedStyleTitles() { + get localizedDefaultStylesTitles() { const t = this.editor.t; return { diff --git a/tests/imagestyle/imagestyleengine.js b/tests/imagestyle/imagestyleengine.js index 4a65bb23..73a2e687 100644 --- a/tests/imagestyle/imagestyleengine.js +++ b/tests/imagestyle/imagestyleengine.js @@ -69,6 +69,8 @@ describe( 'ImageStyleEngine', () => { plugins: [ ImageStyleEngine ] } ) .then( newEditor => { + editor = newEditor; + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ 'imageStyleFull', 'imageStyleSide' ] ); } ); } ); @@ -286,6 +288,8 @@ describe( 'ImageStyleEngine', () => { plugins: [ ImageStyleEngine ] } ) .then( newEditor => { + editor = newEditor; + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ 'imageStyleFull', 'imageStyleSide' ] ); } ); } ); @@ -301,6 +305,8 @@ describe( 'ImageStyleEngine', () => { } } ) .then( newEditor => { + editor = newEditor; + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ 'imageStyleSide' ] ); } ); } ); @@ -316,6 +322,8 @@ describe( 'ImageStyleEngine', () => { } } ) .then( newEditor => { + editor = newEditor; + expect( newEditor.config.get( 'image.styles' ) ).to.deep.equal( [ { name: 'imageStyleSide' } ] ); } ); } ); @@ -434,6 +442,27 @@ describe( 'ImageStyleEngine', () => { } ); } ); + describe( 'localizedDefaultStylesTitles()', () => { + it( 'should return localized titles of default styles', () => { + return VirtualTestEditor + .create( { + plugins: [ ImageStyleEngine ] + } ) + .then( newEditor => { + editor = newEditor; + plugin = editor.plugins.get( ImageStyleEngine ); + + 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( ImageStyleEngine.defaultStyles ).to.deep.equal( { From c86097282621dd4ee47c170f58739e8f29a72f5a Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 13:45:35 +0200 Subject: [PATCH 03/16] Docs: Extended documentation of the ImageStyleEngine.localizedDefaultStylesTitles. --- src/imagestyle/imagestyleengine.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index 677394dd..123c44e2 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -108,6 +108,15 @@ export default class ImageStyleEngine 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/imagestyleengine~ImageStyleEngine.defaultStyles} are available: + * + * * `'Full size image'`, + * * `'Side image'`, + * * `'Left aligned image'`, + * * `'Centered image'`, + * * `'Right aligned image'` + * * @readonly * @type {Object.} */ From f83e2e0acce0af317c8ad8cc05c7d23622e4a7ff Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 13:49:45 +0200 Subject: [PATCH 04/16] Docs: Updated ImageConfig#styles with the new config syntax. --- src/imagestyle.js | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/imagestyle.js b/src/imagestyle.js index b74abd68..62ae7e37 100644 --- a/src/imagestyle.js +++ b/src/imagestyle.js @@ -80,6 +80,39 @@ export default class ImageStyle extends Plugin { * * The default value is: * + * const imageConfig = { + * styles: [ 'imageStyleFull', 'imageStyleSide' ] + * }; + * + * which configures two default styles: + * + * * the "full" style which doesn't apply any class, e.g. for images styled to span 100% width of the content, + * * the "side" style with the `.image-style-side` CSS class. + * + * See {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine.defaultStyles} to learn more about default + * styles provided by the image feature. + * + * The {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine.defaultStyles default styles} can be customized, + * e.g. to change the icon, title or CSS class of the style. The feature also provides several + * {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine.defaultIcons default icons} to chose from. + * + * import customIcon from 'custom-icon.svg'; + * + * // ... + * + * const imageConfig = { + * styles: [ + * // This will only customize the icon of the "full" style. + * // Note: 'right' is one of default icons provided by the feature. + * { name: 'imageStyleFull', icon: 'right' }, + * + * // This will customize the icon, title and CSS class of the default "side" style. + * { name: 'imageStyleSide', icon: customIcon, title: 'My side style', class: 'custom-side-image' } + * ] + * }; + * + * If none of the default styles is good enough, it is possible to define independent custom styles too: + * * import fullSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg'; * import sideIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg'; * @@ -87,15 +120,16 @@ export default class ImageStyle extends Plugin { * * const imageConfig = { * styles: [ - * // Option which defines a style which doesn't apply any class. - * // The style is titled "full" because such images are often styled to take 100% width of the content. - * { name: 'imageStyleFull', title: t( 'Full size image' ), icon: fullSizeIcon, value: null }, + * // A completely custom full size style with no class. + * { name: 'fullSize', title: 'Full size', icon: fullSizeIcon, value: null }, * - * // Option which represents a side image. - * { name: 'imageStyleSide', title: t( 'Side image' ), icon: sideIcon, value: 'side', className: 'image-style-side' } + * { name: 'side', title: 'To the side', icon: sideIcon, value: 'side', className: 'side-image' } * ] * }; * + * Note: Setting `title` to one of {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine#localizedDefaultStylesTitles} + * will automatically translate it to the language of the editor. + * * Read more about styling images in the {@linkTODO Image styles guide}. * * The feature creates commands based on defined styles, so you can change the style of a selected image by executing From 4991d22857387a05ae701f74c0a4feda0144db76 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 13:52:01 +0200 Subject: [PATCH 05/16] Code refactoring: Renamed certain styles in ImageStyleEngine.defaultStyles. --- src/imagestyle/imagestyleengine.js | 18 +++++++++--------- tests/imagestyle/imagestyleengine.js | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index 123c44e2..3e925db1 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -144,9 +144,9 @@ export default class ImageStyleEngine extends Plugin { * * There are also 3 styles focused on formatting: * - * * `imageStyleLeft` aligns the image to the left using the `image-style-align-left` class, - * * `imageStyleCenter` centers the image to the left using the `image-style-align-center` class, - * * `imageStyleRight` aligns the image to the right using the `image-style-align-right` class, + * * `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.} */ @@ -169,8 +169,8 @@ ImageStyleEngine.defaultStyles = { }, // This style represents an imaged aligned to the left. - imageStyleLeft: { - name: 'imageStyleLeft', + imageStyleAlignLeft: { + name: 'imageStyleAlignLeft', title: 'Left aligned image', icon: leftIcon, value: 'left', @@ -178,8 +178,8 @@ ImageStyleEngine.defaultStyles = { }, // This style represents a centered imaged. - imageStyleCenter: { - name: 'imageStyleCenter', + imageStyleAlignCenter: { + name: 'imageStyleAlignCenter', title: 'Centered image', icon: centerIcon, value: 'side', @@ -187,8 +187,8 @@ ImageStyleEngine.defaultStyles = { }, // This style represents an imaged aligned to the right. - imageStyleRight: { - name: 'imageStyleRight', + imageStyleAlignRight: { + name: 'imageStyleAlignRight', title: 'Right aligned image', icon: rightIcon, value: 'right', diff --git a/tests/imagestyle/imagestyleengine.js b/tests/imagestyle/imagestyleengine.js index 73a2e687..32a7f76f 100644 --- a/tests/imagestyle/imagestyleengine.js +++ b/tests/imagestyle/imagestyleengine.js @@ -479,22 +479,22 @@ describe( 'ImageStyleEngine', () => { value: 'side', className: 'image-style-side' }, - imageStyleLeft: { - name: 'imageStyleLeft', + imageStyleAlignLeft: { + name: 'imageStyleAlignLeft', title: 'Left aligned image', icon: leftIcon, value: 'left', className: 'image-style-align-left' }, - imageStyleCenter: { - name: 'imageStyleCenter', + imageStyleAlignCenter: { + name: 'imageStyleAlignCenter', title: 'Centered image', icon: centerIcon, value: 'side', className: 'image-style-align-center' }, - imageStyleRight: { - name: 'imageStyleRight', + imageStyleAlignRight: { + name: 'imageStyleAlignRight', title: 'Right aligned image', icon: rightIcon, value: 'right', From 87e9aa028e1f874013d8880736276532ce113ae3 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 14:01:02 +0200 Subject: [PATCH 06/16] Docs: Simplified the 'Configuring Image Styles' feature overview. --- docs/_snippets/features/image-style-custom.js | 29 +++------------ .../features/image-style-custom.scss | 4 +-- docs/features/image.md | 35 ++++--------------- 3 files changed, 13 insertions(+), 55 deletions(-) diff --git a/docs/_snippets/features/image-style-custom.js b/docs/_snippets/features/image-style-custom.js index 600dca4d..3652af8a 100644 --- a/docs/_snippets/features/image-style-custom.js +++ b/docs/_snippets/features/image-style-custom.js @@ -7,42 +7,21 @@ import './image-style-custom.scss'; -import fullSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg'; -import alignLeftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg'; -import alignRightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg'; - ClassicEditor .create( document.querySelector( '#snippet-image-style-custom' ), { image: { styles: [ // This option is equal to a situation where no style is applied. - { - name: 'imageStyleFull', - title: 'Full size image', - icon: fullSizeIcon, - value: null - }, + 'imageStyleFull', // This represents an image aligned to left. - { - name: 'imageStyleLeft', - title: 'Left aligned image', - icon: alignLeftIcon, - value: 'left', - className: 'image-style-left' - }, + 'imageStyleAlignLeft', // This represents an image aligned to right. - { - name: 'imageStyleRight', - title: 'Right aligned image', - icon: alignRightIcon, - value: 'right', - className: 'image-style-right' - } + 'imageStyleAlignRight' ], - toolbar: [ 'imageTextAlternative', '|', 'imageStyleLeft', 'imageStyleFull', 'imageStyleRight' ] + toolbar: [ 'imageTextAlternative', '|', 'imageStyleAlignLeft', 'imageStyleFull', 'imageStyleAlignRight' ] } } ) .then( editor => { diff --git a/docs/_snippets/features/image-style-custom.scss b/docs/_snippets/features/image-style-custom.scss index f141c701..ca2dcd0b 100644 --- a/docs/_snippets/features/image-style-custom.scss +++ b/docs/_snippets/features/image-style-custom.scss @@ -1,13 +1,13 @@ // Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. // For licensing, see LICENSE.md or http://ckeditor.com/license -.image-style-left { +.image-style-align-left { float: left; width: 50%; margin: 1em 1em 1em 0; } -.image-style-right { +.image-style-align-right { float: right; width: 50%; margin: 1em 0 1em 1em; diff --git a/docs/features/image.md b/docs/features/image.md index 6eaf3859..92de1d05 100644 --- a/docs/features/image.md +++ b/docs/features/image.md @@ -111,45 +111,24 @@ Below you can see a demo of the editor with the image styles feature enabled. Th The available image styles can be configured using the {@link module:image/image~ImageConfig#styles `image.styles`} option. -The following editor supports the default style plus left- and right-aligned images: +The following editor supports the default full style plus left- and right-aligned images: ```js -import fullSizeIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg'; -import alignLeftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg'; -import alignRightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg'; - ClassicEditor .create( document.querySelector( '#editor' ), { image: { // You need to configure the image toolbar too, so it uses the new style buttons. - toolbar: [ 'imageTextAlternative', '|', 'imageStyleLeft', 'imageStyleFull', 'imageStyleRight' ], + toolbar: [ 'imageTextAlternative', '|', 'imageStyleAlignLeft', 'imageStyleFull', 'imageStyleAlignRight' ], styles: [ // This option is equal to a situation where no style is applied. - { - name: 'imageStyleFull', - title: 'Full size image', - icon: fullSizeIcon, - value: null - }, + 'imageStyleFull', // This represents an image aligned to left. - { - name: 'imageStyleLeft', - title: 'Left aligned image', - icon: alignLeftIcon, - value: 'left', - className: 'image-style-left' - }, + 'imageStyleAlignLeft', // This represents an image aligned to right. - { - name: 'imageStyleRight', - title: 'Right aligned image', - icon: alignRightIcon, - value: 'right', - className: 'image-style-right' - } + 'imageStyleAlignRight' ] } } ) @@ -158,13 +137,13 @@ ClassicEditor ``` ```css -.image-style-left { +.image-style-align-left { float: left; width: 50%; margin: 1em 1em 1em 0; } -.image-style-right { +.image-style-align-right { float: right; width: 50%; margin: 1em 0 1em 1em; From f654cfa79f48a8fcc6548fb71e100b87da2cc984 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 14:19:39 +0200 Subject: [PATCH 07/16] Added styles for left-aligned, right-aligned and centered image. --- theme/theme.scss | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/theme/theme.scss b/theme/theme.scss index 89803bba..01d8999a 100644 --- a/theme/theme.scss +++ b/theme/theme.scss @@ -1,15 +1,41 @@ // Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. // For licensing, see LICENSE.md or http://ckeditor.com/license +@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_spacing'; + +$ck-image-spacing: ck-spacing( 'standard' ); +$ck-image-max-width: 50%; + .ck-editor__editable { .image { text-align: center; clear: both; + &.image-style-side, + &.image-style-align-left, + &.image-style-align-center, + &.image-style-align-right { + max-width: $ck-image-max-width; + } + &.image-style-side { float: right; - margin-left: 0.8em; - max-width: 50%; + margin-left: $ck-image-spacing; + } + + &.image-style-align-left { + float: left; + margin-right: $ck-image-spacing; + } + + &.image-style-align-center { + margin-left: auto; + margin-right: auto; + } + + &.image-style-align-right { + float: right; + margin-left: $ck-image-spacing; } } From 9df032f456ab7367ec777a1392f94ccebc7ec41a Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 14:20:09 +0200 Subject: [PATCH 08/16] Tests: Updated the image style manual test with formatting styles. --- tests/manual/caption.js | 3 ++- tests/manual/imagestyle.html | 12 +++++++++++- tests/manual/imagestyle.js | 30 ++++++++++++++++++++++++++++-- tests/manual/imagestyle.md | 7 +++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/manual/caption.js b/tests/manual/caption.js index 876c60fb..9dcb459b 100644 --- a/tests/manual/caption.js +++ b/tests/manual/caption.js @@ -28,7 +28,8 @@ ClassicEditor ], toolbar: [ 'headings', 'undo', 'redo', 'bold', 'italic', 'bulletedList', 'numberedList' ], image: { - toolbar: [ 'imageStyleFull', 'imageStyleSide', '|', 'imageTextAlternative' ] + styles: [ 'imageStyleFull', 'imageStyleAlignCenter' ], + toolbar: [ 'imageStyleFull', 'imageStyleAlignCenter', '|', 'imageTextAlternative' ] } } ) .then( editor => { diff --git a/tests/manual/imagestyle.html b/tests/manual/imagestyle.html index 204ae70e..3a625515 100644 --- a/tests/manual/imagestyle.html +++ b/tests/manual/imagestyle.html @@ -1,10 +1,20 @@ -
+

Semantic–oriented images

+ +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

+
+ +

Formatting–oriented images

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

+
+ +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

diff --git a/tests/manual/imagestyle.js b/tests/manual/imagestyle.js index 5510e922..2f931393 100644 --- a/tests/manual/imagestyle.js +++ b/tests/manual/imagestyle.js @@ -17,7 +17,7 @@ import ImageStyle from '../../src/imagestyle'; import ImageToolbar from '../../src/imagetoolbar'; ClassicEditor - .create( document.querySelector( '#editor' ), { + .create( document.querySelector( '#editor-semantic' ), { plugins: [ ImageToolbar, EnterPlugin, @@ -35,7 +35,33 @@ ClassicEditor } } ) .then( editor => { - window.editor = editor; + window.editorSemantic = editor; + } ) + .catch( err => { + console.error( err.stack ); + } ); + +ClassicEditor + .create( document.querySelector( '#editor-formatting' ), { + plugins: [ + ImageToolbar, + EnterPlugin, + TypingPlugin, + ParagraphPlugin, + HeadingPlugin, + ImagePlugin, + UndoPlugin, + ClipboardPlugin, + ImageStyle + ], + toolbar: [ 'headings', 'undo', 'redo' ], + image: { + styles: [ 'imageStyleAlignLeft', 'imageStyleAlignCenter', 'imageStyleAlignRight' ], + toolbar: [ 'imageStyleAlignLeft', 'imageStyleAlignCenter', 'imageStyleAlignRight' ] + } + } ) + .then( editor => { + window.editorFormatting = editor; } ) .catch( err => { console.error( err.stack ); diff --git a/tests/manual/imagestyle.md b/tests/manual/imagestyle.md index e7403a3b..51e6178f 100644 --- a/tests/manual/imagestyle.md +++ b/tests/manual/imagestyle.md @@ -1,6 +1,13 @@ ## ImageStyle feature +### The "semantic–oriented" editor + * Click on image - toolbar with icons should appear. "Full size image" icon should be selected. * Click on "Side image" icon. Image should be aligned to right. * Click on "Full size image" icon. Image should be back to its original state. * Resize the browser window so the scrollbar is visible. Click on image and scroll editor contents - check if toolbar is placed in proper position. + +### The "formatting–oriented" editor + +* Click on image - toolbar with icons should appear. "Centered image" icon should be selected. +* Check if other icons icons change the alignment of the image. From 84433965107fa6fb28cdece29318b1374db628c6 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 14:30:49 +0200 Subject: [PATCH 09/16] Made the ImageStyleEngine use the object-center and object-full-width icons in different cases. --- src/imagestyle/imagestyleengine.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index 3e925db1..51df0f2e 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -13,6 +13,7 @@ import ImageEngine from '../image/imageengine'; 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'; @@ -155,7 +156,7 @@ ImageStyleEngine.defaultStyles = { imageStyleFull: { name: 'imageStyleFull', title: 'Full size image', - icon: centerIcon, + icon: fullWidthIcon, value: null }, @@ -200,11 +201,12 @@ ImageStyleEngine.defaultStyles = { * Default image style icons provided by the plugin, which can be referred in the * {@link module:image/image~ImageConfig#styles} config. * - * There are 3 icons available: `'left'`, `'center'` and `'right'`. + * There are 3 icons available: `'full'`, `'left'`, `'center'` and `'right'`. * * @member {Object.} */ ImageStyleEngine.defaultIcons = { + full: fullWidthIcon, left: leftIcon, right: rightIcon, center: centerIcon, @@ -269,12 +271,12 @@ function normalizeStyle( style ) { /** * Image style format descriptor. * - * import fullIcon from 'path/to/icon.svg`; + * import fullWidthIcon from 'path/to/icon.svg`; * * const imageStyleFormat = { * name: 'fullSizeImage', * value: 'full', - * icon: fullIcon, + * icon: fullWidthIcon, * title: 'Full size image', * class: 'image-full-size' * } From 3dfa4b560077d81d74ff04adc52c34f60d5a75ed Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 13 Sep 2017 15:43:12 +0200 Subject: [PATCH 10/16] Tests: Updated ImageStyleEngine test to assert the usage of the object-full-width icon. --- tests/imagestyle/imagestyleengine.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/imagestyle/imagestyleengine.js b/tests/imagestyle/imagestyleengine.js index 32a7f76f..f3922943 100644 --- a/tests/imagestyle/imagestyleengine.js +++ b/tests/imagestyle/imagestyleengine.js @@ -12,6 +12,7 @@ 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'; @@ -469,7 +470,7 @@ describe( 'ImageStyleEngine', () => { imageStyleFull: { name: 'imageStyleFull', title: 'Full size image', - icon: centerIcon, + icon: fullWidthIcon, value: null }, imageStyleSide: { @@ -507,6 +508,7 @@ describe( 'ImageStyleEngine', () => { describe( 'defaultIcons', () => { it( 'should be defined', () => { expect( ImageStyleEngine.defaultIcons ).to.deep.equal( { + full: fullWidthIcon, left: leftIcon, right: rightIcon, center: centerIcon, From 47e24ab9af4937f2aa04a12c3315b8e0f3bb5f31 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Thu, 14 Sep 2017 10:12:28 +0200 Subject: [PATCH 11/16] Tests: Fixed unnecessary changes to the image caption manual test. --- tests/manual/caption.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/manual/caption.js b/tests/manual/caption.js index 9dcb459b..876c60fb 100644 --- a/tests/manual/caption.js +++ b/tests/manual/caption.js @@ -28,8 +28,7 @@ ClassicEditor ], toolbar: [ 'headings', 'undo', 'redo', 'bold', 'italic', 'bulletedList', 'numberedList' ], image: { - styles: [ 'imageStyleFull', 'imageStyleAlignCenter' ], - toolbar: [ 'imageStyleFull', 'imageStyleAlignCenter', '|', 'imageTextAlternative' ] + toolbar: [ 'imageStyleFull', 'imageStyleSide', '|', 'imageTextAlternative' ] } } ) .then( editor => { From 1f02a5e0bf81a788caecbbd60d9ab47e1749c277 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 19 Sep 2017 13:23:27 +0200 Subject: [PATCH 12/16] Added static pluginName getter to ImageStyleEngine. Minor docs fixes. --- src/imagestyle/imagestyleengine.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index 51df0f2e..45dbb1e4 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -32,6 +32,13 @@ export default class ImageStyleEngine extends Plugin { return [ ImageEngine ]; } + /** + * @inheritDoc + */ + static get pluginName() { + return 'ImageStyleEngine'; + } + /** * @inheritDoc */ @@ -278,7 +285,7 @@ function normalizeStyle( style ) { * value: 'full', * icon: fullWidthIcon, * title: 'Full size image', - * class: 'image-full-size' + * className: 'image-full-size' * } * * @typedef {Object} module:image/imagestyle/imagestyleengine~ImageStyleFormat From 1285710e268103f85e546ab3ef1af67ffb87256c Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Tue, 19 Sep 2017 13:38:09 +0200 Subject: [PATCH 13/16] Increased default margin for side, left and right-aligned images for better interaction with default list styles. --- theme/theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/theme.scss b/theme/theme.scss index 01d8999a..7dbf7260 100644 --- a/theme/theme.scss +++ b/theme/theme.scss @@ -3,7 +3,7 @@ @import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_spacing'; -$ck-image-spacing: ck-spacing( 'standard' ); +$ck-image-spacing: 5 * ck-spacing( 'small' ); $ck-image-max-width: 50%; .ck-editor__editable { From ad1a540a6a097b292a46944b4779a779da95ed88 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 20 Sep 2017 12:08:23 +0200 Subject: [PATCH 14/16] Removed #value from ImageStyleFormat to simplify configuration. Using #name instead. Defined ImageStyleFormat#isDefault. --- src/imagestyle.js | 6 ++-- src/imagestyle/converters.js | 16 +++++------ src/imagestyle/imagestylecommand.js | 6 ++-- src/imagestyle/imagestyleengine.js | 16 ++++------- tests/imagestyle.js | 6 ++-- tests/imagestyle/imagestylecommand.js | 14 ++++----- tests/imagestyle/imagestyleengine.js | 41 +++++++++++++-------------- 7 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/imagestyle.js b/src/imagestyle.js index 62ae7e37..3aed8042 100644 --- a/src/imagestyle.js +++ b/src/imagestyle.js @@ -120,10 +120,10 @@ export default class ImageStyle extends Plugin { * * const imageConfig = { * styles: [ - * // A completely custom full size style with no class. - * { name: 'fullSize', title: 'Full size', icon: fullSizeIcon, value: null }, + * // A completely custom full size style with no class, used as a default. + * { name: 'fullSize', title: 'Full size', icon: fullSizeIcon, isDefault: true }, * - * { name: 'side', title: 'To the side', icon: sideIcon, value: 'side', className: 'side-image' } + * { name: 'side', title: 'To the side', icon: sideIcon, className: 'side-image' } * ] * }; * diff --git a/src/imagestyle/converters.js b/src/imagestyle/converters.js index f12577f8..9dbba11a 100644 --- a/src/imagestyle/converters.js +++ b/src/imagestyle/converters.js @@ -26,8 +26,8 @@ export function modelToViewStyleAttribute( styles ) { } // Check if there is class name associated with given value. - const newStyle = getStyleByValue( data.attributeNewValue, styles ); - const oldStyle = getStyleByValue( data.attributeOldValue, styles ); + const newStyle = getStyleByName( data.attributeNewValue, styles ); + const oldStyle = getStyleByName( data.attributeOldValue, styles ); const viewElement = conversionApi.mapper.toViewElement( data.item ); const isRemovalHandled = handleRemoval( eventType, oldStyle, viewElement ); @@ -48,7 +48,7 @@ export function modelToViewStyleAttribute( styles ) { */ export function viewToModelStyleAttribute( styles ) { // Convert only styles without `null` value. - const filteredStyles = styles.filter( style => style.value !== null ); + const filteredStyles = styles.filter( style => !style.isDefault ); return ( evt, data, consumable, conversionApi ) => { for ( const style of filteredStyles ) { @@ -88,17 +88,17 @@ function viewToModelImageStyle( style, data, consumable, conversionApi ) { // *** Step2: Convert to model. consumable.consume( viewFigureElement, { class: style.className } ); - modelImageElement.setAttribute( 'imageStyle', style.value ); + modelImageElement.setAttribute( 'imageStyle', style.name ); } -// Returns style with given `value` from array of styles. +// Returns style with given `name` from array of styles. // -// @param {String} value +// @param {String} name // @param {Array. } styles // @return {module:image/imagestyle/imagestyleengine~ImageStyleFormat|undefined} -function getStyleByValue( value, styles ) { +function getStyleByName( name, styles ) { for ( const style of styles ) { - if ( style.value === value ) { + if ( style.name === name ) { return style; } } diff --git a/src/imagestyle/imagestylecommand.js b/src/imagestyle/imagestylecommand.js index 3d7834ef..3a9dd3d7 100644 --- a/src/imagestyle/imagestylecommand.js +++ b/src/imagestyle/imagestylecommand.js @@ -53,10 +53,10 @@ export default class ImageStyleCommand extends Command { if ( !element ) { this.value = false; - } else if ( this.style.value === null ) { + } else if ( this.style.isDefault ) { this.value = !element.hasAttribute( 'imageStyle' ); } else { - this.value = ( element.getAttribute( 'imageStyle' ) == this.style.value ); + this.value = ( element.getAttribute( 'imageStyle' ) == this.style.name ); } } @@ -79,7 +79,7 @@ export default class ImageStyleCommand extends Command { doc.enqueueChanges( () => { const batch = options.batch || doc.batch(); - batch.setAttribute( imageElement, 'imageStyle', this.style.value ); + batch.setAttribute( imageElement, 'imageStyle', this.style.name ); } ); } } diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index 45dbb1e4..f68c5c48 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -164,7 +164,7 @@ ImageStyleEngine.defaultStyles = { name: 'imageStyleFull', title: 'Full size image', icon: fullWidthIcon, - value: null + isDefault: true }, // This represents side image. @@ -172,7 +172,6 @@ ImageStyleEngine.defaultStyles = { name: 'imageStyleSide', title: 'Side image', icon: rightIcon, - value: 'side', className: 'image-style-side' }, @@ -181,7 +180,6 @@ ImageStyleEngine.defaultStyles = { name: 'imageStyleAlignLeft', title: 'Left aligned image', icon: leftIcon, - value: 'left', className: 'image-style-align-left' }, @@ -190,7 +188,6 @@ ImageStyleEngine.defaultStyles = { name: 'imageStyleAlignCenter', title: 'Centered image', icon: centerIcon, - value: 'side', className: 'image-style-align-center' }, @@ -199,7 +196,6 @@ ImageStyleEngine.defaultStyles = { name: 'imageStyleAlignRight', title: 'Right aligned image', icon: rightIcon, - value: 'right', className: 'image-style-align-right' } }; @@ -282,18 +278,18 @@ function normalizeStyle( style ) { * * const imageStyleFormat = { * name: 'fullSizeImage', - * value: 'full', * icon: fullWidthIcon, * title: 'Full size image', * className: 'image-full-size' * } * * @typedef {Object} module:image/imagestyle/imagestyleengine~ImageStyleFormat - * @property {String} name The name of the style. It will be used to: + * @property {String} name The unique name of the style. It will be used to: * * register the {@link module:core/command~Command command} which will apply this style, - * * store the style's button in the editor {@link module:ui/componentfactory~ComponentFactory}. - * @property {String} value A value used to store this style in the model attribute. - * When the value is `null`, the style will be used as the default one. A default style does not apply any CSS class to the view element. + * * store the style's button in the editor {@link module:ui/componentfactory~ComponentFactory}, + * * store the style in the `imageStyle` model attribute. + * @property {Boolean} [isDefault] When set, the style will be used as the default one. + * A default style does not apply any CSS class to the view element. * @property {String} icon One of the following to be used when creating the style's button: * * An SVG icon source (as an XML string), * * One of {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine.defaultIcons} to use a default icon provided by the plugin. diff --git a/tests/imagestyle.js b/tests/imagestyle.js index 02951c0c..89621903 100644 --- a/tests/imagestyle.js +++ b/tests/imagestyle.js @@ -12,9 +12,9 @@ import global from '@ckeditor/ckeditor5-utils/src/dom/global'; describe( 'ImageStyle', () => { let editor; const styles = [ - { name: 'style 1', title: 'Style 1 title', icon: 'style1-icon', value: null }, - { name: 'style 2', title: 'Style 2 title', icon: 'style2-icon', value: 'style2', cssClass: 'style2-class' }, - { name: 'style 3', title: 'Style 3 title', icon: 'style3-icon', value: 'style3', cssClass: 'style3-class' } + { 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' }, + { name: 'style 3', title: 'Style 3 title', icon: 'style3-icon', cssClass: 'style3-class' } ]; beforeEach( () => { diff --git a/tests/imagestyle/imagestylecommand.js b/tests/imagestyle/imagestylecommand.js index 92fb8655..e3a795b4 100644 --- a/tests/imagestyle/imagestylecommand.js +++ b/tests/imagestyle/imagestylecommand.js @@ -8,8 +8,8 @@ import ImageStyleCommand from '../../src/imagestyle/imagestylecommand'; import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'ImageStyleCommand', () => { - const defaultStyle = { name: 'defaultStyle', title: 'foo bar', icon: 'icon-1', value: null }; - const otherStyle = { name: 'otherStyle', title: 'baz', icon: 'icon-2', value: 'other', className: 'other-class-name' }; + const defaultStyle = { name: 'defaultStyle', title: 'foo bar', icon: 'icon-1', isDefault: true }; + const otherStyle = { name: 'otherStyle', title: 'baz', icon: 'icon-2', className: 'other-class-name' }; let document, defaultStyleCommand, otherStyleCommand; @@ -44,7 +44,7 @@ describe( 'ImageStyleCommand', () => { } ); it( 'proper command should have true value when imageStyle attribute is present', () => { - setData( document, '[]' ); + setData( document, '[]' ); expect( defaultStyleCommand.value ).to.be.false; expect( otherStyleCommand.value ).to.be.true; @@ -62,15 +62,15 @@ describe( 'ImageStyleCommand', () => { otherStyleCommand.execute(); - expect( getData( document ) ).to.equal( '[]' ); + expect( getData( document ) ).to.equal( '[]' ); } ); it( 'should do nothing when attribute already present', () => { - setData( document, '[]' ); + setData( document, '[]' ); otherStyleCommand.execute(); - expect( getData( document ) ).to.equal( '[]' ); + expect( getData( document ) ).to.equal( '[]' ); } ); it( 'should allow to provide batch instance', () => { @@ -81,7 +81,7 @@ describe( 'ImageStyleCommand', () => { otherStyleCommand.execute( { batch } ); - expect( getData( document ) ).to.equal( '[]' ); + expect( getData( document ) ).to.equal( '[]' ); sinon.assert.calledOnce( spy ); } ); diff --git a/tests/imagestyle/imagestyleengine.js b/tests/imagestyle/imagestyleengine.js index f3922943..c360d64a 100644 --- a/tests/imagestyle/imagestyleengine.js +++ b/tests/imagestyle/imagestyleengine.js @@ -51,9 +51,9 @@ describe( 'ImageStyleEngine', () => { plugins: [ ImageStyleEngine ], image: { styles: [ - { name: 'fullStyle', title: 'foo', icon: 'object-center', value: null }, - { name: 'sideStyle', title: 'bar', icon: 'object-right', value: 'side', className: 'side-class' }, - { name: 'dummyStyle', title: 'baz', icon: 'object-dummy', value: 'dummy', className: 'dummy-class' }, + { name: 'fullStyle', title: 'foo', icon: 'object-center', isDefault: true }, + { name: 'sideStyle', title: 'bar', icon: 'object-right', className: 'side-class' }, + { name: 'dummyStyle', title: 'baz', icon: 'object-dummy', className: 'dummy-class' }, ] } } ) @@ -91,7 +91,8 @@ describe( 'ImageStyleEngine', () => { it( 'should convert from view to model', () => { editor.setData( '
' ); - expect( getModelData( document, { withoutSelection: true } ) ).to.equal( '' ); + expect( getModelData( document, { withoutSelection: true } ) ) + .to.equal( '' ); expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( '
' + '' + @@ -130,7 +131,7 @@ describe( 'ImageStyleEngine', () => { const batch = document.batch(); document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + batch.setAttribute( image, 'imageStyle', 'sideStyle' ); } ); expect( editor.getData() ).to.equal( '
' ); @@ -140,7 +141,7 @@ describe( 'ImageStyleEngine', () => { } ); it( 'should convert model to view: removing attribute', () => { - setModelData( document, '' ); + setModelData( document, '' ); const image = document.getRoot().getChild( 0 ); const batch = document.batch(); @@ -160,7 +161,7 @@ describe( 'ImageStyleEngine', () => { const batch = document.batch(); document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + batch.setAttribute( image, 'imageStyle', 'sideStyle' ); } ); expect( editor.getData() ).to.equal( '
' ); @@ -171,7 +172,7 @@ describe( 'ImageStyleEngine', () => { ); document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'dummy' ); + batch.setAttribute( image, 'imageStyle', 'dummyStyle' ); } ); expect( editor.getData() ).to.equal( '
' ); @@ -192,7 +193,7 @@ describe( 'ImageStyleEngine', () => { const batch = document.batch(); document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + batch.setAttribute( image, 'imageStyle', 'sideStyle' ); } ); expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( @@ -205,7 +206,7 @@ describe( 'ImageStyleEngine', () => { consumable.consume( data.item, 'removeAttribute:imageStyle' ); }, { priority: 'high' } ); - setModelData( document, '' ); + setModelData( document, '' ); const image = document.getRoot().getChild( 0 ); const batch = document.batch(); @@ -223,12 +224,12 @@ describe( 'ImageStyleEngine', () => { consumable.consume( data.item, 'changeAttribute:imageStyle' ); }, { priority: 'high' } ); - setModelData( document, '' ); + setModelData( document, '' ); const image = document.getRoot().getChild( 0 ); const batch = document.batch(); document.enqueueChanges( () => { - batch.setAttribute( image, 'imageStyle', 'side' ); + batch.setAttribute( image, 'imageStyle', 'sideStyle' ); } ); expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( @@ -356,9 +357,9 @@ describe( 'ImageStyleEngine', () => { image: { styles: [ // Custom user styles. - { name: 'foo', title: 'foo', icon: 'custom', value: null, className: 'foo-class' }, - { name: 'bar', title: 'bar', icon: 'right', value: 'bar', className: 'bar-class' }, - { name: 'baz', title: 'Side image', icon: 'custom', value: 'baz', className: 'baz-class' }, + { 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' } @@ -376,7 +377,7 @@ describe( 'ImageStyleEngine', () => { name: 'foo', title: 'foo', icon: 'custom', - value: null, + isDefault: true, className: 'foo-class' } ); } ); @@ -394,7 +395,7 @@ describe( 'ImageStyleEngine', () => { name: 'imageStyleFull', title: 'Custom title', icon: ImageStyleEngine.defaultIcons.left, - value: null + isDefault: true } ); } ); } ); @@ -471,34 +472,30 @@ describe( 'ImageStyleEngine', () => { name: 'imageStyleFull', title: 'Full size image', icon: fullWidthIcon, - value: null + isDefault: true }, imageStyleSide: { name: 'imageStyleSide', title: 'Side image', icon: rightIcon, - value: 'side', className: 'image-style-side' }, imageStyleAlignLeft: { name: 'imageStyleAlignLeft', title: 'Left aligned image', icon: leftIcon, - value: 'left', className: 'image-style-align-left' }, imageStyleAlignCenter: { name: 'imageStyleAlignCenter', title: 'Centered image', icon: centerIcon, - value: 'side', className: 'image-style-align-center' }, imageStyleAlignRight: { name: 'imageStyleAlignRight', title: 'Right aligned image', icon: rightIcon, - value: 'right', className: 'image-style-align-right' } } ); From c8a2edcf938c403f98b2bc951bbe899b552c7e4f Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 20 Sep 2017 12:14:59 +0200 Subject: [PATCH 15/16] Docs: Fixed outdated comment in the viewToModelStyleAttribute helper. --- src/imagestyle/converters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imagestyle/converters.js b/src/imagestyle/converters.js index 9dbba11a..7124573e 100644 --- a/src/imagestyle/converters.js +++ b/src/imagestyle/converters.js @@ -47,7 +47,7 @@ export function modelToViewStyleAttribute( styles ) { * @returns {Function} A view-to-model converter. */ export function viewToModelStyleAttribute( styles ) { - // Convert only styles without `null` value. + // Convert only non–default styles. const filteredStyles = styles.filter( style => !style.isDefault ); return ( evt, data, consumable, conversionApi ) => { From 463a818115663196489ee6be68bc564de78fa61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Kup=C5=9B?= Date: Thu, 21 Sep 2017 16:27:36 +0200 Subject: [PATCH 16/16] Minor docs fix. --- src/imagestyle/imagestyleengine.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/imagestyle/imagestyleengine.js b/src/imagestyle/imagestyleengine.js index f68c5c48..06036007 100644 --- a/src/imagestyle/imagestyleengine.js +++ b/src/imagestyle/imagestyleengine.js @@ -232,8 +232,7 @@ function normalizeStyle( style ) { if ( defaultStyles[ style ] ) { style = Object.assign( {}, defaultStyles[ style ] ); } - // If it's just a name but non of the defaults, warn because probably it's a mistake. - // A which has just a name makes a little sense for the plugin. + // 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.',