8
0
Просмотр исходного кода

Merge branch 'master' of https://github.com/idleb/ckeditor5-basic-styles into idleb-master

Aleksander Nowodzinski 7 лет назад
Родитель
Сommit
822c01d608

+ 3 - 1
packages/ckeditor5-basic-styles/lang/contexts.json

@@ -3,5 +3,7 @@
 	"Italic": "Toolbar button tooltip for the Italic feature.",
 	"Underline": "Toolbar button tooltip for the Underline feature.",
 	"Code": "Toolbar button tooltip for the Code feature.",
-	"Strikethrough": "Toolbar button tooltip for the Strikethrough feature."
+	"Strikethrough": "Toolbar button tooltip for the Strikethrough feature.",
+	"Subscript": "Toolbar button tooltip for the Subscript feature.",
+	"Superscript": "Toolbar button tooltip for the Superscript feature."
 }

+ 2 - 1
packages/ckeditor5-basic-styles/package.json

@@ -12,7 +12,8 @@
   "dependencies": {
     "@ckeditor/ckeditor5-core": "^11.0.1",
     "@ckeditor/ckeditor5-theme-lark": "^11.1.0",
-    "@ckeditor/ckeditor5-ui": "^11.1.0"
+    "@ckeditor/ckeditor5-ui": "^11.1.0",
+    "@ckeditor/ckeditor5-engine": "^11.0.0"
   },
   "devDependencies": {
     "@ckeditor/ckeditor5-cloud-services": "^10.1.0",

+ 36 - 0
packages/ckeditor5-basic-styles/src/subscript.js

@@ -0,0 +1,36 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/subscript
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import SubscriptEditing from './subscript/subscriptediting';
+import SubscriptUI from './subscript/subscriptui';
+
+/**
+ * The subscript feature.
+ *
+ * It loads the {@link module:basic-styles/subscript/subscriptediting~SubscriptEditing} and
+ * {@link module:basic-styles/subscript/subscriptui~SubscriptUI} plugins.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Subscript extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ SubscriptEditing, SubscriptUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'Subscript';
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/subscript/subscriptediting.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/subscript/subscriptediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import AttributeCommand from '../attributecommand';
+
+const SUBSCRIPT = 'subscript';
+
+/**
+ * The subscript editing feature.
+ *
+ * It registers the `sub` command and introduces the `sub` attribute in the model which renders to the view
+ * as a `<sub>` element.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SubscriptEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		// Allow sub attribute on text nodes.
+		editor.model.schema.extend( '$text', { allowAttributes: SUBSCRIPT } );
+
+		// Build converter from model to view for data and editing pipelines.
+
+		editor.conversion.attributeToElement( {
+			model: SUBSCRIPT,
+			view: 'sub',
+			upcastAlso: [
+				{
+					styles: {
+						'vertical-align': 'sub'
+					}
+				}
+			]
+		} );
+
+		// Create sub command.
+		editor.commands.add( SUBSCRIPT, new AttributeCommand( editor, SUBSCRIPT ) );
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/subscript/subscriptui.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/subscript/subscriptui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import subscriptIcon from '../../theme/icons/subscript.svg';
+
+const SUBSCRIPT = 'subscript';
+
+/**
+ * The subscript UI feature. It introduces the Subscript button.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SubscriptUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		// Add subscript button to feature components.
+		editor.ui.componentFactory.add( SUBSCRIPT, locale => {
+			const command = editor.commands.get( SUBSCRIPT );
+			const view = new ButtonView( locale );
+
+			view.set( {
+				label: t( 'Subscript' ),
+				icon: subscriptIcon,
+				tooltip: true
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => editor.execute( SUBSCRIPT ) );
+
+			return view;
+		} );
+	}
+}

+ 36 - 0
packages/ckeditor5-basic-styles/src/superscript.js

@@ -0,0 +1,36 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/superscript
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import SuperscriptEditing from './superscript/superscriptediting';
+import SuperscriptUI from './superscript/superscriptui';
+
+/**
+ * The superscript feature.
+ *
+ * It loads the {@link module:basic-styles/superscript/superscriptediting~SuperscriptEditing} and
+ * {@link module:basic-styles/superscript/superscriptui~SuperscriptUI} plugins.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Superscript extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ SuperscriptEditing, SuperscriptUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'Superscript';
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/superscript/superscriptediting.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/superscript/superscriptediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import AttributeCommand from '../attributecommand';
+
+const SUPERSCRIPT = 'superscript';
+
+/**
+ * The superscript editing feature.
+ *
+ * It registers the `super` command and introduces the `super` attribute in the model which renders to the view
+ * as a `<super>` element.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SuperscriptEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		// Allow super attribute on text nodes.
+		editor.model.schema.extend( '$text', { allowAttributes: SUPERSCRIPT } );
+
+		// Build converter from model to view for data and editing pipelines.
+
+		editor.conversion.attributeToElement( {
+			model: SUPERSCRIPT,
+			view: 'sup',
+			upcastAlso: [
+				{
+					styles: {
+						'vertical-align': 'super'
+					}
+				}
+			]
+		} );
+
+		// Create super command.
+		editor.commands.add( SUPERSCRIPT, new AttributeCommand( editor, SUPERSCRIPT ) );
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/superscript/superscriptui.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/superscript/superscriptui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import superscriptIcon from '../../theme/icons/superscript.svg';
+
+const SUPERSCRIPT = 'superscript';
+
+/**
+ * The superscript UI feature. It introduces the Superscript button.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SuperscriptUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		// Add superscript button to feature components.
+		editor.ui.componentFactory.add( SUPERSCRIPT, locale => {
+			const command = editor.commands.get( SUPERSCRIPT );
+			const view = new ButtonView( locale );
+
+			view.set( {
+				label: t( 'Superscript' ),
+				icon: superscriptIcon,
+				tooltip: true
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => editor.execute( SUPERSCRIPT ) );
+
+			return view;
+		} );
+	}
+}

+ 1 - 1
packages/ckeditor5-basic-styles/tests/manual/basic-styles.html

@@ -1,3 +1,3 @@
 <div id="editor">
-	<p><i>This</i> <s>is</s> <code>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>, X<sub>1</sub>, X<sup>2</sup>.</p>
 </div>

+ 4 - 2
packages/ckeditor5-basic-styles/tests/manual/basic-styles.js

@@ -13,11 +13,13 @@ import Italic from '../../src/italic';
 import Strikethrough from '../../src/strikethrough';
 import Underline from '../../src/underline';
 import Code from '../../src/code';
+import Subscript from '../../src/subscript';
+import Superscript from '../../src/Superscript';
 
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
-		plugins: [ Essentials, Paragraph, Bold, Italic, Strikethrough, Underline, Code ],
-		toolbar: [ 'bold', 'italic', 'strikethrough', 'underline', 'code', 'undo', 'redo' ]
+		plugins: [ Essentials, Paragraph, Bold, Italic, Strikethrough, Underline, Code, Subscript, Superscript ],
+		toolbar: [ 'bold', 'italic', 'strikethrough', 'underline', 'code', 'undo', 'redo', 'subscript', 'superscript' ]
 	} )
 	.then( editor => {
 		window.editor = editor;

+ 4 - 2
packages/ckeditor5-basic-styles/tests/manual/basic-styles.md

@@ -5,5 +5,7 @@
   * bold **"editor"**,
   * underline "instance",
   * strikethrough ~~"is"~~,
-  * code `"an"`.
-2. Test the bold, italic, strikethrough, underline and code features live.
+  * code `"an"`,
+  * subscript X<sub>1</sub>,
+  * superscript X<sup>2</sup>.
+2. Test the bold, italic, strikethrough, underline, code, subscript and superscript features live.

+ 18 - 0
packages/ckeditor5-basic-styles/tests/subscript.js

@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Subscript from '../src/subscript';
+import SubEditing from '../src/subscript/subscriptediting';
+import SubUI from '../src/subscript/subscriptui';
+
+describe( 'Subscript', () => {
+	it( 'should require SubEditing and SubUI', () => {
+		expect( Subscript.requires ).to.deep.equal( [ SubEditing, SubUI ] );
+	} );
+
+	it( 'should be named', () => {
+		expect( Subscript.pluginName ).to.equal( 'Subscript' );
+	} );
+} );

+ 87 - 0
packages/ckeditor5-basic-styles/tests/subscript/subscriptediting.js

@@ -0,0 +1,87 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import SubEditing from '../../src/subscript/subscriptediting';
+
+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( 'SubEditing', () => {
+	let editor, model;
+
+	beforeEach( () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ Paragraph, SubEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should be loaded', () => {
+		expect( editor.plugins.get( SubEditing ) ).to.be.instanceOf( SubEditing );
+	} );
+
+	it( 'should set proper schema rules', () => {
+		expect( model.schema.checkAttribute( [ '$root', '$block', '$text' ], 'subscript' ) ).to.be.true;
+		expect( model.schema.checkAttribute( [ '$clipboardHolder', '$text' ], 'subscript' ) ).to.be.true;
+	} );
+
+	describe( 'command', () => {
+		it( 'should register subscript command', () => {
+			const command = editor.commands.get( 'subscript' );
+
+			expect( command ).to.be.instanceOf( AttributeCommand );
+			expect( command ).to.have.property( 'attributeKey', 'subscript' );
+		} );
+	} );
+
+	describe( 'data pipeline conversions', () => {
+		it( 'should convert <sub> to sub attribute', () => {
+			editor.setData( '<p><sub>foo</sub>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text subscript="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+
+		it( 'should convert vertical-align:sub to sub attribute', () => {
+			editor.setData( '<p><span style="vertical-align: sub;">foo</span>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text subscript="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+
+		it( 'should be integrated with autoparagraphing', () => {
+			editor.setData( '<sub>foo</sub>bar' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text subscript="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+	} );
+
+	describe( 'editing pipeline conversion', () => {
+		it( 'should convert attribute', () => {
+			setModelData( model, '<paragraph><$text subscript="true">foo</$text>bar</paragraph>' );
+
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+	} );
+} );

+ 68 - 0
packages/ckeditor5-basic-styles/tests/subscript/subscriptui.js

@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import SubscriptEditing from '../../src/subscript/subscriptediting';
+import SubscriptUI from '../../src/subscript/subscriptui';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+
+describe( 'SubscriptUI', () => {
+	let editor, subView;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		const editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ Paragraph, SubscriptEditing, SubscriptUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+
+				subView = editor.ui.componentFactory.create( 'subscript' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should register subscript feature component', () => {
+		expect( subView ).to.be.instanceOf( ButtonView );
+		expect( subView.isOn ).to.be.false;
+		expect( subView.label ).to.equal( 'Subscript' );
+		expect( subView.icon ).to.match( /<svg / );
+	} );
+
+	it( 'should execute subscript command on model execute event', () => {
+		const executeSpy = testUtils.sinon.spy( editor, 'execute' );
+
+		subView.fire( 'execute' );
+
+		sinon.assert.calledOnce( executeSpy );
+		sinon.assert.calledWithExactly( executeSpy, 'subscript' );
+	} );
+
+	it( 'should bind model to subscript command', () => {
+		const command = editor.commands.get( 'subscript' );
+
+		expect( subView.isOn ).to.be.false;
+		expect( subView.isEnabled ).to.be.true;
+
+		command.value = true;
+		expect( subView.isOn ).to.be.true;
+
+		command.isEnabled = false;
+		expect( subView.isEnabled ).to.be.false;
+	} );
+} );

+ 18 - 0
packages/ckeditor5-basic-styles/tests/superscript.js

@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Superscript from '../src/superscript';
+import SuperEditing from '../src/superscript/superscriptediting';
+import SuperUI from '../src/superscript/superscriptui';
+
+describe( 'Superscript', () => {
+	it( 'should require SuperEditing and SuperUI', () => {
+		expect( Superscript.requires ).to.deep.equal( [ SuperEditing, SuperUI ] );
+	} );
+
+	it( 'should be named', () => {
+		expect( Superscript.pluginName ).to.equal( 'Superscript' );
+	} );
+} );

+ 87 - 0
packages/ckeditor5-basic-styles/tests/superscript/superscriptediting.js

@@ -0,0 +1,87 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import SuperEditing from '../../src/superscript/superscriptediting';
+
+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( 'SuperEditing', () => {
+	let editor, model;
+
+	beforeEach( () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ Paragraph, SuperEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should be loaded', () => {
+		expect( editor.plugins.get( SuperEditing ) ).to.be.instanceOf( SuperEditing );
+	} );
+
+	it( 'should set proper schema rules', () => {
+		expect( model.schema.checkAttribute( [ '$root', '$block', '$text' ], 'superscript' ) ).to.be.true;
+		expect( model.schema.checkAttribute( [ '$clipboardHolder', '$text' ], 'superscript' ) ).to.be.true;
+	} );
+
+	describe( 'command', () => {
+		it( 'should register superscript command', () => {
+			const command = editor.commands.get( 'superscript' );
+
+			expect( command ).to.be.instanceOf( AttributeCommand );
+			expect( command ).to.have.property( 'attributeKey', 'superscript' );
+		} );
+	} );
+
+	describe( 'data pipeline conversions', () => {
+		it( 'should convert <sup> to superscript attribute', () => {
+			editor.setData( '<p><sup>foo</sup>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text superscript="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+
+		it( 'should convert vertical-align:super to super attribute', () => {
+			editor.setData( '<p><span style="vertical-align: super;">foo</span>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text superscript="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+
+		it( 'should be integrated with autoparagraphing', () => {
+			editor.setData( '<sup>foo</sup>bar' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text superscript="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+	} );
+
+	describe( 'editing pipeline conversion', () => {
+		it( 'should convert attribute', () => {
+			setModelData( model, '<paragraph><$text superscript="true">foo</$text>bar</paragraph>' );
+
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+	} );
+} );

+ 68 - 0
packages/ckeditor5-basic-styles/tests/superscript/superscriptui.js

@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import SuperscriptEditing from '../../src/superscript/superscriptediting';
+import SuperscriptUI from '../../src/superscript/superscriptui';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+
+describe( 'SuperscriptUI', () => {
+	let editor, superView;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		const editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ Paragraph, SuperscriptEditing, SuperscriptUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+
+				superView = editor.ui.componentFactory.create( 'superscript' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should register superscript feature component', () => {
+		expect( superView ).to.be.instanceOf( ButtonView );
+		expect( superView.isOn ).to.be.false;
+		expect( superView.label ).to.equal( 'Superscript' );
+		expect( superView.icon ).to.match( /<svg / );
+	} );
+
+	it( 'should execute superscript command on model execute event', () => {
+		const executeSpy = testUtils.sinon.spy( editor, 'execute' );
+
+		superView.fire( 'execute' );
+
+		sinon.assert.calledOnce( executeSpy );
+		sinon.assert.calledWithExactly( executeSpy, 'superscript' );
+	} );
+
+	it( 'should bind model to superscript command', () => {
+		const command = editor.commands.get( 'superscript' );
+
+		expect( superView.isOn ).to.be.false;
+		expect( superView.isEnabled ).to.be.true;
+
+		command.value = true;
+		expect( superView.isOn ).to.be.true;
+
+		command.isEnabled = false;
+		expect( superView.isEnabled ).to.be.false;
+	} );
+} );

+ 10 - 0
packages/ckeditor5-basic-styles/theme/icons/subscript.svg

@@ -0,0 +1,10 @@
+<svg viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="subscript" fill="#000000">
+            <g id="Group" transform="translate(3.000000, 6.000000)">
+                <polygon id="x" points="0.900000095 8.89257812 1.91953135 8.69042969 4.72324228 5.19238281 2.00742197 1.80859375 0.98789072 1.60644531 0.98789072 0.490234375 4.71445322 0.490234375 4.71445322 1.60644531 3.8531251 1.71191406 5.62851572 3.99707031 7.41269541 1.703125 6.56015635 1.60644531 6.56015635 0.490234375 10.313086 0.490234375 10.313086 1.60644531 9.29355478 1.80859375 6.57773447 5.19238281 9.37265635 8.69042969 10.4009767 8.89257812 10.4009767 10 6.67441416 10 6.67441416 8.89257812 7.51816416 8.79589844 5.65488291 6.40527344 3.79160166 8.79589844 4.64414072 8.89257812 4.64414072 10 0.900000095 10"></polygon>
+                <path d="M11.0905438,11.7695312 L11.0905438,11.0234375 L12.9303876,9.05078125 C13.1960139,8.74609223 13.3848141,8.48893334 13.4967938,8.27929688 C13.6087735,8.06966041 13.6647626,7.8750009 13.6647626,7.6953125 C13.6647626,7.45572797 13.6003101,7.25976639 13.4714032,7.10742188 C13.3424963,6.95507736 13.1595554,6.87890625 12.9225751,6.87890625 C12.6595529,6.87890625 12.4596851,6.96809807 12.3229657,7.14648438 C12.1862462,7.32487068 12.1178876,7.56249852 12.1178876,7.859375 L11.0085126,7.859375 L11.0007001,7.8359375 C10.9876792,7.3229141 11.1562973,6.88867365 11.5065594,6.53320312 C11.8568216,6.1777326 12.3288221,6 12.9225751,6 C13.5085155,6 13.9694484,6.15234223 14.3053876,6.45703125 C14.6413267,6.76172027 14.8092938,7.16796621 14.8092938,7.67578125 C14.8092938,8.01953297 14.7148937,8.33723812 14.5260907,8.62890625 C14.3372877,8.92057438 14.0241397,9.30077891 13.5866376,9.76953125 L12.5749188,10.875 L12.5827313,10.8945312 L14.0592938,10.8945312 L14.1061688,10.3320312 L14.9733563,10.3320312 L14.9733563,11.7695312 L11.0905438,11.7695312 Z" id="2"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 10 - 0
packages/ckeditor5-basic-styles/theme/icons/superscript.svg

@@ -0,0 +1,10 @@
+<svg viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="superscript" fill="#000000">
+            <g id="Group-2" transform="translate(3.000000, 3.000000)">
+                <polygon id="x" points="0.900000095 11.8925781 1.91953135 11.6904297 4.72324228 8.19238281 2.00742197 4.80859375 0.98789072 4.60644531 0.98789072 3.49023438 4.71445322 3.49023438 4.71445322 4.60644531 3.8531251 4.71191406 5.62851572 6.99707031 7.41269541 4.703125 6.56015635 4.60644531 6.56015635 3.49023438 10.313086 3.49023438 10.313086 4.60644531 9.29355478 4.80859375 6.57773447 8.19238281 9.37265635 11.6904297 10.4009767 11.8925781 10.4009767 13 6.67441416 13 6.67441416 11.8925781 7.51816416 11.7958984 5.65488291 9.40527344 3.79160166 11.7958984 4.64414072 11.8925781 4.64414072 13 0.900000095 13"></polygon>
+                <path d="M11.0905438,5.76953125 L11.0905438,5.0234375 L12.9303876,3.05078125 C13.1960139,2.74609223 13.3848141,2.48893334 13.4967938,2.27929688 C13.6087735,2.06966041 13.6647626,1.8750009 13.6647626,1.6953125 C13.6647626,1.45572797 13.6003101,1.25976639 13.4714032,1.10742188 C13.3424963,0.955077363 13.1595554,0.87890625 12.9225751,0.87890625 C12.6595529,0.87890625 12.4596851,0.968098066 12.3229657,1.14648438 C12.1862462,1.32487068 12.1178876,1.56249852 12.1178876,1.859375 L11.0085126,1.859375 L11.0007001,1.8359375 C10.9876792,1.3229141 11.1562973,0.888673652 11.5065594,0.533203125 C11.8568216,0.177732598 12.3288221,0 12.9225751,0 C13.5085155,0 13.9694484,0.152342227 14.3053876,0.45703125 C14.6413267,0.761720273 14.8092938,1.16796621 14.8092938,1.67578125 C14.8092938,2.01953297 14.7148937,2.33723812 14.5260907,2.62890625 C14.3372877,2.92057438 14.0241397,3.30077891 13.5866376,3.76953125 L12.5749188,4.875 L12.5827313,4.89453125 L14.0592938,4.89453125 L14.1061688,4.33203125 L14.9733563,4.33203125 L14.9733563,5.76953125 L11.0905438,5.76953125 Z" id="2"></path>
+            </g>
+        </g>
+    </g>
+</svg>