8
0
Quellcode durchsuchen

Changed: Rename alignment commands and buttons.

Maciej Gołaszewski vor 7 Jahren
Ursprung
Commit
e3692bce4b

+ 1 - 1
packages/ckeditor5-alignment/docs/_snippets/features/custom-text-alignment-toolbar.js

@@ -11,7 +11,7 @@ ClassicEditor
 	.create( document.querySelector( '#snippet-custom-text-alignment-toolbar' ), {
 		toolbar: {
 			items: [
-				'headings', '|', 'alignLeft', 'alignRight', 'alignCenter', 'alignJustify'
+				'headings', '|', 'alignment:left', 'alignment:right', 'alignment:center', 'alignment:justify'
 			],
 			viewportTopOffset: 60
 		},

+ 9 - 7
packages/ckeditor5-alignment/docs/features/text-alignment.md

@@ -24,7 +24,7 @@ ClassicEditor
 			options: [ 'left', 'right' ]
 		},
 		toolbar: [
-			'headings', '|', 'bulletedList', 'numberedList', 'alignmentDropdown', 'undo', 'redo'
+			'headings', '|', 'bulletedList', 'numberedList', 'alignment', 'undo', 'redo'
 		]
 	} )
 	.then( ... )
@@ -35,13 +35,13 @@ ClassicEditor
 
 ## Configuring the toolbar
 
-You can choose to use the alignment drop-down (`'alignmentDropdown'`) or configure the toolbar to use separate buttons for each of the options:
+You can choose to use the alignment drop-down (`'alignment'`) or configure the toolbar to use separate buttons for each of the options:
 
 ```js
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		toolbar: [
-			'headings', '|', 'alignLeft', 'alignRight', 'alignCenter', 'alignJustify'
+			'headings', '|', 'alignment:left', 'alignment:right', 'alignment:center', 'alignment:justify'
 		]
 	} )
 	.then( ... )
@@ -66,7 +66,7 @@ import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment';
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		plugins: [ Alignment, ... ],
-		toolbar: [ 'alignmentDropdown', ... ]
+		toolbar: [ 'alignment', ... ]
 	} )
 	.then( ... )
 	.catch( ... );
@@ -80,15 +80,17 @@ ClassicEditor
 
 The {@link module:alignment/alignment~Alignment} plugin registers:
 
-* Drop-down: `'alignmentDropdown'`.
-* Buttons and commands: `'alignLeft'`, `'alignRight'`, `'alignCenter'`, `'alignJustify'`.
+* Drop-down: `'alignment'`.
+* Buttons: `'alignment:left'`, `'alignment:right'`, `'alignment:center'`, `'alignment:justify'`.
 
 	The number of options and their names are based on the {@link module:alignment/alignment~AlignmentConfig#options `alignment.options`} configuration option).
 
+* Command: `'alignment'`:
+
 	You can align the currently selected block(s) by executing one of these commands:
 
 	```js
