Explorar el Código

Brought RTL content support to the Alignment feature.

Aleksander Nowodzinski hace 6 años
padre
commit
96a64f71b9

+ 9 - 2
packages/ckeditor5-alignment/src/alignmentcommand.js

@@ -24,6 +24,8 @@ export default class AlignmentCommand extends Command {
 	 * @inheritDoc
 	 */
 	refresh() {
+		const editor = this.editor;
+		const locale = editor.locale;
 		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.
@@ -36,7 +38,11 @@ export default class AlignmentCommand extends Command {
 		 * @readonly
 		 * @member {String} #value
 		 */
-		this.value = ( this.isEnabled && firstBlock.hasAttribute( 'alignment' ) ) ? firstBlock.getAttribute( 'alignment' ) : 'left';
+		if ( this.isEnabled && firstBlock.hasAttribute( 'alignment' ) ) {
+			this.value = firstBlock.getAttribute( 'alignment' );
+		} else {
+			this.value = locale.contentLanguageDirection === 'rtl' ? 'right' : 'left';
+		}
 	}
 
 	/**
@@ -50,6 +56,7 @@ export default class AlignmentCommand extends Command {
 	 */
 	execute( options = {} ) {
 		const editor = this.editor;
+		const locale = editor.locale;
 		const model = editor.model;
 		const doc = model.document;
 
@@ -64,7 +71,7 @@ export default class AlignmentCommand extends Command {
 			// - 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;
+			const removeAlignment = isDefault( value, locale ) || currentAlignment === value || !value;
 
 			if ( removeAlignment ) {
 				removeAlignmentFromSelection( blocks, writer );

+ 2 - 1
packages/ckeditor5-alignment/src/alignmentediting.js

@@ -34,6 +34,7 @@ export default class AlignmentEditing extends Plugin {
 	 */
 	init() {
 		const editor = this.editor;
+		const locale = editor.locale;
 		const schema = editor.model.schema;
 
 		// Filter out unsupported options.
@@ -43,7 +44,7 @@ export default class AlignmentEditing extends Plugin {
 		schema.extend( '$block', { allowAttributes: 'alignment' } );
 		editor.model.schema.setAttributeProperties( 'alignment', { isFormatting: true } );
 
-		const definition = _buildDefinition( enabledOptions.filter( option => !isDefault( option ) ) );
+		const definition = _buildDefinition( enabledOptions.filter( option => !isDefault( option, locale ) ) );
 
 		editor.conversion.attributeToAttribute( definition );
 

+ 2 - 2
packages/ckeditor5-alignment/src/alignmentui.js

@@ -100,8 +100,8 @@ export default class AlignmentUI extends Plugin {
 				}
 			} );
 
-			// The default icon is align left as we do not support RTL yet (see #3).
-			const defaultIcon = alignLeftIcon;
+			// The default icon depends on the direction of the content.
+			const defaultIcon = locale.contentLanguageDirection === 'rtl' ? alignRightIcon : alignLeftIcon;
 
 			// Change icon to reflect current selection's alignment.
 			dropdownView.buttonView.bind( 'icon' ).toMany( buttons, 'isOn', ( ...areActive ) => {

+ 10 - 3
packages/ckeditor5-alignment/src/utils.js

@@ -28,12 +28,19 @@ export function isSupported( option ) {
 }
 
 /**
- * Checks whether alignment is the default one.
+ * Checks whether alignment is the default one considering the direction
+ * of the editor content.
  *
  * @param {String} alignment The name of the alignment to check.
+ * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
  * @returns {Boolean}
  */
-export function isDefault( alignment ) {
+export function isDefault( alignment, locale ) {
 	// Right now only LTR is supported so the 'left' value is always the default one.
-	return alignment === 'left';
+
+	if ( locale.contentLanguageDirection == 'rtl' ) {
+		return alignment === 'right';
+	} else {
+		return alignment === 'left';
+	}
 }

+ 44 - 2
packages/ckeditor5-alignment/tests/alignmentcommand.js

@@ -44,11 +44,31 @@ describe( 'AlignmentCommand', () => {
 			expect( command ).to.have.property( 'value', 'center' );
 		} );
 
-		it( 'is set to default alignment when selection is in block with default alignment', () => {
+		it( 'is set to default alignment when selection is in block with default alignment (LTR content)', () => {
 			setModelData( model, '<paragraph>x[]x</paragraph>' );
 
 			expect( command ).to.have.property( 'value', 'left' );
 		} );
+
+		it( 'is set to default alignment when selection is in block with default alignment (RTL content)', () => {
+			return ModelTestEditor.create( {
+				contentLanguage: 'ar'
+			} ).then( newEditor => {
+				model = newEditor.model;
+				command = new AlignmentCommand( newEditor );
+				newEditor.commands.add( 'alignment', command );
+
+				model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+				model.schema.register( 'div', { inheritAllFrom: '$block' } );
+				model.schema.extend( 'paragraph', { allowAttributes: 'alignment' } );
+
+				setModelData( model, '<paragraph>x[]x</paragraph>' );
+
+				expect( command ).to.have.property( 'value', 'right' );
+
+				return newEditor.destroy();
+			} );
+		} );
 	} );
 
 	describe( 'isEnabled', () => {
@@ -75,7 +95,7 @@ describe( 'AlignmentCommand', () => {
 				expect( getModelData( model ) ).to.equal( '<paragraph alignment="center">x[]x</paragraph>' );
 			} );
 
-			it( 'should remove alignment from single block element if already has one', () => {
+			it( 'should remove alignment from single block element if already has one (LTR content)', () => {
 				setModelData( model, '<paragraph alignment="center">x[]x</paragraph>' );
 
 				editor.execute( 'alignment', { value: 'left' } );
@@ -83,6 +103,28 @@ describe( 'AlignmentCommand', () => {
 				expect( getModelData( model ) ).to.equal( '<paragraph>x[]x</paragraph>' );
 			} );
 
+			it( 'should remove alignment from single block element if already has one (RTL content)', () => {
+				return ModelTestEditor.create( {
+					contentLanguage: 'ar'
+				} ).then( newEditor => {
+					model = newEditor.model;
+					command = new AlignmentCommand( newEditor );
+					newEditor.commands.add( 'alignment', command );
+
+					model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+					model.schema.register( 'div', { inheritAllFrom: '$block' } );
+					model.schema.extend( 'paragraph', { allowAttributes: 'alignment' } );
+
+					setModelData( model, '<paragraph alignment="center">x[]x</paragraph>' );
+
+					newEditor.execute( 'alignment', { value: 'right' } );
+
+					expect( getModelData( model ) ).to.equal( '<paragraph>x[]x</paragraph>' );
+
+					return newEditor.destroy();
+				} );
+			} );
+
 			it( 'adds alignment to all selected blocks', () => {
 				setModelData( model, '<paragraph>x[x</paragraph><paragraph>xx</paragraph><paragraph>x]x</paragraph>' );
 

+ 97 - 13
packages/ckeditor5-alignment/tests/alignmentediting.js

@@ -77,13 +77,58 @@ describe( 'AlignmentEditing', () => {
 	} );
 
 	describe( 'left alignment', () => {
-		it( 'adds converters to the data pipeline', () => {
-			const data = '<p style="text-align:left;">x</p>';
+		describe( 'LTR content', () => {
+			it( 'adds converters to the data pipeline', () => {
+				const data = '<p style="text-align:left;">x</p>';
 
-			editor.setData( data );
+				editor.setData( data );
 
-			expect( getModelData( model ) ).to.equal( '<paragraph>[]x</paragraph>' );
-			expect( editor.getData() ).to.equal( '<p>x</p>' );
+				expect( getModelData( model ) ).to.equal( '<paragraph>[]x</paragraph>' );
+				expect( editor.getData() ).to.equal( '<p>x</p>' );
+			} );
+
+			it( 'adds a converter to the view pipeline', () => {
+				setModelData( model, '<paragraph alignment="left">[]x</paragraph>' );
+
+				expect( editor.getData() ).to.equal( '<p>x</p>' );
+			} );
+		} );
+
+		describe( 'RTL content', () => {
+			it( 'adds converters to the data pipeline', () => {
+				return VirtualTestEditor
+					.create( {
+						contentLanguage: 'ar',
+						plugins: [ AlignmentEditing, Paragraph ]
+					} )
+					.then( newEditor => {
+						const model = newEditor.model;
+						const data = '<p style="text-align:left;">x</p>';
+
+						newEditor.setData( data );
+
+						expect( getModelData( model ) ).to.equal( '<paragraph alignment="left">[]x</paragraph>' );
+						expect( newEditor.getData() ).to.equal( '<p style="text-align:left;">x</p>' );
+
+						return newEditor.destroy();
+					} );
+			} );
+
+			it( 'adds a converter to the view pipeline', () => {
+				return VirtualTestEditor
+					.create( {
+						contentLanguage: 'ar',
+						plugins: [ AlignmentEditing, Paragraph ]
+					} )
+					.then( newEditor => {
+						const model = newEditor.model;
+
+						setModelData( model, '<paragraph alignment="left">[]x</paragraph>' );
+						expect( newEditor.getData() ).to.equal( '<p style="text-align:left;">x</p>' );
+
+						return newEditor.destroy();
+					} );
+			} );
 		} );
 
 		it( 'adds a converter to the view pipeline for removing attribute', () => {
@@ -125,19 +170,58 @@ describe( 'AlignmentEditing', () => {
 	} );
 
 	describe( 'right alignment', () => {
-		it( 'adds converters to the data pipeline', () => {
-			const data = '<p style="text-align:right;">x</p>';
+		describe( 'LTR content', () => {
+			it( 'adds converters to the data pipeline', () => {
+				const data = '<p style="text-align:right;">x</p>';
 
-			editor.setData( data );
+				editor.setData( data );
 
-			expect( getModelData( model ) ).to.equal( '<paragraph alignment="right">[]x</paragraph>' );
-			expect( editor.getData() ).to.equal( data );
+				expect( getModelData( model ) ).to.equal( '<paragraph alignment="right">[]x</paragraph>' );
+				expect( editor.getData() ).to.equal( data );
+			} );
+
+			it( 'adds a converter to the view pipeline', () => {
+				setModelData( model, '<paragraph alignment="right">[]x</paragraph>' );
+
+				expect( editor.getData() ).to.equal( '<p style="text-align:right;">x</p>' );
+			} );
 		} );
 
-		it( 'adds a converter to the view pipeline', () => {
-			setModelData( model, '<paragraph alignment="right">[]x</paragraph>' );
+		describe( 'RTL content', () => {
+			it( 'adds converters to the data pipeline', () => {
+				return VirtualTestEditor
+					.create( {
+						contentLanguage: 'ar',
+						plugins: [ AlignmentEditing, Paragraph ]
+					} )
+					.then( newEditor => {
+						const model = newEditor.model;
+						const data = '<p style="text-align:right;">x</p>';
 
-			expect( editor.getData() ).to.equal( '<p style="text-align:right;">x</p>' );
+						newEditor.setData( data );
+
+						expect( getModelData( model ) ).to.equal( '<paragraph>[]x</paragraph>' );
+						expect( newEditor.getData() ).to.equal( '<p>x</p>' );
+
+						return newEditor.destroy();
+					} );
+			} );
+
+			it( 'adds a converter to the view pipeline', () => {
+				return VirtualTestEditor
+					.create( {
+						contentLanguage: 'ar',
+						plugins: [ AlignmentEditing, Paragraph ]
+					} )
+					.then( newEditor => {
+						const model = newEditor.model;
+
+						setModelData( model, '<paragraph alignment="right">[]x</paragraph>' );
+						expect( newEditor.getData() ).to.equal( '<p>x</p>' );
+
+						return newEditor.destroy();
+					} );
+			} );
 		} );
 	} );
 

+ 21 - 1
packages/ckeditor5-alignment/tests/alignmentui.js

@@ -9,6 +9,7 @@ import AlignmentEditing from '../src/alignmentediting';
 import AlignmentUI from '../src/alignmentui';
 
 import alignLeftIcon from '../theme/icons/align-left.svg';
+import alignRightIcon from '../theme/icons/align-right.svg';
 
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 
@@ -284,10 +285,29 @@ describe( 'Alignment UI', () => {
 				expect( items.includes( 'Justify' ) ).to.be.true;
 			} );
 
-			it( 'should have default icon set', () => {
+			it( 'should have default icon set (LTR content)', () => {
 				expect( dropdown.buttonView.icon ).to.equal( alignLeftIcon );
 			} );
 
+			it( 'should have default icon set (RTL content)', () => {
+				const element = document.createElement( 'div' );
+				document.body.appendChild( element );
+
+				return ClassicTestEditor
+					.create( element, {
+						contentLanguage: 'ar',
+						plugins: [ AlignmentEditing, AlignmentUI ],
+						alignment: { options: [ 'center', 'justify' ] }
+					} )
+					.then( newEditor => {
+						dropdown = newEditor.ui.componentFactory.create( 'alignment' );
+
+						expect( dropdown.buttonView.icon ).to.equal( alignRightIcon );
+
+						return newEditor.destroy();
+					} );
+			} );
+
 			it( 'should change icon to active alignment', () => {
 				command.value = 'center';
 

+ 20 - 5
packages/ckeditor5-alignment/tests/utils.js

@@ -7,11 +7,26 @@ 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;
+		it( 'should return true for "left" alignment only (LTR)', () => {
+			const locale = {
+				contentLanguageDirection: 'ltr'
+			};
+
+			expect( isDefault( 'left', locale ) ).to.be.true;
+			expect( isDefault( 'right', locale ) ).to.be.false;
+			expect( isDefault( 'center', locale ) ).to.be.false;
+			expect( isDefault( 'justify', locale ) ).to.be.false;
+		} );
+
+		it( 'should return true for "right" alignment only (RTL)', () => {
+			const locale = {
+				contentLanguageDirection: 'rtl'
+			};
+
+			expect( isDefault( 'left', locale ) ).to.be.false;
+			expect( isDefault( 'right', locale ) ).to.be.true;
+			expect( isDefault( 'center', locale ) ).to.be.false;
+			expect( isDefault( 'justify', locale ) ).to.be.false;
 		} );
 	} );