Przeglądaj źródła

Merge branch 'master' into t/ckeditor5/1403

Aleksander Nowodzinski 6 lat temu
rodzic
commit
51855e94a2

+ 5 - 1
packages/ckeditor5-alignment/docs/features/text-alignment.md

@@ -13,7 +13,11 @@ The {@link module:alignment/alignment~Alignment} feature enables support for tex
 
 ## Configuring alignment options
 
-It is possible to configure which alignment options are available in the editor by setting the {@link module:alignment/alignment~AlignmentConfig#options `alignment.options`} configuration option. You can choose from `'left'`, `'right'`, `'center'` and `'justify'`;  note that `'left'` should always be included.
+It is possible to configure which alignment options are available in the editor by setting the {@link module:alignment/alignment~AlignmentConfig#options `alignment.options`} configuration option. You can choose from `'left'`, `'right'`, `'center'` and `'justify'`.
+
+<info-box>
+	Note that the `'left'` option should always be included for the <abbr title="left–to–right">LTR</abbr> content. Similarly, the `'right'` option should always be included for the <abbr title="right-to-left">RTL</abbr> content. Learn more about {@link features/ui-language#setting-the-language-of-the-content configuring language of the editor content}.
+</info-box>
 
 For example, the following editor will support only two alignment options: to the left and to the right:
 

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

@@ -69,8 +69,9 @@ export default class Alignment extends Plugin {
  *
  * The available options are: `'left'`, `'right'`, `'center'` and `'justify'`. Other values are ignored.
  *
- * **Note:** It is recommended to always use `'left'` as it is the default value which the user should
- * normally be able to choose.
+ * **Note:** It is recommended to always use `'left'` or `'right'` as these are default values which the user should
+ * normally be able to choose depending on the
+ * {@glink features/ui-language#setting-the-language-of-the-content language of the editor content}.
  *
  *		ClassicEditor
  *			.create( editorElement, {

+ 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';
+	}
 }

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

@@ -44,11 +44,33 @@ 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( {
+				language: {
+					content: '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 +97,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 +105,30 @@ 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( {
+					language: {
+						content: '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>' );
 

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

@@ -77,13 +77,62 @@ 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( {
+						language: {
+							content: '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( {
+						language: {
+							content: '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 +174,62 @@ 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( {
+						language: {
+							content: 'ar'
+						},
+						plugins: [ AlignmentEditing, Paragraph ]
+					} )
+					.then( newEditor => {
+						const model = newEditor.model;
+						const data = '<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();
+					} );
+			} );
 
-			expect( editor.getData() ).to.equal( '<p style="text-align:right;">x</p>' );
+			it( 'adds a converter to the view pipeline', () => {
+				return VirtualTestEditor
+					.create( {
+						language: {
+							content: '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();
+					} );
+			} );
 		} );
 	} );
 

+ 23 - 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';
 
@@ -285,10 +286,31 @@ 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, {
+						language: {
+							content: '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';
 

+ 9 - 0
packages/ckeditor5-alignment/tests/manual/rtl.html

@@ -0,0 +1,9 @@
+<div id="editor">
+	<p>This should be aligned to the right</p>
+
+	<p style="text-align:left">This should be aligned to the left.</p>
+
+	<p style="text-align:center">This should be centered.</p>
+
+	<p style="text-align:justify">This should be justified but also to the right. This should be justified but also to the right. This should be justified but also to the right.</p>
+</div>

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

@@ -0,0 +1,25 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* 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' ), {
+		language: 'ar',
+		plugins: [ ArticlePluginSet, Alignment ],
+		toolbar: [
+			'heading', '|', 'alignment', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'
+		]
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 6 - 0
packages/ckeditor5-alignment/tests/manual/rtl.md

@@ -0,0 +1,6 @@
+## Text alignment and RTL content
+
+1. The editor should run in RTL mode.
+1. Paragraphs in the content should be aligned according to the text inside.
+1. See the toolbar (dropdown) icon as you travel across the document with your caret. The icon should reflect the alignment (default is **right**).
+1. You should be able to use the dropdown to change the alignment of existing and new text.

+ 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;
 		} );
 	} );