-	editor.execute( 'alignCenter' );
+	editor.execute( 'align', { value: 'center' } );
 	```
 
 ## Contribute

+ 33 - 79
packages/ckeditor5-alignment/src/alignmentcommand.js

@@ -8,9 +8,11 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
-
 import first from '@ckeditor/ckeditor5-utils/src/first';
-import upperFirst from '@ckeditor/ckeditor5-utils/src/lib/lodash/upperFirst';
+
+import { isDefault } from './utils';
+
+const ALIGNMENT = 'alignment';
 
 /**
  * The alignment command plugin.
@@ -19,73 +21,55 @@ import upperFirst from '@ckeditor/ckeditor5-utils/src/lib/lodash/upperFirst';
  */
 export default class AlignmentCommand extends Command {
 	/**
-	 * Creates an instance of the alignment command.
-	 *
-	 * @param {module:core/editor/editor~Editor} editor The editor instance.
-	 * @param {'left'|'right'|'center'|'justify'} alignment Alignment value to be handled by this command.
-	 * @param {Boolean} isDefault Indicates if the command is the default alignment.
+	 * @inheritDoc
 	 */
-	constructor( editor, alignment, isDefault ) {
-		super( editor );
-
-		/**
-		 * The alignment value handled by the command.
-		 *
-		 * @readonly
-		 * @member {'left'|'right'|'center'|'justify'}
-		 */
-		this.alignment = alignment;
+	refresh() {
+		const firstBlock = first( this.editor.model.document.selection.getSelectedBlocks() );
 
-		/**
-		 * Whether this command is the default alignment.
-		 *
-		 * @readonly
-		 * @private
-		 * @member {Boolean}
-		 */
-		this._isDefault = isDefault;
+		// As first check whether to enable or disable the command as the value will always be false if the command cannot be enabled.
+		this.isEnabled = !!firstBlock && this._canBeAligned( firstBlock );
 
 		/**
-		 * A flag indicating whether the command is active, which means that the selection starts in a block
-		 * which has the same alignment as {@link #alignment this command}.
+		 * A value of current block's alignment.
 		 *
 		 * @observable
 		 * @readonly
-		 * @member {Boolean} #value
+		 * @member {String} #value
 		 */
+		this.value = this.isEnabled && firstBlock.hasAttribute( 'alignment' ) ? firstBlock.getAttribute( 'alignment' ) : 'left';
 	}
 
 	/**
-	 * @inheritDoc
-	 */
-	refresh() {
-		const firstBlock = first( this.editor.model.document.selection.getSelectedBlocks() );
-
-		// As first check whether to enable or disable the command as the value will always be false if the command cannot be enabled.
-		this.isEnabled = !!firstBlock && this._canBeAligned( firstBlock );
-		this.value = this._getValue( firstBlock );
-	}
-
-	/**
-	 * Executes the command.
+	 * Executes the command. Applies the alignment `value` to the selected blocks.
+	 * If no `value` is passed, is a default one or is equal to currently selected block's alignment attribute,
+	 * it will remove the attribute from the selected blocks.
 	 *
-	 * @protected
+	 * @param {Object} [options] Options for the executed command.
+	 * @param {String} [options.value] a value to apply.
+	 * @fires execute
 	 */
-	execute() {
+	execute( options = {} ) {
 		const editor = this.editor;
 		const model = editor.model;
 		const doc = model.document;
 
+		const value = options.value;
+
 		model.change( writer => {
 			// Get only those blocks from selected that can have alignment set
 			const blocks = Array.from( doc.selection.getSelectedBlocks() ).filter( block => this._canBeAligned( block ) );
+			const currentAlignment = blocks[ 0 ].getAttribute( 'alignment' );
+
+			// Remove alignment attribute if current alignment is:
+			// - default (should not be stored in model as it will bloat model data)
+			// - equal to currently set
+			// - or no value is passed - denotes default alignment.
+			const removeAlignment = isDefault( value ) || currentAlignment === value || !value;
 
-			// Remove alignment attribute if current alignment is as selected or is default one.
-			// Default alignment should not be stored in model as it will bloat model data.
-			if ( this.value || this._isDefault ) {
+			if ( removeAlignment ) {
 				removeAlignmentFromSelection( blocks, writer );
 			} else {
-				setAlignmentOnSelection( blocks, writer, this.alignment );
+				setAlignmentOnSelection( blocks, writer, value );
 			}
 		} );
 	}
@@ -98,45 +82,15 @@ export default class AlignmentCommand extends Command {
 	 * @returns {Boolean}
 	 */
 	_canBeAligned( block ) {
-		return this.editor.model.schema.checkAttribute( block, 'alignment' );
+		return this.editor.model.schema.checkAttribute( block, ALIGNMENT );
 	}
-
-	/**
-	 * Checks the command's {@link #value}.
-	 *
-	 * @private
-	 * @param {module:engine/model/element~Element} firstBlock The first block in the selection to be checked.
-	 * @returns {Boolean} The current value.
-	 */
-	_getValue( firstBlock ) {
-		// The #_checkEnabled is checked as first so if command is disabled it's value is also false.
-		if ( !this.isEnabled || !firstBlock ) {
-			return false;
-		}
-
-		const selectionAlignment = firstBlock.getAttribute( 'alignment' );
-
-		// Command's value will be on when command's alignment matches the alignment of the current block,
-		// or when it's the default alignment and the block has no alignment set.
-		return selectionAlignment ? selectionAlignment === this.alignment : this._isDefault;
-	}
-}
-
-/**
- * Helper function that returns the command name for the alignment option.
- *
- * @param {String} option
- * @returns {String}
- */
-export function commandNameFromOptionName( option ) {
-	return `align${ upperFirst( option ) }`;
 }
 
 // Removes alignment attribute from blocks.
 // @private
 function removeAlignmentFromSelection( blocks, writer ) {
 	for ( const block of blocks ) {
-		writer.removeAttribute( 'alignment', block );
+		writer.removeAttribute( ALIGNMENT, block );
 	}
 }
 
@@ -144,6 +98,6 @@ function removeAlignmentFromSelection( blocks, writer ) {
 // @private
 function setAlignmentOnSelection( blocks, writer, alignment ) {
 	for ( const block of blocks ) {
-		writer.setAttribute( 'alignment', alignment, block );
+		writer.setAttribute( ALIGNMENT, alignment, block );
 	}
 }

+ 7 - 39
packages/ckeditor5-alignment/src/alignmentediting.js

@@ -7,10 +7,14 @@
  * @module alignment/alignmentediting
  */
 
-import AlignmentCommand, { commandNameFromOptionName } from './alignmentcommand';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 
+import AlignmentCommand from './alignmentcommand';
+import { isDefault, isSupported, supportedOptions } from './utils';
+
 /**
+ * The alignment editing feature. It introduces the {@link module:alignment/alignmentcommand~AlignmentCommand command} and adds
+ * the `alignment` attribute of block elements in the {@link module:engine/model/model~Model model}.
  * @extends module:core/plugin~Plugin
  */
 export default class AlignmentEditing extends Plugin {
@@ -21,27 +25,11 @@ export default class AlignmentEditing extends Plugin {
 		super( editor );
 
 		editor.config.define( 'alignment', {
-			options: [ ...this.constructor.supportedOptions ]
+			options: [ ...supportedOptions ]
 		} );
 	}
 
 	/**
-	 * The list of supported alignment options:
-	 *
-	 * * `'left'`,
-	 * * `'right'`,
-	 * * `'center'`,
-	 * * `'justify'`
-	 *
-	 * @static
-	 * @readonly
-	 * @member {Array.<String>} module:alignment/alignmentediting~AlignmentEditing.supportedOptions
-	 */
-	static get supportedOptions() {
-		return [ 'left', 'right', 'center', 'justify' ];
-	}
-
-	/**
 	 * @inheritDoc
 	 */
 	init() {
@@ -58,23 +46,10 @@ export default class AlignmentEditing extends Plugin {
 
 		editor.conversion.attributeToAttribute( definition );
 
-		// Add only enabled commands.
-		enabledOptions.forEach( option => {
-			editor.commands.add( commandNameFromOptionName( option ), new AlignmentCommand( editor, option, isDefault( option ) ) );
-		} );
+		editor.commands.add( 'alignment', new AlignmentCommand( editor ) );
 	}
 }
 
-/**
- * Checks whether the passed option is supported by {@link module:alignment/alignmentediting~AlignmentEditing}.
- *
- * @param {String} option The option value to check.
- * @returns {Boolean}
- */
-export function isSupported( option ) {
-	return AlignmentEditing.supportedOptions.includes( option );
-}
-
 // Utility function responsible for building converter definition.
 // @private
 function _buildDefinition( options ) {
@@ -97,10 +72,3 @@ function _buildDefinition( options ) {
 
 	return definition;
 }
-
-// Check whether alignment is the default one.
-// @private
-function isDefault( textAlign ) {
-	// Right now only LTR is supported so 'left' value is always the default one.
-	return textAlign === 'left';
-}

+ 9 - 13
packages/ckeditor5-alignment/src/alignmentui.js

@@ -8,13 +8,10 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
-
 import { createDropdown, addToolbarToDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
 
-import { commandNameFromOptionName } from './alignmentcommand';
-import { isSupported } from './alignmentediting';
+import { isSupported } from './utils';
 
 import alignLeftIcon from '../theme/icons/align-left.svg';
 import alignRightIcon from '../theme/icons/align-right.svg';
@@ -31,7 +28,7 @@ const icons = new Map( [
 /**
  * The default alignment UI plugin.
  *
- * It introduces the `'alignLeft'`, `'alignRight'`, `'alignCenter'` and `'alignJustify'` buttons
+ * It introduces the `'alignment:left'`, `'alignment:right'`, `'alignment:center'` and `'alignment:justify'` buttons
  * and the `'alignmentDropdown'` drop-down.
  *
  * @extends module:core/plugin~Plugin
@@ -82,11 +79,11 @@ export default class AlignmentUI extends Plugin {
 			.filter( isSupported )
 			.forEach( option => this._addButton( option ) );
 
-		componentFactory.add( 'alignmentDropdown', locale => {
+		componentFactory.add( 'alignment', locale => {
 			const dropdownView = createDropdown( locale );
 
 			// Add existing alignment buttons to dropdown's toolbar.
-			const buttons = options.map( option => componentFactory.create( commandNameFromOptionName( option ) ) );
+			const buttons = options.map( option => componentFactory.create( `alignment:${ option }` ) );
 			addToolbarToDropdown( dropdownView, buttons );
 
 			// Configure dropdown properties an behavior.
@@ -136,10 +133,8 @@ export default class AlignmentUI extends Plugin {
 	_addButton( option ) {
 		const editor = this.editor;
 
-		const commandName = commandNameFromOptionName( option );
-
-		editor.ui.componentFactory.add( commandName, locale => {
-			const command = editor.commands.get( commandName );
+		editor.ui.componentFactory.add( `alignment:${ option }`, locale => {
+			const command = editor.commands.get( 'alignment' );
 			const buttonView = new ButtonView( locale );
 
 			buttonView.set( {
@@ -149,11 +144,12 @@ export default class AlignmentUI extends Plugin {
 			} );
 
 			// Bind button model to command.
-			buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+			buttonView.bind( 'isEnabled' ).to( command );
+			buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );
 
 			// Execute command.
 			this.listenTo( buttonView, 'execute', () => {
-				editor.execute( commandName );
+				editor.execute( 'alignment', { value: option } );
 				editor.editing.view.focus();
 			} );
 

+ 39 - 0
packages/ckeditor5-alignment/src/utils.js

@@ -0,0 +1,39 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module alignment/utils
+ */
+
+/**
+ * The list of supported alignment options:
+ *
+ * * `'left'`,
+ * * `'right'`,
+ * * `'center'`,
+ * * `'justify'`
+ */
+export const supportedOptions = [ 'left', 'right', 'center', 'justify' ];
+
+/**
+ * Checks whether the passed option is supported by {@link module:alignment/alignmentediting~AlignmentEditing}.
+ *
+ * @param {String} option The option value to check.
+ * @returns {Boolean}
+ */
+export function isSupported( option ) {
+	return supportedOptions.includes( option );
+}
+
+/**
+ * Checks whether alignment is the default one.
+ *
+ * @param {String} alignment Name of alignment to check.
+ * @returns {Boolean}
+ */
+export function isDefault( alignment ) {
+	// Right now only LTR is supported so 'left' value is always the default one.
+	return alignment === 'left';
+}

+ 38 - 30
packages/ckeditor5-alignment/tests/alignmentcommand.js

@@ -11,21 +11,20 @@ import Command from '@ckeditor/ckeditor5-core/src/command';
 import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
 
 describe( 'AlignmentCommand', () => {
-	let editor, model, command, defaultAlignmentCommand;
+	let editor, model, command;
 
 	beforeEach( () => {
 		return ModelTestEditor.create()
 			.then( newEditor => {
 				model = newEditor.model;
-				command = new AlignmentCommand( newEditor, 'center', false );
-				defaultAlignmentCommand = new AlignmentCommand( newEditor, 'left', true );
+				command = new AlignmentCommand( newEditor );
 				editor = newEditor;
 
-				editor.commands.add( 'alignCenter', command );
-				editor.commands.add( 'alignLeft', defaultAlignmentCommand );
+				editor.commands.add( 'alignment', command );
 
 				model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
-				model.schema.extend( '$block', { allowAttributes: 'alignment' } );
+				model.schema.register( 'div', { inheritAllFrom: '$block' } );
+				model.schema.extend( 'paragraph', { allowAttributes: 'alignment' } );
 			} );
 	} );
 
@@ -39,28 +38,16 @@ describe( 'AlignmentCommand', () => {
 	} );
 
 	describe( 'value', () => {
-		it( 'is true when selection is in block with command type alignment', () => {
+		it( 'is set to block alignment when selection is in block that has alignment attribute set', () => {
 			setModelData( model, '<paragraph alignment="center">x[]x</paragraph>' );
 
-			expect( command ).to.have.property( 'value', true );
+			expect( command ).to.have.property( 'value', 'center' );
 		} );
 
-		it( 'is false when selection is inside block that has different alignment', () => {
-			setModelData( model, '<paragraph alignment="justify">x[]x</paragraph>' );
-
-			expect( command ).to.have.property( 'value', false );
-		} );
-
-		it( 'is true when selection is in block with default alignment', () => {
+		it( 'is set to default alignment when selection is in block with default alignment', () => {
 			setModelData( model, '<paragraph>x[]x</paragraph>' );
 
-			expect( defaultAlignmentCommand ).to.have.property( 'value', true );
-		} );
-
-		it( 'is false when selection is inside block that has different alignment (default option)', () => {
-			setModelData( model, '<paragraph alignment="justify">x[]x</paragraph>' );
-
-			expect( defaultAlignmentCommand ).to.have.property( 'value', false );
+			expect( command ).to.have.property( 'value', 'left' );
 		} );
 	} );
 
@@ -70,6 +57,12 @@ describe( 'AlignmentCommand', () => {
 
 			expect( command ).to.have.property( 'isEnabled', true );
 		} );
+
+		it( 'is false when selection is in a block which can not be aligned', () => {
+			setModelData( model, '<div>x[]x</div>' );
+
+			expect( command ).to.have.property( 'isEnabled', false );
+		} );
 	} );
 
 	describe( 'execute()', () => {
@@ -77,7 +70,7 @@ describe( 'AlignmentCommand', () => {
 			it( 'adds alignment to block element', () => {
 				setModelData( model, '<paragraph>x[]x</paragraph>' );
 
-				editor.execute( 'alignCenter' );
+				editor.execute( 'alignment', { value: 'center' } );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph alignment="center">x[]x</paragraph>' );
 			} );
@@ -85,7 +78,7 @@ describe( 'AlignmentCommand', () => {
 			it( 'should remove alignment from single block element if already has one', () => {
 				setModelData( model, '<paragraph alignment="center">x[]x</paragraph>' );
 
-				editor.execute( 'alignCenter' );
+				editor.execute( 'alignment', { value: 'left' } );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>x[]x</paragraph>' );
 			} );
@@ -93,7 +86,7 @@ describe( 'AlignmentCommand', () => {
 			it( 'adds alignment to all selected blocks', () => {
 				setModelData( model, '<paragraph>x[x</paragraph><paragraph>xx</paragraph><paragraph>x]x</paragraph>' );
 
-				editor.execute( 'alignCenter' );
+				editor.execute( 'alignment', { value: 'center' } );
 
 				expect( getModelData( model ) ).to.equal(
 					'<paragraph alignment="center">x[x</paragraph>' +
@@ -110,7 +103,7 @@ describe( 'AlignmentCommand', () => {
 					'<paragraph alignment="center">x]x</paragraph>'
 				);
 
-				editor.execute( 'alignCenter' );
+				editor.execute( 'alignment', { value: 'center' } );
 
 				expect( getModelData( model ) ).to.equal(
 					'<paragraph alignment="center">x[x</paragraph>' +
@@ -118,13 +111,28 @@ describe( 'AlignmentCommand', () => {
 					'<paragraph alignment="center">x]x</paragraph>'
 				);
 			} );
+
+			it( 'should remove alignment if block has the same alignment set', () => {
+				setModelData( model, '<paragraph alignment="center">x[]x</paragraph>' );
+
+				editor.execute( 'alignment', { value: 'center' } );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph>x[]x</paragraph>' );
+			} );
 		} );
 
 		describe( 'applying default alignment', () => {
-			it( 'removes alignment from block element', () => {
+			it( 'removes alignment from block element when passed as value', () => {
+				setModelData( model, '<paragraph alignment="justify">x[]x</paragraph>' );
+
+				editor.execute( 'alignment', { value: 'left' } );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph>x[]x</paragraph>' );
+			} );
+			it( 'removes alignment from block element when no value is passed', () => {
 				setModelData( model, '<paragraph alignment="justify">x[]x</paragraph>' );
 
-				editor.execute( 'alignLeft' );
+				editor.execute( 'alignment' );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>x[]x</paragraph>' );
 			} );
@@ -136,7 +144,7 @@ describe( 'AlignmentCommand', () => {
 					'<paragraph alignment="center">x]x</paragraph>'
 				);
 
-				editor.execute( 'alignLeft' );
+				editor.execute( 'alignment', { value: 'left' } );
 
 				expect( getModelData( model ) ).to.equal(
 					'<paragraph>x[x</paragraph><paragraph>xx</paragraph><paragraph>x]x</paragraph>'
@@ -150,7 +158,7 @@ describe( 'AlignmentCommand', () => {
 					'<paragraph alignment="center">x]x</paragraph>'
 				);
 
-				editor.execute( 'alignLeft' );
+				editor.execute( 'alignment', { value: 'left' } );
 
 				expect( getModelData( model ) ).to.equal(
 					'<paragraph>x[x</paragraph><paragraph>xx</paragraph><paragraph>x]x</paragraph>'

+ 9 - 31
packages/ckeditor5-alignment/tests/alignmentediting.js

@@ -31,11 +31,8 @@ describe( 'AlignmentEditing', () => {
 		editor.destroy();
 	} );
 
-	it( 'adds alignment commands', () => {
-		expect( editor.commands.get( 'alignLeft' ) ).to.be.instanceOf( AlignmentCommand );
-		expect( editor.commands.get( 'alignRight' ) ).to.be.instanceOf( AlignmentCommand );
-		expect( editor.commands.get( 'alignCenter' ) ).to.be.instanceOf( AlignmentCommand );
-		expect( editor.commands.get( 'alignJustify' ) ).to.be.instanceOf( AlignmentCommand );
+	it( 'adds alignment command', () => {
+		expect( editor.commands.get( 'alignment' ) ).to.be.instanceOf( AlignmentCommand );
 	} );
 
 	it( 'allows for alignment in $blocks', () => {
@@ -73,7 +70,7 @@ describe( 'AlignmentEditing', () => {
 		} );
 	} );
 
-	describe( 'alignLeft', () => {
+	describe( 'left alignment', () => {
 		it( 'adds converters to the data pipeline', () => {
 			const data = '<p style="text-align:left;">x</p>';
 
@@ -88,15 +85,13 @@ describe( 'AlignmentEditing', () => {
 
 			expect( editor.getData() ).to.equal( '<p style="text-align:center;">x</p>' );
 
-			const command = editor.commands.get( 'alignLeft' );
-
-			command.execute();
+			editor.execute( 'alignment' );
 
 			expect( editor.getData() ).to.equal( '<p>x</p>' );
 		} );
 	} );
 
-	describe( 'alignCenter', () => {
+	describe( 'center alignment', () => {
 		it( 'adds converters to the data pipeline', () => {
 			const data = '<p style="text-align:center;">x</p>';
 
@@ -117,15 +112,13 @@ describe( 'AlignmentEditing', () => {
 
 			expect( editor.getData() ).to.equal( '<p style="text-align:right;">x</p>' );
 
-			const command = editor.commands.get( 'alignCenter' );
-
-			command.execute();
+			editor.execute( 'alignment', { value: 'center' } );
 
 			expect( editor.getData() ).to.equal( '<p style="text-align:center;">x</p>' );
 		} );
 	} );
 
-	describe( 'alignRight', () => {
+	describe( 'right alignment', () => {
 		it( 'adds converters to the data pipeline', () => {
 			const data = '<p style="text-align:right;">x</p>';
 
@@ -142,7 +135,7 @@ describe( 'AlignmentEditing', () => {
 		} );
 	} );
 
-	describe( 'alignJustify', () => {
+	describe( 'justify alignment', () => {
 		it( 'adds converters to the data pipeline', () => {
 			const data = '<p style="text-align:justify;">x</p>';
 
@@ -172,8 +165,7 @@ describe( 'AlignmentEditing', () => {
 
 			expect( editor.getData() ).to.equal( '<p>x</p>' );
 
-			const command = editor.commands.get( 'alignLeft' );
-			command.execute();
+			editor.execute( 'alignment' );
 
 			expect( editor.getData() ).to.equal( '<p>x</p>' );
 		} );
@@ -206,20 +198,6 @@ describe( 'AlignmentEditing', () => {
 					expect( editor.config.get( 'alignment.options' ) ).to.deep.equal( [ 'left', 'right', 'center', 'justify' ] );
 				} );
 			} );
-
-			it( 'should customize commands', () => {
-				return VirtualTestEditor
-					.create( {
-						alignment: { options: [ 'left', 'right' ] },
-						plugins: [ AlignmentEditing, Paragraph ]
-					} )
-					.then( editor => {
-						expect( editor.commands.get( 'alignLeft' ), 'adds alignLeft' ).to.be.instanceof( AlignmentCommand );
-						expect( editor.commands.get( 'alignRight' ), 'adds alignLeft' ).to.be.instanceof( AlignmentCommand );
-						expect( editor.commands.get( 'alignCenter' ), 'does not add alignCenter' ).to.be.undefined;
-						expect( editor.commands.get( 'alignJustify' ), 'does not add alignJustify' ).to.be.undefined;
-					} );
-			} );
 		} );
 	} );
 } );

+ 43 - 27
packages/ckeditor5-alignment/tests/alignmentui.js

@@ -49,10 +49,10 @@ describe( 'Alignment UI', () => {
 		} );
 	} );
 
-	describe( 'alignLeft button', () => {
+	describe( 'alignment:left button', () => {
 		beforeEach( () => {
-			command = editor.commands.get( 'alignLeft' );
-			button = editor.ui.componentFactory.create( 'alignLeft' );
+			command = editor.commands.get( 'alignment' );
+			button = editor.ui.componentFactory.create( 'alignment:left' );
 		} );
 
 		it( 'has the base properties', () => {
@@ -65,8 +65,11 @@ describe( 'Alignment UI', () => {
 			command.value = false;
 			expect( button ).to.have.property( 'isOn', false );
 
-			command.value = true;
+			command.value = 'left';
 			expect( button ).to.have.property( 'isOn', true );
+
+			command.value = 'justify';
+			expect( button ).to.have.property( 'isOn', false );
 		} );
 
 		it( 'has isEnabled bound to command\'s isEnabled', () => {
@@ -83,14 +86,15 @@ describe( 'Alignment UI', () => {
 			button.fire( 'execute' );
 
 			expect( spy.calledOnce ).to.be.true;
-			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignLeft' );
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignment' );
+			expect( spy.args[ 0 ][ 1 ] ).to.deep.equal( { value: 'left' } );
 		} );
 	} );
 
-	describe( 'alignRight button', () => {
+	describe( 'alignment:right button', () => {
 		beforeEach( () => {
-			command = editor.commands.get( 'alignRight' );
-			button = editor.ui.componentFactory.create( 'alignRight' );
+			command = editor.commands.get( 'alignment' );
+			button = editor.ui.componentFactory.create( 'alignment:right' );
 		} );
 
 		it( 'has the base properties', () => {
@@ -103,8 +107,11 @@ describe( 'Alignment UI', () => {
 			command.value = false;
 			expect( button ).to.have.property( 'isOn', false );
 
-			command.value = true;
+			command.value = 'right';
 			expect( button ).to.have.property( 'isOn', true );
+
+			command.value = 'justify';
+			expect( button ).to.have.property( 'isOn', false );
 		} );
 
 		it( 'has isEnabled bound to command\'s isEnabled', () => {
@@ -121,14 +128,15 @@ describe( 'Alignment UI', () => {
 			button.fire( 'execute' );
 
 			expect( spy.calledOnce ).to.be.true;
-			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignRight' );
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignment' );
+			expect( spy.args[ 0 ][ 1 ] ).to.deep.equal( { value: 'right' } );
 		} );
 	} );
 
-	describe( 'alignCenter button', () => {
+	describe( 'alignment:center button', () => {
 		beforeEach( () => {
-			command = editor.commands.get( 'alignCenter' );
-			button = editor.ui.componentFactory.create( 'alignCenter' );
+			command = editor.commands.get( 'alignment' );
+			button = editor.ui.componentFactory.create( 'alignment:center' );
 		} );
 
 		it( 'has the base properties', () => {
@@ -141,8 +149,11 @@ describe( 'Alignment UI', () => {
 			command.value = false;
 			expect( button ).to.have.property( 'isOn', false );
 
-			command.value = true;
+			command.value = 'center';
 			expect( button ).to.have.property( 'isOn', true );
+
+			command.value = 'justify';
+			expect( button ).to.have.property( 'isOn', false );
 		} );
 
 		it( 'has isEnabled bound to command\'s isEnabled', () => {
@@ -159,14 +170,15 @@ describe( 'Alignment UI', () => {
 			button.fire( 'execute' );
 
 			expect( spy.calledOnce ).to.be.true;
-			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignCenter' );
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignment' );
+			expect( spy.args[ 0 ][ 1 ] ).to.deep.equal( { value: 'center' } );
 		} );
 	} );
 
-	describe( 'alignJustify button', () => {
+	describe( 'alignment:justify button', () => {
 		beforeEach( () => {
-			command = editor.commands.get( 'alignJustify' );
-			button = editor.ui.componentFactory.create( 'alignJustify' );
+			command = editor.commands.get( 'alignment' );
+			button = editor.ui.componentFactory.create( 'alignment:justify' );
 		} );
 
 		it( 'has the base properties', () => {
@@ -179,8 +191,11 @@ describe( 'Alignment UI', () => {
 			command.value = false;
 			expect( button ).to.have.property( 'isOn', false );
 
-			command.value = true;
+			command.value = 'justify';
 			expect( button ).to.have.property( 'isOn', true );
+
+			command.value = 'center';
+			expect( button ).to.have.property( 'isOn', false );
 		} );
 
 		it( 'has isEnabled bound to command\'s isEnabled', () => {
@@ -197,16 +212,17 @@ describe( 'Alignment UI', () => {
 			button.fire( 'execute' );
 
 			expect( spy.calledOnce ).to.be.true;
-			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignJustify' );
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( 'alignment' );
+			expect( spy.args[ 0 ][ 1 ] ).to.deep.equal( { value: 'justify' } );
 		} );
 	} );
 
-	describe( 'alignmentDropdown', () => {
+	describe( 'alignment', () => {
 		let dropdown;
 
 		beforeEach( () => {
-			command = editor.commands.get( 'alignLeft' );
-			dropdown = editor.ui.componentFactory.create( 'alignmentDropdown' );
+			command = editor.commands.get( 'alignment' );
+			dropdown = editor.ui.componentFactory.create( 'alignment' );
 		} );
 
 		it( '#buttonView has the base properties', () => {
@@ -253,9 +269,9 @@ describe( 'Alignment UI', () => {
 					.then( newEditor => {
 						editor = newEditor;
 
-						dropdown = editor.ui.componentFactory.create( 'alignmentDropdown' );
-						command = editor.commands.get( 'alignCenter' );
-						button = editor.ui.componentFactory.create( 'alignCenter' );
+						dropdown = editor.ui.componentFactory.create( 'alignment' );
+						command = editor.commands.get( 'alignment' );
+						button = editor.ui.componentFactory.create( 'alignment:center' );
 					} );
 			} );
 
@@ -273,7 +289,7 @@ describe( 'Alignment UI', () => {
 			} );
 
 			it( 'should change icon to active alignment', () => {
-				command.value = true;
+				command.value = 'center';
 
 				expect( dropdown.buttonView.icon ).to.equal( button.icon );
 			} );

+ 5 - 5
packages/ckeditor5-alignment/tests/integration.js

@@ -45,7 +45,7 @@ describe( 'Alignment integration', () => {
 		it( 'does not work inside image caption', () => {
 			setModelData( model, '<image src="foo.png"><caption>Foo[]</caption></image>' );
 
-			editor.execute( 'alignCenter' );
+			editor.execute( 'alignment', { value: 'center' } );
 
 			expect( getModelData( model ) ).to.equal( '<image src="foo.png"><caption>Foo[]</caption></image>' );
 		} );
@@ -58,7 +58,7 @@ describe( 'Alignment integration', () => {
 				'<paragraph>baz]baz</paragraph>'
 			);
 
-			editor.execute( 'alignCenter' );
+			editor.execute( 'alignment', { value: 'center' } );
 
 			expect( getModelData( model ) ).to.equal(
 				'<paragraph alignment="center">foo[foo</paragraph>' +
@@ -72,7 +72,7 @@ describe( 'Alignment integration', () => {
 		it( 'does work inside BlockQuote on paragraph', () => {
 			setModelData( model, '<blockQuote><paragraph>Foo[]</paragraph></blockQuote>' );
 
-			editor.execute( 'alignCenter' );
+			editor.execute( 'alignment', { value: 'center' } );
 
 			expect( getModelData( model ) ).to.equal( '<blockQuote><paragraph alignment="center">Foo[]</paragraph></blockQuote>' );
 		} );
@@ -80,7 +80,7 @@ describe( 'Alignment integration', () => {
 		it( 'does work inside blockQuote on heading', () => {
 			setModelData( model, '<blockQuote><heading1>Foo[]</heading1></blockQuote>' );
 
-			editor.execute( 'alignCenter' );
+			editor.execute( 'alignment', { value: 'center' } );
 
 			expect( getModelData( model ) ).to.equal( '<blockQuote><heading1 alignment="center">Foo[]</heading1></blockQuote>' );
 		} );
@@ -88,7 +88,7 @@ describe( 'Alignment integration', () => {
 		it( 'does work inside blockQuote on listItem', () => {
 			setModelData( model, '<blockQuote><listItem indent="0" type="numbered">Foo[]</listItem></blockQuote>' );
 
-			editor.execute( 'alignCenter' );
+			editor.execute( 'alignment', { value: 'center' } );
 
 			expect( getModelData( model ) ).to.equal(
 				'<blockQuote><listItem alignment="center" indent="0" type="numbered">Foo[]</listItem></blockQuote>'

+ 1 - 1
packages/ckeditor5-alignment/tests/manual/alignment.html

@@ -1,5 +1,5 @@
 <div id="editor">
-	<p><strong>This should be aligned to the left</strong>This is a test for text alignment feature.</p>
+	<p><strong>This should be aligned to the left</strong>. This is a test for text alignment feature.</p>
 	<figure class="image">
 		<img src="sample.jpg" alt="CKEditor logo" />
 		<figcaption>An image</figcaption>

+ 1 - 1
packages/ckeditor5-alignment/tests/manual/alignment.js

@@ -13,7 +13,7 @@ ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		plugins: [ ArticlePluginSet, Alignment ],
 		toolbar: [
-			'headings', '|', 'bold', 'italic', 'link', 'alignmentDropdown', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'
+			'headings', '|', 'alignment', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'
 		]
 	} )
 	.then( editor => {

+ 22 - 0
packages/ckeditor5-alignment/tests/manual/alignmentbuttons.html

@@ -0,0 +1,22 @@
+<div id="editor">
+	<p><strong>This should be aligned to the left</strong>. This is a test for text alignment feature.</p>
+	<figure class="image">
+		<img src="sample.jpg" alt="CKEditor logo"/>
+		<figcaption>An image</figcaption>
+	</figure>
+	<p style="text-align:right"><strong>This should be aligned to the right</strong>. Lorem ipsum dolor sit amet, consectetur adipiscing
+		elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+		laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
+		fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
+		laborum.</p>
+
+	<p style="text-align:center"><strong>This should be centered</strong>. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+		eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+		ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+		pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+
+	<p style="text-align:justify"><strong>This should be justified</strong>. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+		eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+		ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+		pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
+</div>

+ 25 - 0
packages/ckeditor5-alignment/tests/manual/alignmentbuttons.js

@@ -0,0 +1,25 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console, window, document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+import Alignment from '../../src/alignment';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ ArticlePluginSet, Alignment ],
+		toolbar: [
+			'headings', '|', 'alignment:left', 'alignment:right', 'alignment:center', 'alignment:justify', '|',
+			'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'
+		]
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 34 - 0
packages/ckeditor5-alignment/tests/manual/alignmentbuttons.md

@@ -0,0 +1,34 @@
+### Loading
+
+1. The data should be loaded with:
+  * first paragraph with default (left) alignment,
+  * second paragraph aligned to right,
+  * third paragraph should be centered,
+  * fourth paragraph should be justified,
+  * an image with caption between first and second paragraphs.
+2. Toolbar should have alignment button dropdown with four buttons for alignment control: left, right, center and justify.
+
+### Testing
+
+You should be able to:
+1. Change alignment on any paragraph.
+2. Change alignment on any selection of paragraphs.
+
+You should not be able to change alignment of figcaption by:
+1. Placing cursor inside caption.
+2. Selecting image with other paragraphs.
+
+You should be able to create blocks that can be aligned:
+1. paragraphs
+2. headings
+3. lists
+
+Alignment UI should:
+1. change button dropdown to "left" and highlight "left" button when selection is inside left aligned block (default one)
+2. change button dropdown to "right" and highlight "right" button when selection is inside right aligned block
+3. change button dropdown to "center" and highlight "center" button when selection is inside centered block
+4. change button dropdown to "justify" and highlight "justify" button when selection is inside justified block
+5. be disabled if in figcaption
+6. only one button should be active at once inside dropdown
+7. alignment dropdown should be vertical
+

+ 2 - 2
packages/ckeditor5-alignment/tests/manual/alignmentconfig.js

@@ -13,8 +13,8 @@ ClassicEditor
 	.create( document.querySelector( '#editor' ), {
 		plugins: [ ArticlePluginSet, Alignment ],
 		toolbar: [
-			'headings', '|', 'bold', 'italic', 'link', 'alignmentDropdown', 'bulletedList', 'numberedList',
-			'blockQuote', 'undo', 'redo'
+			'headings', '|', 'alignment', '|', 'alignment:left', 'alignment:right', 'alignment:center', 'alignment:justify', '|',
+			'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'
 		],
 		alignment: { options: [ 'center', 'justify' ] }
 	} )

+ 35 - 0
packages/ckeditor5-alignment/tests/utils.js

@@ -0,0 +1,35 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import { isDefault, isSupported, supportedOptions } from '../src/utils';
+
+describe( 'utils', () => {
+	describe( 'isDefault()', () => {
+		it( 'should return true for "left" alignment only', () => {
+			expect( isDefault( 'left' ) ).to.be.true;
+			expect( isDefault( 'right' ) ).to.be.false;
+			expect( isDefault( 'center' ) ).to.be.false;
+			expect( isDefault( 'justify' ) ).to.be.false;
+		} );
+	} );
+
+	describe( 'isSupported()', () => {
+		it( 'should return true for supported alignments', () => {
+			expect( isSupported( 'left' ) ).to.be.true;
+			expect( isSupported( 'right' ) ).to.be.true;
+			expect( isSupported( 'center' ) ).to.be.true;
+			expect( isSupported( 'justify' ) ).to.be.true;
+
+			expect( isSupported( '' ) ).to.be.false;
+			expect( isSupported( 'middle' ) ).to.be.false;
+		} );
+	} );
+
+	describe( 'supportedOptions', () => {
+		it( 'should be set', () => {
+			expect( supportedOptions ).to.deep.equal( [ 'left', 'right', 'center', 'justify' ] );
+		} );
+	} );
+} );