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 #58 from Natim/add-strike-engine-and-plugin
Browse files Browse the repository at this point in the history
Feature: Added strikethrough feature.

Thanks to @Natim!
  • Loading branch information
szymonkups authored Dec 5, 2017
2 parents cb0c3e0 + 69e6a79 commit 78719c9
Show file tree
Hide file tree
Showing 10 changed files with 344 additions and 9 deletions.
3 changes: 2 additions & 1 deletion docs/api/basic-styles.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ category: api-reference

[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-basic-styles.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-basic-styles)

This package contains features allowing to apply basic text formatting such as bold, italic, underline and code in CKEditor 5.
This package contains features allowing to apply basic text formatting such as bold, italic, underline, strikethrough and code in CKEditor 5.

## Documentation

Check out the following plugins:

* {@link module:basic-styles/bold~Bold}
* {@link module:basic-styles/italic~Italic}
* {@link module:basic-styles/strikethrough~Strikethrough}
* {@link module:basic-styles/underline~Underline}
* {@link module:basic-styles/code~Code}

Expand Down
3 changes: 2 additions & 1 deletion lang/contexts.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"Bold": "Toolbar button tooltip for the Bold feature.",
"Italic": "Toolbar button tooltip for the Italic feature.",
"Underline": "Toolbar button tooltip for the Underline feature.",
"Code": "Toolbar button tooltip for the Code feature."
"Code": "Toolbar button tooltip for the Code feature.",
"Strikethrough": "Toolbar button tooltip for the Strikethrough feature."
}
68 changes: 68 additions & 0 deletions src/strikethrough.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @license Copyright (c) 2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module basic-styles/strikethrough
*/

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import StrikethroughEngine from './strikethroughengine';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import strikethroughIcon from '../theme/icons/strikethrough.svg';

/**
* The strikethrough feature. It introduces the Strikethrough button and the <kbd>Ctrl+Shift+X</kbd> keystroke.
*
* It uses the {@link module:basic-styles/strikethroughengine~StrikethroughEngine strikethrough engine feature}.
*
* @extends module:core/plugin~Plugin
*/
export default class Strikethrough extends Plugin {
/**
* @inheritDoc
*/
static get requires() {
return [ StrikethroughEngine ];
}

/**
* @inheritDoc
*/
static get pluginName() {
return 'Strikethrough';
}

/**
* @inheritDoc
*/
init() {
const editor = this.editor;
const t = editor.t;
const command = editor.commands.get( 'strikethrough' );
const keystroke = 'CTRL+SHIFT+X';

// Add strikethrough button to feature components.
editor.ui.componentFactory.add( 'strikethrough', locale => {
const view = new ButtonView( locale );

view.set( {
label: t( 'Strikethrough' ),
icon: strikethroughIcon,
keystroke,
tooltip: true
} );

view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

// Execute command.
this.listenTo( view, 'execute', () => editor.execute( 'strikethrough' ) );

return view;
} );

// Set the Ctrl+Shift+X keystroke.
editor.keystrokes.set( keystroke, 'strikethrough' );
}
}
56 changes: 56 additions & 0 deletions src/strikethroughengine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @license Copyright (c) 2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* @module basic-styles/strikeengine
*/

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
import AttributeCommand from './attributecommand';

const STRIKETHROUGH = 'strikethrough';

