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

Commit

Permalink
Merge pull request #15 from ckeditor/t/14
Browse files Browse the repository at this point in the history
Feature: Implemented `ParagraphCommand`, previously part of the `HeadingCommand`. Closes #14.
  • Loading branch information
Reinmar authored Mar 9, 2017
2 parents b9f1eb8 + ce7b717 commit 876877d
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/paragraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* @module paragraph/paragraph
*/

import ParagraphCommand from './paragraphcommand';
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';

import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
Expand Down Expand Up @@ -65,6 +66,8 @@ export default class Paragraph extends Plugin {
data.viewToModel.on( 'element', ( evt, data, consumable, conversionApi ) => {
autoparagraphParagraphLikeElements( doc, evt, data, consumable, conversionApi );
}, { priority: 'low' } );

editor.commands.set( 'paragraph', new ParagraphCommand( editor ) );
}
}

Expand Down
78 changes: 78 additions & 0 deletions src/paragraphcommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module paragraph/paragraphcommand
*/

import Command from '@ckeditor/ckeditor5-core/src/command/command';

/**
* The paragraph command.
*
* @extends module:core/command/command~Command
*/
export default class ParagraphCommand extends Command {
/**
* Creates an instance of the command.
*
* @param {module:core/editor/editor~Editor} editor Editor instance.
*/
constructor( editor ) {
super( editor );

/**
* Value of the command, indicating whether it is applied in the context
* of current {@link module:engine/model/document~Document#selection selection}.
*
* @readonly
* @observable
* @member {Boolean}
*/
this.set( 'value', false );

// Update current value each time changes are done on document.
this.listenTo( editor.document, 'changesDone', () => this._updateValue() );
}

/**
* Executes the command. All the {@link module:engine/model/schema~Schema#blocks blocks} in the selection
* will be turned to paragraphs.
*
* @protected
* @param {Object} [options] Options for executed command.
* @param {module:engine/model/batch~Batch} [options.batch] Batch to collect all the change steps.
* New batch will be created if this option is not set.
* @param {module:engine/model/selection~Selection} [options.selection] Selection the command should be applied to.
* By default, if not provided, the command is applied to {@link module:engine/model/document~Document#selection}.
*/
_doExecute( options = {} ) {
const document = this.editor.document;

document.enqueueChanges( () => {
const batch = options.batch || document.batch();
const blocks = ( options.selection || document.selection ).getSelectedBlocks();

for ( let block of blocks ) {
if ( !block.is( 'paragraph' ) ) {
batch.rename( block, 'paragraph' );
}
}
} );
}

/**
* Updates command's {@link #value value} based on current selection.
*
* @private
*/
_updateValue() {
const block = this.editor.document.selection.getSelectedBlocks().next().value;

if ( block ) {
this.value = block.is( 'paragraph' );
}
}
}
7 changes: 7 additions & 0 deletions tests/paragraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import Paragraph from '../src/paragraph';
import ParagraphCommand from '../src/paragraphcommand';
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
import {
getData as getModelData,
Expand Down Expand Up @@ -337,4 +338,10 @@ describe( 'Paragraph feature', () => {
expect( getModelData( doc ) ).to.equal( '<paragraph>[]foo</paragraph>' );
} );
} );

describe( 'command', () => {
it( 'should be set in the editor', () => {
expect( editor.commands.get( 'paragraph' ) ).to.be.instanceof( ParagraphCommand );
} );
} );
} );
150 changes: 150 additions & 0 deletions tests/paragraphcommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
import ParagraphCommand from '../src/paragraphcommand';
import Selection from '@ckeditor/ckeditor5-engine/src/model/selection';
import Range from '@ckeditor/ckeditor5-engine/src/model/range';
import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';

