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

The initial implementation of the select all feature.

Aleksander Nowodzinski 5 лет назад
Родитель
Сommit
988723cf9e
25 измененных файлов с 624 добавлено и 3 удалено
  1. 16 0
      packages/ckeditor5-select-all/.editorconfig
  2. 12 0
      packages/ckeditor5-select-all/.eslintrc.js
  3. 18 0
      packages/ckeditor5-select-all/.gitattributes
  4. 9 0
      packages/ckeditor5-select-all/.github/PULL_REQUEST_TEMPLATE.md
  5. 1 0
      packages/ckeditor5-select-all/.gitignore
  6. 3 0
      packages/ckeditor5-select-all/.stylelintrc
  7. 31 0
      packages/ckeditor5-select-all/.travis.yml
  8. 2 0
      packages/ckeditor5-select-all/CHANGELOG.md
  9. 4 0
      packages/ckeditor5-select-all/CONTRIBUTING.md
  10. 17 0
      packages/ckeditor5-select-all/LICENSE.md
  11. 10 1
      packages/ckeditor5-select-all/README.md
  12. 16 2
      packages/ckeditor5-select-all/package.json
  13. 38 0
      packages/ckeditor5-select-all/src/selectall.js
  14. 39 0
      packages/ckeditor5-select-all/src/selectallcommand.js
  15. 46 0
      packages/ckeditor5-select-all/src/selectallediting.js
  16. 58 0
      packages/ckeditor5-select-all/src/selectallui.js
  17. BIN
      packages/ckeditor5-select-all/tests/manual/sample.jpg
  18. 4 0
      packages/ckeditor5-select-all/tests/manual/selectall.html
  19. 27 0
      packages/ckeditor5-select-all/tests/manual/selectall.js
  20. 15 0
      packages/ckeditor5-select-all/tests/manual/selectall.md
  21. 18 0
      packages/ckeditor5-select-all/tests/selectall.js
  22. 79 0
      packages/ckeditor5-select-all/tests/selectallcommand.js
  23. 80 0
      packages/ckeditor5-select-all/tests/selectallediting.js
  24. 80 0
      packages/ckeditor5-select-all/tests/selectallui.js
  25. 1 0
      packages/ckeditor5-select-all/theme/icons/select-all.svg

+ 16 - 0
packages/ckeditor5-select-all/.editorconfig

@@ -0,0 +1,16 @@
+# Configurations to normalize the IDE behavior.
+# http://editorconfig.org/
+
+root = true
+
+[*]
+indent_style = tab
+tab_width = 4
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[package.json]
+indent_style = space
+tab_width = 2

+ 12 - 0
packages/ckeditor5-select-all/.eslintrc.js

@@ -0,0 +1,12 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* eslint-env node */
+
+'use strict';
+
+module.exports = {
+	extends: 'ckeditor5'
+};

+ 18 - 0
packages/ckeditor5-select-all/.gitattributes

@@ -0,0 +1,18 @@
+*			text=auto
+
+*.htaccess	eol=lf
+*.cgi		eol=lf
+*.sh		eol=lf
+
+*.css		text
+*.htm		text
+*.html		text
+*.js		text
+*.json		text
+*.php		text
+*.txt		text
+*.md		text
+
+*.png		-text
+*.gif		-text
+*.jpg		-text

+ 9 - 0
packages/ckeditor5-select-all/.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,9 @@
+### Suggested merge commit message ([convention](https://github.com/ckeditor/ckeditor5-design/wiki/Git-commit-message-convention))
+
+Type: Message. Closes ckeditor/ckeditor5#000.
+
+---
+
+### Additional information
+
+*For example – encountered issues, assumptions you had to make, other affected tickets, etc.*

+ 1 - 0
packages/ckeditor5-select-all/.gitignore

@@ -0,0 +1 @@
+node_modules/

+ 3 - 0
packages/ckeditor5-select-all/.stylelintrc

@@ -0,0 +1,3 @@
+{
+	"extends": "stylelint-config-ckeditor5"
+}

+ 31 - 0
packages/ckeditor5-select-all/.travis.yml