/**
* The strikethrough engine feature.
*
* It registers the `strikethrough` command and introduces the
* `strikethroughsthrough` attribute in the model which renders to the view
* as a `<s>` element.
*
* @extends module:core/plugin~Plugin
*/
export default class StrikethroughEngine extends Plugin {
/**
* @inheritDoc
*/
init() {
const editor = this.editor;
const data = editor.data;
const editing = editor.editing;

// Allow strikethrough attribute on all inline nodes.
editor.document.schema.allow( { name: '$inline', attributes: STRIKETHROUGH, inside: '$block' } );
// Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477.
editor.document.schema.allow( { name: '$inline', attributes: STRIKETHROUGH, inside: '$clipboardHolder' } );

// Build converter from model to view for data and editing pipelines.
buildModelConverter().for( data.modelToView, editing.modelToView )
.fromAttribute( STRIKETHROUGH )
.toElement( 's' );

// Build converter from view to model for data pipeline.
buildViewConverter().for( data.viewToModel )
.fromElement( 's' )
.fromElement( 'del' )
.fromElement( 'strike' )
.fromAttribute( 'style', { 'text-decoration': 'line-through' } )
.toAttribute( STRIKETHROUGH, true );

// Create strikethrough command.
editor.commands.add( STRIKETHROUGH, new AttributeCommand( editor, STRIKETHROUGH ) );
}
}
2 changes: 1 addition & 1 deletion tests/manual/basic-styles.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div id="editor">
<p><i>This</i> <code>is an</code> <strong>editor</strong> <u>instance</u>.</p>
<p><i>This</i> <s>is</s> <code>an</code> <strong>editor</strong> <u>instance</u>.</p>
</div>
5 changes: 3 additions & 2 deletions tests/manual/basic-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '../../src/bold';
import Italic from '../../src/italic';
import Strikethrough from '../../src/strikethrough';
import Underline from '../../src/underline';
import Code from '../../src/code';

ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ Essentials, Paragraph, Bold, Italic, Underline, Code ],
toolbar: [ 'bold', 'italic', 'underline', 'code', 'undo', 'redo' ]
plugins: [ Essentials, Paragraph, Bold, Italic, Strikethrough, Underline, Code ],
toolbar: [ 'bold', 'italic', 'strikethrough', 'underline', 'code', 'undo', 'redo' ]
} )
.then( editor => {
window.editor = editor;
Expand Down
9 changes: 5 additions & 4 deletions tests/manual/basic-styles.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## Basic styles

1. The data should be loaded with:
* italic "This",
* bold "editor",
* italic _"This"_,
* bold **"editor"**,
* underline "instance",
* code "is an".
2. Test the bold, italic, underline and code features live.
* strikethrough ~~"is"~~,
* code `"an"`.
2. Test the bold, italic, strikethrough, underline and code features live.
98 changes: 98 additions & 0 deletions tests/strikethrough.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

/* globals document */

import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
import Strikethrough from '../src/strikethrough';
import StrikethroughEngine from '../src/strikethroughengine';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';

testUtils.createSinonSandbox();

describe( 'Strikethrough', () => {
let editor, strikeView;

beforeEach( () => {
const editorElement = document.createElement( 'div' );
document.body.appendChild( editorElement );

return ClassicTestEditor
.create( editorElement, {
plugins: [ Strikethrough ]
} )
.then( newEditor => {
editor = newEditor;

strikeView = editor.ui.componentFactory.create( 'strikethrough' );
} );
} );

afterEach( () => {
return editor.destroy();
} );

it( 'should be loaded', () => {
expect( editor.plugins.get( Strikethrough ) ).to.be.instanceOf( Strikethrough );
} );

it( 'should load StrikethroughEngine', () => {
expect( editor.plugins.get( StrikethroughEngine ) ).to.be.instanceOf( StrikethroughEngine );
} );

it( 'should register strikethrough feature component', () => {
expect( strikeView ).to.be.instanceOf( ButtonView );
expect( strikeView.isOn ).to.be.false;
expect( strikeView.label ).to.equal( 'Strikethrough' );
expect( strikeView.icon ).to.match( /<svg / );
expect( strikeView.keystroke ).to.equal( 'CTRL+SHIFT+X' );
} );

it( 'should execute strikethrough command on model execute event', () => {
const executeSpy = testUtils.sinon.spy( editor, 'execute' );

strikeView.fire( 'execute' );

sinon.assert.calledOnce( executeSpy );
sinon.assert.calledWithExactly( executeSpy, 'strikethrough' );
} );

it( 'should bind model to strikethrough command', () => {
const command = editor.commands.get( 'strikethrough' );

expect( strikeView.isOn ).to.be.false;

expect( strikeView.isEnabled ).to.be.false;

command.value = true;
expect( strikeView.isOn ).to.be.true;

command.isEnabled = true;
expect( strikeView.isEnabled ).to.be.true;
} );

it( 'should set keystroke in the model', () => {
expect( strikeView.keystroke ).to.equal( 'CTRL+SHIFT+X' );
} );

it( 'should set editor keystroke', () => {
const spy = sinon.spy( editor, 'execute' );
const keyEventData = {
keyCode: keyCodes.x,
shiftKey: true,
ctrlKey: true,
preventDefault: sinon.spy(),
stopPropagation: sinon.spy()
};

const wasHandled = editor.keystrokes.press( keyEventData );

expect( wasHandled ).to.be.true;
expect( spy.calledOnce ).to.be.true;
expect( keyEventData.preventDefault.calledOnce ).to.be.true;
} );
} );
108 changes: 108 additions & 0 deletions tests/strikethroughengine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

import StrikethroughEngine from '../src/strikethroughengine';

import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import AttributeCommand from '../src/attributecommand';

import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';

describe( 'StrikethroughEngine', () => {
let editor, doc;

beforeEach( () => {
return VirtualTestEditor
.create( {
plugins: [ Paragraph, StrikethroughEngine ]
} )
.then( newEditor => {
editor = newEditor;

doc = editor.document;
} );
} );

afterEach( () => {
return editor.destroy();
} );

it( 'should be loaded', () => {
expect( editor.plugins.get( StrikethroughEngine ) ).to.be.instanceOf( StrikethroughEngine );
} );

it( 'should set proper schema rules', () => {
expect( doc.schema.check( { name: '$inline', attributes: 'strikethrough', inside: '$root' } ) ).to.be.false;
expect( doc.schema.check( { name: '$inline', attributes: 'strikethrough', inside: '$block' } ) ).to.be.true;
expect( doc.schema.check( { name: '$inline', attributes: 'strikethrough', inside: '$clipboardHolder' } ) ).to.be.true;
} );

describe( 'command', () => {
it( 'should register strikethrough command', () => {
const command = editor.commands.get( 'strikethrough' );

expect( command ).to.be.instanceOf( AttributeCommand );
expect( command ).to.have.property( 'attributeKey', 'strikethrough' );
} );
} );

describe( 'data pipeline conversions', () => {
it( 'should convert <strike> to strikethrough attribute', () => {
editor.setData( '<p><strike>foo</strike>bar</p>' );

expect( getModelData( doc, { withoutSelection: true } ) )
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );

expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
} );
it( 'should convert <del> to strikethrough attribute', () => {
editor.setData( '<p><del>foo</del>bar</p>' );

expect( getModelData( doc, { withoutSelection: true } ) )
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );

expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
} );

it( 'should convert <s> to strikethrough attribute', () => {
editor.setData( '<p><s>foo</s>bar</p>' );

expect( getModelData( doc, { withoutSelection: true } ) )
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );

expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
} );

it( 'should convert text-decoration:line-through to strikethrough attribute', () => {
editor.setData( '<p><span style="text-decoration: line-through;">foo</span>bar</p>' );

expect( getModelData( doc, { withoutSelection: true } ) )
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );

expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
} );

it( 'should be integrated with autoparagraphing', () => {
// Incorrect results because autoparagraphing works incorrectly (issue in paragraph).
// https://github.com/ckeditor/ckeditor5-paragraph/issues/10

editor.setData( '<s>foo</s>bar' );

expect( getModelData( doc, { withoutSelection: true } ) ).to.equal( '<paragraph>foobar</paragraph>' );

expect( editor.getData() ).to.equal( '<p>foobar</p>' );
} );
} );

describe( 'editing pipeline conversion', () => {
it( 'should convert attribute', () => {
setModelData( doc, '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );

expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><s>foo</s>bar</p>' );
} );
} );
} );
1 change: 1 addition & 0 deletions theme/icons/strikethrough.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 78719c9

Please sign in to comment.