describe( 'ParagraphCommand', () => {
let editor, document, command, root, schema;

beforeEach( () => {
return ModelTestEditor.create().then( ( newEditor ) => {
editor = newEditor;
document = editor.document;
schema = document.schema;
command = new ParagraphCommand( editor );
root = document.getRoot();

editor.commands.set( 'paragraph', command );
schema.registerItem( 'paragraph', '$block' );
schema.registerItem( 'heading1', '$block' );
} );
} );

afterEach( () => {
command.destroy();
} );

describe( 'value', () => {
it( 'has default value', () => {
setData( document, '' );

expect( command.value ).to.be.false;
} );

it( 'responds to changes in selection (collapsed selection)', () => {
setData( document, '<heading1>foo[]bar</heading1>' );
expect( command.value ).to.be.false;

setData( document, '<paragraph>foo[]bar</paragraph>' );
expect( command.value ).to.be.true;
} );

it( 'responds to changes in selection (non–collapsed selection)', () => {
setData( document, '<heading1>[foo]</heading1><paragraph>bar</paragraph>' );
expect( command.value ).to.be.false;

setData( document, '<heading1>[foo</heading1><paragraph>bar]</paragraph>' );
expect( command.value ).to.be.false;

setData( document, '<heading1>foo</heading1>[<paragraph>bar]</paragraph>' );
expect( command.value ).to.be.true;

setData( document, '<heading1>foo</heading1><paragraph>[bar]</paragraph>' );
expect( command.value ).to.be.true;

setData( document, '<paragraph>[bar</paragraph><heading1>foo]</heading1>' );
expect( command.value ).to.be.true;
} );
} );

describe( '_doExecute', () => {
it( 'should update value after execution', () => {
setData( document, '<heading1>[]</heading1>' );
command._doExecute();

expect( getData( document ) ).to.equal( '<paragraph>[]</paragraph>' );
expect( command.value ).to.be.true;
} );

it( 'should not rename blocks which already are pargraphs', () => {
const batch = editor.document.batch();

setData( document, '<paragraph>foo[</paragraph><heading1>bar]</heading1>' );
expect( batch.deltas.length ).to.equal( 0 );

command._doExecute( { batch } );
expect( batch.deltas.length ).to.equal( 1 );
} );

describe( 'custom options', () => {
it( 'should use provided batch', () => {
const batch = editor.document.batch();

setData( document, '<heading1>foo[]bar</heading1>' );
expect( batch.deltas.length ).to.equal( 0 );

command._doExecute( { batch } );
expect( batch.deltas.length ).to.be.above( 0 );
} );

it( 'should use provided selection', () => {
setData( document, '<heading1>foo[]bar</heading1><heading1>baz</heading1><heading1>qux</heading1>' );

const secondTolastHeading = root.getChild( 1 );
const lastHeading = root.getChild( 2 );
const selection = new Selection();
selection.addRange( Range.createFromParentsAndOffsets( secondTolastHeading, 0, lastHeading, 0 ) );

command._doExecute( { selection } );
expect( getData( document ) ).to.equal( '<heading1>foo[]bar</heading1><paragraph>baz</paragraph><paragraph>qux</paragraph>' );
} );
} );

describe( 'collapsed selection', () => {
it( 'does nothing when executed with already applied', () => {
setData( document, '<paragraph>foo[]bar</paragraph>' );
command._doExecute();

expect( getData( document ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
} );

it( 'converts topmost blocks', () => {
schema.registerItem( 'inlineImage', '$inline' );
schema.allow( { name: '$text', inside: 'inlineImage' } );

setData( document, '<heading1><inlineImage>foo[]</inlineImage>bar</heading1>' );
command._doExecute();

expect( getData( document ) ).to.equal( '<paragraph><inlineImage>foo[]</inlineImage>bar</paragraph>' );
} );
} );

describe( 'non-collapsed selection', () => {
it( 'converts all elements where selection is applied', () => {
schema.registerItem( 'heading2', '$block' );

setData( document, '<heading1>foo[</heading1><heading2>bar</heading2><heading2>]baz</heading2>' );

command._doExecute();
expect( getData( document ) ).to.equal(
'<paragraph>foo[</paragraph><paragraph>bar</paragraph><paragraph>]baz</paragraph>'
);
} );

it( 'converts all elements even if already anchored in paragraph', () => {
schema.registerItem( 'heading2', '$block' );

setData( document, '<paragraph>foo[</paragraph><heading2>bar]</heading2>' );

command._doExecute();
expect( getData( document ) ).to.equal( '<paragraph>foo[</paragraph><paragraph>bar]</paragraph>' );
} );
} );
} );
} );

0 comments on commit 876877d

Please sign in to comment.