@@ -0,0 +1,31 @@
+sudo: required
+dist: xenial
+addons:
+  chrome: stable
+  firefox: latest
+language: node_js
+services:
+- xvfb
+node_js:
+- '10'
+cache:
+  yarn: true
+branches:
+  except:
+  - stable
+before_install:
+- export START_TIME=$( date +%s )
+- npm i -g yarn
+install:
+- yarn add @ckeditor/ckeditor5-dev-tests
+- ckeditor5-dev-tests-install-dependencies
+script:
+- ckeditor5-dev-tests-travis
+after_success:
+- ckeditor5-dev-tests-save-revision
+after_script:
+- export END_TIME=$( date +%s )
+- ckeditor5-dev-tests-notify-travis-status
+env:
+  global:
+    secure: E8sNxgxevWnr96lkpBGsQr+jzkaDCLcqHgbZxpx8cCdU9gLpS13DXeROVuMare2eGpfc7RgnJ52SKit8Z4V5WOgUF6jLLqWitOL116wTnDKgHuAW79+qpO0JNXBcBy5dCqk0Lb/SMXbDh3UtRIPCIF1cF+DhmxdaVKyxAjR9ShI7P7Voz3yMy/XA7np8ANBthe7Mlf44E8mXFatN85xEAPhrnpZBaLnKIvfYjxZ86TM2dI/Bqz/6GUry6ZFoAy8knR7leFfrJwRBCMqkDah9YhIH1zh7HA70V1dC9k56dIiaBzQDcmcZ4fKLZtWFVJa5oGl0LdZSZm8ud//9P1ULGLJ9sI4Bp02xfy2FXOAnKdpEIC5suIXwhdvsvQSfyAr4xuBmmRdGR6YJpLdBJdZd4NaTIH9msCEaaksCDXkSa4FYXbLA01EmfqUD9MjDzECtkcW/dzT/8gG5SiAsR40wOsaKRpUBxv9OeEt1EW0kQkfHtVUysIZhTzrZjW86S/RoNTECqLC9zVva57aOGglwilRlkS7/YOtd16u30QHxYk2zyKXXzqu9YMN2GwtJtw6+T2pKc0D0wS1d9NfmwE1VkDuqJ+CnLyVGzqs81wNqgkj5w/MNEGP/6ZpptAWskkf0TDIz/iIUAz45DJMq8cGivlcl2FGdNK5wOEZf6T8v+9s=

+ 2 - 0
packages/ckeditor5-select-all/CHANGELOG.md

@@ -0,0 +1,2 @@
+Changelog
+=========

+ 4 - 0
packages/ckeditor5-select-all/CONTRIBUTING.md

