This repository has been archived by the owner on Jun 26, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #54 from ckeditor/t/51
Feature: Introduced the `Underline` plugin. Closes #51.
- Loading branch information
Showing
9 changed files
with
318 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
{ | ||
"Bold": "Toolbar button tooltip for the Bold feature.", | ||
"Italic": "Toolbar button tooltip for the Italic feature." | ||
} | ||
"Italic": "Toolbar button tooltip for the Italic feature.", | ||
"Underline": "Toolbar button tooltip for the Underline feature." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/** | ||
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
/** | ||
* @module basic-styles/underline | ||
*/ | ||
|
||
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; | ||
import UnderlineEngine from './underlineengine'; | ||
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview'; | ||
import underlineIcon from '../theme/icons/underline.svg'; | ||
|
||
/** | ||
* The underline feature. It introduces the Underline button and the <kbd>Ctrl+U</kbd> keystroke. | ||
* | ||
* It uses the {@link module:basic-styles/underlineengine~UnderlineEngine underline engine feature}. | ||
* | ||
* @extends module:core/plugin~Plugin | ||
*/ | ||
export default class Underline extends Plugin { | ||
/** | ||
* @inheritDoc | ||
*/ | ||
static get requires() { | ||
return [ UnderlineEngine ]; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
static get pluginName() { | ||
return 'Underline'; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
init() { | ||
const editor = this.editor; | ||
const t = editor.t; | ||
const command = editor.commands.get( 'underline' ); | ||
const keystroke = 'CTRL+U'; | ||
|
||
// Add bold button to feature components. | ||
editor.ui.componentFactory.add( 'underline', locale => { | ||
const view = new ButtonView( locale ); | ||
|
||
view.set( { | ||
label: t( 'Underline' ), | ||
icon: underlineIcon, | ||
keystroke, | ||
tooltip: true | ||
} ); | ||
|
||
view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' ); | ||
|
||
// Execute command. | ||
this.listenTo( view, 'execute', () => editor.execute( 'underline' ) ); | ||
|
||
return view; | ||
} ); | ||
|
||
// Set the Ctrl+U keystroke. | ||
editor.keystrokes.set( keystroke, 'underline' ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/** | ||
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
/** | ||
* @module basic-styles/underlineengine | ||
*/ | ||
|
||
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 UNDERLINE = 'underline'; | ||
|
||
/** | ||
* The underline engine feature. | ||
* | ||
* It registers the `underline` command and introduces the `underline` attribute in the model which renders to the view | ||
* as an `<u>` element. | ||
* | ||
* @extends module:core/plugin~Plugin | ||
*/ | ||
export default class UnderlineEngine extends Plugin { | ||
/** | ||
* @inheritDoc | ||
*/ | ||
init() { | ||
const editor = this.editor; | ||
const data = editor.data; | ||
const editing = editor.editing; | ||
|
||
// Allow underline attribute on all inline nodes. | ||
editor.document.schema.allow( { name: '$inline', attributes: UNDERLINE, inside: '$block' } ); | ||
// Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477. | ||
editor.document.schema.allow( { name: '$inline', attributes: UNDERLINE, inside: '$clipboardHolder' } ); | ||
|
||
// Build converter from model to view for data and editing pipelines. | ||
buildModelConverter().for( data.modelToView, editing.modelToView ) | ||
.fromAttribute( UNDERLINE ) | ||
.toElement( 'u' ); | ||
|
||
// Build converter from view to model for data pipeline. | ||
buildViewConverter().for( data.viewToModel ) | ||
.fromElement( 'u' ) | ||
.fromAttribute( 'style', { 'text-decoration': 'underline' } ) | ||
.toAttribute( UNDERLINE, true ); | ||
|
||
// Create underline command. | ||
editor.commands.add( UNDERLINE, new AttributeCommand( editor, UNDERLINE ) ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
<div id="editor"> | ||
<p><i>This</i> is an <strong>editor</strong> instance.</p> | ||
<p><i>This</i> is an <strong>editor</strong> <u>instance</u>.</p> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* @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 Underline from '../src/underline'; | ||
import UnderlineEngine from '../src/underlineengine'; | ||
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( 'Underline', () => { | ||
let editor, underlineView; | ||
|
||
beforeEach( () => { | ||
const editorElement = document.createElement( 'div' ); | ||
document.body.appendChild( editorElement ); | ||
|
||
return ClassicTestEditor | ||
.create( editorElement, { | ||
plugins: [ Underline ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
|
||
underlineView = editor.ui.componentFactory.create( 'underline' ); | ||
} ); | ||
} ); | ||
|
||
afterEach( () => { | ||
return editor.destroy(); | ||
} ); | ||
|
||
it( 'should be loaded', () => { | ||
expect( editor.plugins.get( Underline ) ).to.be.instanceOf( Underline ); | ||
} ); | ||
|
||
it( 'should load UnderlineEngine', () => { | ||
expect( editor.plugins.get( UnderlineEngine ) ).to.be.instanceOf( UnderlineEngine ); | ||
} ); | ||
|
||
it( 'should register underline feature component', () => { | ||
expect( underlineView ).to.be.instanceOf( ButtonView ); | ||
expect( underlineView.isOn ).to.be.false; | ||
expect( underlineView.label ).to.equal( 'Underline' ); | ||
expect( underlineView.icon ).to.match( /<svg / ); | ||
expect( underlineView.keystroke ).to.equal( 'CTRL+U' ); | ||
} ); | ||
|
||
it( 'should execute underline command on model execute event', () => { | ||
const executeSpy = testUtils.sinon.spy( editor, 'execute' ); | ||
|
||
underlineView.fire( 'execute' ); | ||
|
||
sinon.assert.calledOnce( executeSpy ); | ||
sinon.assert.calledWithExactly( executeSpy, 'underline' ); | ||
} ); | ||
|
||
it( 'should bind model to underline command', () => { | ||
const command = editor.commands.get( 'underline' ); | ||
|
||
expect( underlineView.isOn ).to.be.false; | ||
|
||
expect( underlineView.isEnabled ).to.be.false; | ||
|
||
command.value = true; | ||
expect( underlineView.isOn ).to.be.true; | ||
|
||
command.isEnabled = true; | ||
expect( underlineView.isEnabled ).to.be.true; | ||
} ); | ||
|
||
it( 'should set keystroke in the model', () => { | ||
expect( underlineView.keystroke ).to.equal( 'CTRL+U' ); | ||
} ); | ||
|
||
it( 'should set editor keystroke', () => { | ||
const spy = sinon.spy( editor, 'execute' ); | ||
|
||
const wasHandled = editor.keystrokes.press( { | ||
keyCode: keyCodes.u, | ||
ctrlKey: true, | ||
preventDefault: sinon.spy(), | ||
stopPropagation: sinon.spy() | ||
} ); | ||
|
||
expect( wasHandled ).to.be.true; | ||
expect( spy.calledOnce ).to.be.true; | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/** | ||
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
import UnderlineEngine from '../src/underlineengine'; | ||
|
||
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( 'UnderlineEngine', () => { | ||
let editor, doc; | ||
|
||
beforeEach( () => { | ||
return VirtualTestEditor | ||
.create( { | ||
plugins: [ Paragraph, UnderlineEngine ] | ||
} ) | ||
.then( newEditor => { | ||
editor = newEditor; | ||
|
||
doc = editor.document; | ||
} ); | ||
} ); | ||
|
||
afterEach( () => { | ||
return editor.destroy(); | ||
} ); | ||
|
||
it( 'should be loaded', () => { | ||
expect( editor.plugins.get( UnderlineEngine ) ).to.be.instanceOf( UnderlineEngine ); | ||
} ); | ||
|
||
it( 'should set proper schema rules', () => { | ||
expect( doc.schema.check( { name: '$inline', attributes: 'underline', inside: '$root' } ) ).to.be.false; | ||
expect( doc.schema.check( { name: '$inline', attributes: 'underline', inside: '$block' } ) ).to.be.true; | ||
expect( doc.schema.check( { name: '$inline', attributes: 'underline', inside: '$clipboardHolder' } ) ).to.be.true; | ||
} ); | ||
|
||
describe( 'command', () => { | ||
it( 'should register underline command', () => { | ||
const command = editor.commands.get( 'underline' ); | ||
|
||
expect( command ).to.be.instanceOf( AttributeCommand ); | ||
expect( command ).to.have.property( 'attributeKey', 'underline' ); | ||
} ); | ||
} ); | ||
|
||
describe( 'data pipeline conversions', () => { | ||
it( 'should convert <u> to underline attribute', () => { | ||
editor.setData( '<p><u>foo</u>bar</p>' ); | ||
|
||
expect( getModelData( doc, { withoutSelection: true } ) ) | ||
.to.equal( '<paragraph><$text underline="true">foo</$text>bar</paragraph>' ); | ||
|
||
expect( editor.getData() ).to.equal( '<p><u>foo</u>bar</p>' ); | ||
} ); | ||
|
||
it( 'should convert text-decoration:underline to underline attribute', () => { | ||
editor.setData( '<p><span style="text-decoration: underline;">foo</span>bar</p>' ); | ||
|
||
expect( getModelData( doc, { withoutSelection: true } ) ) | ||
.to.equal( '<paragraph><$text underline="true">foo</$text>bar</paragraph>' ); | ||
|
||
expect( editor.getData() ).to.equal( '<p><u>foo</u>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( '<u>foo</u>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 underline="true">foo</$text>bar</paragraph>' ); | ||
|
||
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><u>foo</u>bar</p>' ); | ||
} ); | ||
} ); | ||
} ); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.