Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge 78b6eff into 8a0a293
Browse files Browse the repository at this point in the history
  • Loading branch information
oleq committed Jul 26, 2019
2 parents 8a0a293 + 78b6eff commit 8586b98
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 32 deletions.
6 changes: 5 additions & 1 deletion docs/features/text-alignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `'left'` or `'right'` should always be included for <abbr title="left–to–right">LTR</abbr> and <abbr title="right-to-left">RTL</abbr> content, respectively. 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:

Expand Down
5 changes: 3 additions & 2 deletions src/alignment.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
11 changes: 9 additions & 2 deletions src/alignmentcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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';
}
}

/**
Expand All @@ -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;

Expand All @@ -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 );
Expand Down
3 changes: 2 additions & 1 deletion src/alignmentediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 );

Expand Down
4 changes: 2 additions & 2 deletions src/alignmentui.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) => {
Expand Down
13 changes: 10 additions & 3 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
}
46 changes: 44 additions & 2 deletions tests/alignmentcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -75,14 +95,36 @@ 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' } );

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>' );

Expand Down
110 changes: 97 additions & 13 deletions tests/alignmentediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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();
} );
} );
} );
} );

Expand Down
22 changes: 21 additions & 1 deletion tests/alignmentui.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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';

Expand Down
9 changes: 9 additions & 0 deletions tests/manual/rtl.html
Original file line number Diff line number Diff line change
@@ -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>
26 changes: 26 additions & 0 deletions tests/manual/rtl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @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',
contentLanguage: '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 changes: 6 additions & 0 deletions tests/manual/rtl.md
Original file line number Diff line number Diff line change
@@ -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.

0 comments on commit 8586b98

Please sign in to comment.