@@ -0,0 +1,4 @@
+Contributing
+========================================
+
+See the [official contributors' guide to CKEditor 5](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html) to learn more.

+ 17 - 0
packages/ckeditor5-select-all/LICENSE.md

@@ -0,0 +1,17 @@
+Software License Agreement
+==========================
+
+**CKEditor 5 Select All Feature** – https://github.com/ckeditor/ckeditor5-select-all <br>
+Copyright (c) 2003-2020, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
+
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
+
+Sources of Intellectual Property Included in CKEditor
+-----------------------------------------------------
+
+Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
+
+Trademarks
+----------
+
+**CKEditor** is a trademark of [CKSource](http://cksource.com) Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.

+ 10 - 1
packages/ckeditor5-select-all/README.md

@@ -8,4 +8,13 @@ CKEditor 5 select all feature
 [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-select-all/status.svg)](https://david-dm.org/ckeditor/ckeditor5-select-all)
 [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-select-all/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-select-all?type=dev)
 
-This is an initial package for development purposes. It does not contain code yet.
+This package implements the select all feature for CKEditor 5.
+
+## Documentation
+
+See the [`@ckeditor/ckeditor5-select-all` package](https://ckeditor.com/docs/ckeditor5/latest/api/select-all.html) in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
+
+## License
+
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
+

+ 16 - 2
packages/ckeditor5-select-all/package.json

@@ -8,11 +8,21 @@
     "ckeditor 5",
     "ckeditor5-lib"
   ],
+  "dependencies": {
+    "@ckeditor/ckeditor5-core": "^18.0.0",
+    "@ckeditor/ckeditor5-ui": "^18.0.0",
+    "@ckeditor/ckeditor5-utils": "^18.0.0"
+  },
   "devDependencies": {
+    "@ckeditor/ckeditor5-engine": "^18.0.0",
+    "@ckeditor/ckeditor5-image": "^18.0.0",
+    "@ckeditor/ckeditor5-paragraph": "^18.0.0",
     "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^2.0.0",
     "husky": "^2.4.1",
-    "lint-staged": "^8.2.1"
+    "lint-staged": "^8.2.1",
+    "stylelint": "^11.1.1",
+    "stylelint-config-ckeditor5": "^1.0.0"
   },
   "engines": {
     "node": ">=8.0.0",
@@ -32,11 +42,15 @@
     "theme"
   ],
   "scripts": {
-    "lint": "eslint --quiet '**/*.js'"
+    "lint": "eslint --quiet '**/*.js'",
+    "stylelint": "stylelint --quiet --allow-empty-input 'theme/**/*.css' 'docs/**/*.css'"
   },
   "lint-staged": {
     "**/*.js": [
       "eslint --quiet"
+    ],
+    "**/*.css": [
+      "stylelint --quiet --allow-empty-input"
     ]
   },
   "eslintIgnore": [

+ 38 - 0
packages/ckeditor5-select-all/src/selectall.js

@@ -0,0 +1,38 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module selectall/selectall
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import SelectAllEditing from './selectallediting';
+import SelectAllUI from './selectallui';
+
+/**
+ * The select all feature.
+ *
+ * This is a "glue" plugin which loads the {@link module:selectall/selectallediting~SelectAllEditing select all editing feature}
+ * and the {@link module:selectall/selectallui~SelectAllUI select all UI feature}.
+ *
+ * Please refer to the documentation of individual features to learn more.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SelectAll extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ SelectAllEditing, SelectAllUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SelectAll';
+	}
+}

+ 39 - 0
packages/ckeditor5-select-all/src/selectallcommand.js

@@ -0,0 +1,39 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module selectall/selectallcommand
+ */
+
+import Command from '@ckeditor/ckeditor5-core/src/command';
+
+/**
+ * The select all command.
+ *
+ * It is used by the {@link module:selectall/selectallediting~SelectAllEditing select all editing feature} to handle
+ * the <kbd>Ctrl</kbd>+<kbd>A</kbd> keystroke.
+ *
+ * Executing this command changes the {@glink framework/guides/architecture/editing-engine#model model}
+ * selection so it contains the entire content of the editable root of the editor the selection is
+ * {@link module:engine/model/selection~Selection#anchor anchored} in.
+ *
+ * If the selection was anchored in a {@link framework/guides/tutorials/implementing-a-block-widget nested editable}
+ * (e.g. a caption of an image), the new selection will contain its entire content.
+ *
+ * @extends module:core/command~Command
+ */
+export default class SelectAllCommand extends Command {
+	/**
+	 * @inheritDoc
+	 */
+	execute() {
+		const model = this.editor.model;
+		const limitElement = model.schema.getLimitElement( model.document.selection );
+
+		model.change( writer => {
+			writer.setSelection( limitElement, 'in' );
+		} );
+	}
+}

+ 46 - 0
packages/ckeditor5-select-all/src/selectallediting.js

@@ -0,0 +1,46 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module selectall/selectallediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import { getCode, parseKeystroke } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import SelectAllCommand from './selectallcommand';
+
+const SELECT_ALL_KEYSTROKE = parseKeystroke( 'Ctrl+A' );
+
+/**
+ * The select all editing feature.
+ *
+ * It registers the `'selectAll'` {@link module:selectall/selectallcommand~SelectAllCommand command}
+ * and the <kbd>Ctrl</kbd>+<kbd>A</kbd> keystroke listener which executes it.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SelectAllEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SelectAllEditing';
+	}
+
+	init() {
+		const editor = this.editor;
+		const view = editor.editing.view;
+		const viewDocument = view.document;
+
+		editor.commands.add( 'selectAll', new SelectAllCommand( editor ) );
+
+		this.listenTo( viewDocument, 'keydown', ( eventInfo, domEventData ) => {
+			if ( getCode( domEventData ) === SELECT_ALL_KEYSTROKE ) {
+				editor.execute( 'selectAll' );
+				domEventData.preventDefault();
+			}
+		} );
+	}
+}

+ 58 - 0
packages/ckeditor5-select-all/src/selectallui.js

@@ -0,0 +1,58 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module selectall/selectallediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import selectAllIcon from '../theme/icons/select-all.svg';
+
+/**
+ * The select all UI feature.
+ *
+ * It registers the `'selectAll'` UI button in the editor's
+ * {@link module:ui/componentfactory~ComponentFactory component factory}. When clicked, the button
+ * executes the {@link module:selectall/selectallcommand~SelectAllCommand select all command}.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SelectAllUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SelectAllUI';
+	}
+
+	init() {
+		const editor = this.editor;
+
+		editor.ui.componentFactory.add( 'selectAll', locale => {
+			const command = editor.commands.get( 'selectAll' );
+			const view = new ButtonView( locale );
+			const t = locale.t;
+
+			view.set( {
+				label: t( 'Select all' ),
+				icon: selectAllIcon,
+				keystroke: 'CTRL+A',
+				tooltip: true
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => {
+				editor.execute( 'selectAll' );
+				editor.editing.view.focus();
+			} );
+
+			return view;
+		} );
+	}
+}

BIN
packages/ckeditor5-select-all/tests/manual/sample.jpg


+ 4 - 0
packages/ckeditor5-select-all/tests/manual/selectall.html

@@ -0,0 +1,4 @@
+<div id="editor">
+	<p>This is an <a href="https://ckeditor.com">editor</a> instance.</p>
+	<figure class="image"><img src="./sample.jpg"><figcaption>Caption</figcaption></figure>
+</div>

+ 27 - 0
packages/ckeditor5-select-all/tests/manual/selectall.js

@@ -0,0 +1,27 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* globals console:false, window, document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ Essentials, Paragraph, Bold, Italic, Heading, Image, ImageCaption ],
+		toolbar: [ 'heading', '|', 'bold', 'italic', '|', 'selectAll' ]
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 15 - 0
packages/ckeditor5-select-all/tests/manual/selectall.md

@@ -0,0 +1,15 @@
+## The select all feature
+
+### Selecting the entire editor content
+
+1. Put the selection anywhere but inside the image caption.
+2. Press <kbd>Ctrl</kbd>+<kbd>A</kbd>.
+3. The entire content should be selected. You should be able to remove it using the <kbd>Backspace</kbd> key.
+4. Repeat the scenario but instead of the keystroke use the toolbar button.
+
+### Selecting the nested editable content
+
+1. Put the selection inside the image caption.
+2. Press <kbd>Ctrl</kbd>+<kbd>A</kbd>.
+3. The caption content should be selected. **The selection should not go beyond the nested editable.**
+4. Repeat the scenario but instead of the keystroke use the toolbar button.

+ 18 - 0
packages/ckeditor5-select-all/tests/selectall.js

@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import SelectAll from '../src/selectall';
+import SelectAllEditing from '../src/selectallediting';
+import SelectAllUI from '../src/selectallui';
+
+describe( 'SelectAll', () => {
+	it( 'should require SelectAllEditing and SelectAllUI', () => {
+		expect( SelectAll.requires ).to.deep.equal( [ SelectAllEditing, SelectAllUI ] );
+	} );
+
+	it( 'should be named', () => {
+		expect( SelectAll.pluginName ).to.equal( 'SelectAll' );
+	} );
+} );

+ 79 - 0
packages/ckeditor5-select-all/tests/selectallcommand.js

@@ -0,0 +1,79 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import SelectAllEditing from '../src/selectallediting';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
+import ImageCaptionEditing from '@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionediting';
+import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+describe( 'SelectAllCommand', () => {
+	let editor, model, command;
+
+	beforeEach( () => {
+		return ModelTestEditor
+			.create( {
+				plugins: [ SelectAllEditing, Paragraph, ImageEditing, ImageCaptionEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+				command = editor.commands.get( 'selectAll' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	describe( 'isEnabled', () => {
+		it( 'should always be "true" because the command is stateless', () => {
+			expect( command.isEnabled ).to.be.true;
+		} );
+	} );
+
+	describe( 'execute()', () => {
+		it( 'should select all (collapsed selection in a block with text)', () => {
+			setData( model, '<paragraph>f[]oo</paragraph>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo]</paragraph>' );
+		} );
+
+		it( 'should select all (collapsed selection in a content with an object)', () => {
+			setData( model, '<paragraph>fo[]o</paragraph><image src="foo.png"><caption></caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph><image src="foo.png"><caption></caption></image>]' );
+		} );
+
+		it( 'should select all (selection on an object)', () => {
+			setData( model, '<paragraph>foo</paragraph>[<image src="foo.png"><caption></caption></image>]' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>[foo</paragraph><image src="foo.png"><caption></caption></image>]' );
+		} );
+
+		it( 'should select all (collapsed selection in a nested editable)', () => {
+			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>b[]ar</caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+		} );
+
+		it( 'should select all (selection in a nested editable)', () => {
+			setData( model, '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+
+			editor.execute( 'selectAll' );
+
+			expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph><image src="foo.png"><caption>[bar]</caption></image>' );
+		} );
+	} );
+} );

+ 80 - 0
packages/ckeditor5-select-all/tests/selectallediting.js

@@ -0,0 +1,80 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import SelectAllEditing from '../src/selectallediting';
+import SelectAllCommand from '../src/selectallcommand';
+import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
+
+describe( 'SelectAllEditing', () => {
+	let editor, viewDocument;
+
+	beforeEach( () => {
+		return ModelTestEditor
+			.create( {
+				plugins: [ SelectAllEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				viewDocument = editor.editing.view.document;
+
+				sinon.spy( editor, 'execute' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should have a name', () => {
+		expect( SelectAllEditing.pluginName ).to.equal( 'SelectAllEditing' );
+	} );
+
+	it( 'should register the "selectAll" command', () => {
+		const command = editor.commands.get( 'selectAll' );
+
+		expect( command ).to.be.instanceOf( SelectAllCommand );
+	} );
+
+	describe( 'Ctrl+A keystroke listener', () => {
+		it( 'should execute the "selectAll" command', () => {
+			const domEventDataMock = {
+				keyCode: keyCodes.a,
+				ctrlKey: true,
+				preventDefault: sinon.spy()
+			};
+
+			viewDocument.fire( 'keydown', domEventDataMock );
+
+			sinon.assert.calledOnce( editor.execute );
+			sinon.assert.calledWithExactly( editor.execute, 'selectAll' );
+		} );
+
+		it( 'should prevent the default action', () => {
+			const domEventDataMock = {
+				keyCode: keyCodes.a,
+				ctrlKey: true,
+				preventDefault: sinon.spy()
+			};
+
+			viewDocument.fire( 'keydown', domEventDataMock );
+
+			sinon.assert.calledOnce( domEventDataMock.preventDefault );
+		} );
+
+		it( 'should not react to other keystrokes', () => {
+			const domEventDataMock = {
+				keyCode: keyCodes.x,
+				ctrlKey: true,
+				preventDefault: sinon.spy()
+			};
+
+			viewDocument.fire( 'keydown', domEventDataMock );
+
+			sinon.assert.notCalled( editor.execute );
+			sinon.assert.notCalled( domEventDataMock.preventDefault );
+		} );
+	} );
+} );

+ 80 - 0
packages/ckeditor5-select-all/tests/selectallui.js

@@ -0,0 +1,80 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/* global document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import SelectAllEditing from '../src/selectallediting';
+import SelectAllUI from '../src/selectallui';
+
+describe( 'SelectAllUI', () => {
+	let editor, editorElement, button;
+
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ SelectAllEditing, SelectAllUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				button = editor.ui.componentFactory.create( 'selectAll' );
+			} );
+	} );
+
+	afterEach( () => {
+		editorElement.remove();
+
+		return editor.destroy();
+	} );
+
+	it( 'should have a name', () => {
+		expect( SelectAllUI.pluginName ).to.equal( 'SelectAllUI' );
+	} );
+
+	describe( 'the "selectAll" button', () => {
+		it( 'should be an instance of ButtonView', () => {
+			expect( button ).to.be.instanceOf( ButtonView );
+		} );
+
+		it( 'should have a label', () => {
+			expect( button.label ).to.equal( 'Select all' );
+		} );
+
+		it( 'should have an icon', () => {
+			expect( button.icon ).to.match( /^<svg/ );
+		} );
+
+		it( 'should have a keystroke', () => {
+			expect( button.keystroke ).to.equal( 'CTRL+A' );
+		} );
+
+		it( 'should have a tooltip', () => {
+			expect( button.tooltip ).to.be.true;
+		} );
+
+		it( 'should have #isEnabled bound to the command state', () => {
+			expect( button.isEnabled ).to.be.true;
+
+			editor.commands.get( 'selectAll' ).isEnabled = false;
+
+			expect( button.isEnabled ).to.be.false;
+		} );
+
+		it( 'should execute the "selectAll" command and focus the editing view', () => {
+			sinon.spy( editor, 'execute' );
+			sinon.spy( editor.editing.view, 'focus' );
+
+			button.fire( 'execute' );
+
+			sinon.assert.calledOnce( editor.execute );
+			sinon.assert.calledWithExactly( editor.execute, 'selectAll' );
+			sinon.assert.calledOnce( editor.editing.view.focus );
+		} );
+	} );
+} );

+ 1 - 0
packages/ckeditor5-select-all/theme/icons/select-all.svg

@@ -0,0 +1 @@
+<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M.75 15.5a.75.75 0 0 1 .75.75V18l.008.09A.5.5 0 0 0 2 18.5h1.75a.75.75 0 1 1 0 1.5H1.5l-.144-.007a1.5 1.5 0 0 1-1.35-1.349L0 18.5v-2.25a.75.75 0 0 1 .75-.75zm18.5 0a.75.75 0 0 1 .75.75v2.25l-.007.144a1.5 1.5 0 0 1-1.349 1.35L18.5 20h-2.25a.75.75 0 1 1 0-1.5H18a.5.5 0 0 0 .492-.41L18.5 18v-1.75a.75.75 0 0 1 .75-.75zm-10.45 3c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2H7.2a.2.2 0 0 1-.2-.2v-1.1c0-.11.09-.2.2-.2h1.6zm4 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2h-1.6a.2.2 0 0 1-.2-.2v-1.1c0-.11.09-.2.2-.2h1.6zm.45-5.5a.75.75 0 1 1 0 1.5h-8.5a.75.75 0 1 1 0-1.5h8.5zM1.3 11c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2H.2a.2.2 0 0 1-.2-.2v-1.6c0-.11.09-.2.2-.2h1.1zm18.5 0c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2h-1.1a.2.2 0 0 1-.2-.2v-1.6c0-.11.09-.2.2-.2h1.1zm-4.55-2a.75.75 0 1 1 0 1.5H4.75a.75.75 0 1 1 0-1.5h10.5zM1.3 7c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2H.2a.2.2 0 0 1-.2-.2V7.2c0-.11.09-.2.2-.2h1.1zm18.5 0c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2h-1.1a.2.2 0 0 1-.2-.2V7.2c0-.11.09-.2.2-.2h1.1zm-4.55-2a.75.75 0 1 1 0 1.5h-2.5a.75.75 0 1 1 0-1.5h2.5zm-5 0a.75.75 0 1 1 0 1.5h-5.5a.75.75 0 0 1 0-1.5h5.5zm-6.5-5a.75.75 0 0 1 0 1.5H2a.5.5 0 0 0-.492.41L1.5 2v1.75a.75.75 0 0 1-1.5 0V1.5l.007-.144A1.5 1.5 0 0 1 1.356.006L1.5 0h2.25zM18.5 0l.144.007a1.5 1.5 0 0 1 1.35 1.349L20 1.5v2.25a.75.75 0 1 1-1.5 0V2l-.008-.09A.5.5 0 0 0 18 1.5h-1.75a.75.75 0 1 1 0-1.5h2.25zM8.8 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2H7.2a.2.2 0 0 1-.2-.2V.2c0-.11.09-.2.2-.2h1.6zm4 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2h-1.6a.2.2 0 0 1-.2-.2V.2c0-.11.09-.2.2-.2h1.6z"/></svg>