Parcourir la source

Extracted mouse handlers from the TableSelection plugin to the new TableMouse plugin.

Kuba Niegowski il y a 5 ans
Parent
commit
b362e2454c

+ 2 - 1
packages/ckeditor5-table/src/table.js

@@ -14,6 +14,7 @@ import TableUI from './tableui';
 import TableSelection from './tableselection';
 import TableClipboard from './tableclipboard';
 import TableKeyboard from './tablekeyboard';
+import TableMouse from './tablemouse';
 import Widget from '@ckeditor/ckeditor5-widget/src/widget';
 
 import '../theme/table.css';
@@ -38,7 +39,7 @@ export default class Table extends Plugin {
 	 * @inheritDoc
 	 */
 	static get requires() {
-		return [ TableEditing, TableUI, TableSelection, TableClipboard, TableKeyboard, Widget ];
+		return [ TableEditing, TableUI, TableSelection, TableMouse, TableKeyboard, TableClipboard, Widget ];
 	}
 
 	/**

+ 223 - 0
packages/ckeditor5-table/src/tablemouse.js

@@ -0,0 +1,223 @@
+/**
+ * @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 table/tablemouse
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+
+import TableSelection from './tableselection';
+import MouseEventsObserver from './tablemouse/mouseeventsobserver';
+
+import { findAncestor } from './utils/common';
+import { getTableCellsContainingSelection } from './utils/selection';
+
+/**
+ * This plugin enables a table cells' selection with the mouse.
+ * It is loaded automatically by the {@link module:table/table~Table} plugin.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class TableMouse extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'TableMouse';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ TableSelection ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+
+		// Currently the MouseObserver only handles `mouseup` events.
+		// TODO move to the engine?
+		editor.editing.view.addObserver( MouseEventsObserver );
+
+		this._enableShiftClickSelection();
+		this._enableMouseDragSelection();
+	}
+
+	/**
+	 * Enables making cells selection by <kbd>Shift</kbd>+click. Creates a selection from the cell which previously held
+	 * the selection to the cell which was clicked. It can be the same cell, in which case it selects a single cell.
+	 *
+	 * @private
+	 */
+	_enableShiftClickSelection() {
+		const editor = this.editor;
+		let blockSelectionChange = false;
+
+		const tableSelection = editor.plugins.get( TableSelection );
+
+		this.listenTo( editor.editing.view.document, 'mousedown', ( evt, domEventData ) => {
+			if ( !this.isEnabled || !tableSelection.isEnabled ) {
+				return;
+			}
+
+			if ( !domEventData.domEvent.shiftKey ) {
+				return;
+			}
+
+			const anchorCell = tableSelection.getAnchorCell() || getTableCellsContainingSelection( editor.model.document.selection )[ 0 ];
+
+			if ( !anchorCell ) {
+				return;
+			}
+
+			const targetCell = this._getModelTableCellFromDomEvent( domEventData );
+
+			if ( targetCell && haveSameTableParent( anchorCell, targetCell ) ) {
+				blockSelectionChange = true;
+				tableSelection.setCellSelection( anchorCell, targetCell );
+
+				domEventData.preventDefault();
+			}
+		} );
+
+		this.listenTo( editor.editing.view.document, 'mouseup', () => {
+			blockSelectionChange = false;
+		} );
+
+		// We need to ignore a `selectionChange` event that is fired after we render our new table cells selection.
+		// When downcasting table cells selection to the view, we put the view selection in the last selected cell
+		// in a place that may not be natively a "correct" location. This is – we put it directly in the `<td>` element.
+		// All browsers fire the native `selectionchange` event.
+		// However, all browsers except Safari return the selection in the exact place where we put it
+		// (even though it's visually normalized). Safari returns `<td><p>^foo` that makes our selection observer
+		// fire our `selectionChange` event (because the view selection that we set in the first step differs from the DOM selection).
+		// Since `selectionChange` is fired, we automatically update the model selection that moves it that paragraph.
+		// This breaks our dear cells selection.
+		//
+		// Theoretically this issue concerns only Safari that is the only browser that do normalize the selection.
+		// However, to avoid code branching and to have a good coverage for this event blocker, I enabled it for all browsers.
+		//
+		// Note: I'm keeping the `blockSelectionChange` state separately for shift+click and mouse drag (exact same logic)
+		// so I don't have to try to analyze whether they don't overlap in some weird cases. Probably they don't.
+		// But I have other things to do, like writing this comment.
+		this.listenTo( editor.editing.view.document, 'selectionChange', evt => {
+			if ( blockSelectionChange ) {
+				// @if CK_DEBUG // console.log( 'Blocked selectionChange to avoid breaking table cells selection.' );
+
+				evt.stop();
+			}
+		}, { priority: 'highest' } );
+	}
+
+	/**
+	 * Enables making cells selection by dragging.
+	 *
+	 * The selection is made only on mousemove. Mouse tracking is started on mousedown.
+	 * However, the cells selection is enabled only after the mouse cursor left the anchor cell.
+	 * Thanks to that normal text selection within one cell works just fine. However, you can still select
+	 * just one cell by leaving the anchor cell and moving back to it.
+	 *
+	 * @private
+	 */
+	_enableMouseDragSelection() {
+		const editor = this.editor;
+		let anchorCell, targetCell;
+		let beganCellSelection = false;
+		let blockSelectionChange = false;
+
+		const tableSelection = editor.plugins.get( TableSelection );
+
+		this.listenTo( editor.editing.view.document, 'mousedown', ( evt, domEventData ) => {
+			if ( !this.isEnabled || !tableSelection.isEnabled ) {
+				return;
+			}
+
+			// Make sure to not conflict with the shift+click listener and any other possible handler.
+			if ( domEventData.domEvent.shiftKey || domEventData.domEvent.ctrlKey || domEventData.domEvent.altKey ) {
+				return;
+			}
+
+			anchorCell = this._getModelTableCellFromDomEvent( domEventData );
+		} );
+
+		this.listenTo( editor.editing.view.document, 'mousemove', ( evt, domEventData ) => {
+			if ( !domEventData.domEvent.buttons ) {
+				return;
+			}
+
+			if ( !anchorCell ) {
+				return;
+			}
+
+			const newTargetCell = this._getModelTableCellFromDomEvent( domEventData );
+
+			if ( newTargetCell && haveSameTableParent( anchorCell, newTargetCell ) ) {
+				targetCell = newTargetCell;
+
+				// Switch to the cell selection mode after the mouse cursor left the anchor cell.
+				// Switch off only on mouseup (makes selecting a single cell possible).
+				if ( !beganCellSelection && targetCell != anchorCell ) {
+					beganCellSelection = true;
+				}
+			}
+
+			// Yep, not making a cell selection yet. See method docs.
+			if ( !beganCellSelection ) {
+				return;
+			}
+
+			blockSelectionChange = true;
+			tableSelection.setCellSelection( anchorCell, targetCell );
+
+			domEventData.preventDefault();
+		} );
+
+		this.listenTo( editor.editing.view.document, 'mouseup', () => {
+			beganCellSelection = false;
+			blockSelectionChange = false;
+			anchorCell = null;
+			targetCell = null;
+		} );
+
+		// See the explanation in `_enableShiftClickSelection()`.
+		this.listenTo( editor.editing.view.document, 'selectionChange', evt => {
+			if ( blockSelectionChange ) {
+				// @if CK_DEBUG // console.log( 'Blocked selectionChange to avoid breaking table cells selection.' );
+
+				evt.stop();
+			}
+		}, { priority: 'highest' } );
+	}
+
+	/**
+	 * Returns the model table cell element based on the target element of the passed DOM event.
+	 *
+	 * @private
+	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
+	 * @returns {module:engine/model/element~Element|undefined} Returns the table cell or `undefined`.
+	 */
+	_getModelTableCellFromDomEvent( domEventData ) {
+		// Note: Work with positions (not element mapping) because the target element can be an attribute or other non-mapped element.
+		const viewTargetElement = domEventData.target;
+		const viewPosition = this.editor.editing.view.createPositionAt( viewTargetElement, 0 );
+		const modelPosition = this.editor.editing.mapper.toModelPosition( viewPosition );
+		const modelElement = modelPosition.parent;
+
+		if ( modelElement.is( 'tableCell' ) ) {
+			return modelElement;
+		}
+
+		return findAncestor( 'tableCell', modelElement );
+	}
+}
+
+function haveSameTableParent( cellA, cellB ) {
+	return cellA.parent.parent == cellB.parent.parent;
+}

packages/ckeditor5-table/src/tableselection/mouseeventsobserver.js → packages/ckeditor5-table/src/tablemouse/mouseeventsobserver.js


+ 1 - 175
packages/ckeditor5-table/src/tableselection.js

@@ -12,11 +12,10 @@ import first from '@ckeditor/ckeditor5-utils/src/first';
 
 import TableWalker from './tablewalker';
 import TableUtils from './tableutils';
-import MouseEventsObserver from './tableselection/mouseeventsobserver';
 
 import { findAncestor } from './utils/common';
 import { cropTableToDimensions } from './utils/structure';
-import { getColumnIndexes, getRowIndexes, getSelectedTableCells, getTableCellsContainingSelection } from './utils/selection';
+import { getColumnIndexes, getRowIndexes, getSelectedTableCells } from './utils/selection';
 
 import '../theme/tableselection.css';
 
@@ -50,13 +49,7 @@ export default class TableSelection extends Plugin {
 
 		this.listenTo( model, 'deleteContent', ( evt, args ) => this._handleDeleteContent( evt, args ), { priority: 'high' } );
 
-		// Currently the MouseObserver only handles `mouseup` events.
-		// TODO move to the engine?
-		editor.editing.view.addObserver( MouseEventsObserver );
-
 		this._defineSelectionConverter();
-		this._enableShiftClickSelection();
-		this._enableMouseDragSelection();
 		this._enablePluginDisabling(); // sic!
 	}
 
@@ -224,148 +217,6 @@ export default class TableSelection extends Plugin {
 	}
 
 	/**
-	 * Enables making cells selection by <kbd>Shift</kbd>+click. Creates a selection from the cell which previously held
-	 * the selection to the cell which was clicked. It can be the same cell, in which case it selects a single cell.
-	 *
-	 * @private
-	 */
-	_enableShiftClickSelection() {
-		const editor = this.editor;
-		let blockSelectionChange = false;
-
-		this.listenTo( editor.editing.view.document, 'mousedown', ( evt, domEventData ) => {
-			if ( !this.isEnabled ) {
-				return;
-			}
-
-			if ( !domEventData.domEvent.shiftKey ) {
-				return;
-			}
-
-			const anchorCell = this.getAnchorCell() || getTableCellsContainingSelection( editor.model.document.selection )[ 0 ];
-
-			if ( !anchorCell ) {
-				return;
-			}
-
-			const targetCell = this._getModelTableCellFromDomEvent( domEventData );
-
-			if ( targetCell && haveSameTableParent( anchorCell, targetCell ) ) {
-				blockSelectionChange = true;
-				this.setCellSelection( anchorCell, targetCell );
-
-				domEventData.preventDefault();
-			}
-		} );
-
-		this.listenTo( editor.editing.view.document, 'mouseup', () => {
-			blockSelectionChange = false;
-		} );
-
-		// We need to ignore a `selectionChange` event that is fired after we render our new table cells selection.
-		// When downcasting table cells selection to the view, we put the view selection in the last selected cell
-		// in a place that may not be natively a "correct" location. This is – we put it directly in the `<td>` element.
-		// All browsers fire the native `selectionchange` event.
-		// However, all browsers except Safari return the selection in the exact place where we put it
-		// (even though it's visually normalized). Safari returns `<td><p>^foo` that makes our selection observer
-		// fire our `selectionChange` event (because the view selection that we set in the first step differs from the DOM selection).
-		// Since `selectionChange` is fired, we automatically update the model selection that moves it that paragraph.
-		// This breaks our dear cells selection.
-		//
-		// Theoretically this issue concerns only Safari that is the only browser that do normalize the selection.
-		// However, to avoid code branching and to have a good coverage for this event blocker, I enabled it for all browsers.
-		//
-		// Note: I'm keeping the `blockSelectionChange` state separately for shift+click and mouse drag (exact same logic)
-		// so I don't have to try to analyze whether they don't overlap in some weird cases. Probably they don't.
-		// But I have other things to do, like writing this comment.
-		this.listenTo( editor.editing.view.document, 'selectionChange', evt => {
-			if ( blockSelectionChange ) {
-				// @if CK_DEBUG // console.log( 'Blocked selectionChange to avoid breaking table cells selection.' );
-
-				evt.stop();
-			}
-		}, { priority: 'highest' } );
-	}
-
-	/**
-	 * Enables making cells selection by dragging.
-	 *
-	 * The selection is made only on mousemove. Mouse tracking is started on mousedown.
-	 * However, the cells selection is enabled only after the mouse cursor left the anchor cell.
-	 * Thanks to that normal text selection within one cell works just fine. However, you can still select
-	 * just one cell by leaving the anchor cell and moving back to it.
-	 *
-	 * @private
-	 */
-	_enableMouseDragSelection() {
-		const editor = this.editor;
-		let anchorCell, targetCell;
-		let beganCellSelection = false;
-		let blockSelectionChange = false;
-
-		this.listenTo( editor.editing.view.document, 'mousedown', ( evt, domEventData ) => {
-			if ( !this.isEnabled ) {
-				return;
-			}
-
-			// Make sure to not conflict with the shift+click listener and any other possible handler.
-			if ( domEventData.domEvent.shiftKey || domEventData.domEvent.ctrlKey || domEventData.domEvent.altKey ) {
-				return;
-			}
-
-			anchorCell = this._getModelTableCellFromDomEvent( domEventData );
-		} );
-
-		this.listenTo( editor.editing.view.document, 'mousemove', ( evt, domEventData ) => {
-			if ( !domEventData.domEvent.buttons ) {
-				return;
-			}
-
-			if ( !anchorCell ) {
-				return;
-			}
-
-			const newTargetCell = this._getModelTableCellFromDomEvent( domEventData );
-
-			if ( newTargetCell && haveSameTableParent( anchorCell, newTargetCell ) ) {
-				targetCell = newTargetCell;
-
-				// Switch to the cell selection mode after the mouse cursor left the anchor cell.
-				// Switch off only on mouseup (makes selecting a single cell possible).
-				if ( !beganCellSelection && targetCell != anchorCell ) {
-					beganCellSelection = true;
-				}
-			}
-
-			// Yep, not making a cell selection yet. See method docs.
-			if ( !beganCellSelection ) {
-				return;
-			}
-
-			blockSelectionChange = true;
-			this.setCellSelection( anchorCell, targetCell );
-
-			domEventData.preventDefault();
-		} );
-
-		this.listenTo( editor.editing.view.document, 'mouseup', () => {
-			beganCellSelection = false;
-			blockSelectionChange = false;
-			anchorCell = null;
-			targetCell = null;
-		} );
-
-		// See the explanation in `_enableShiftClickSelection()`.
-		this.listenTo( editor.editing.view.document, 'selectionChange', evt => {
-			if ( blockSelectionChange ) {
-				// @if CK_DEBUG // console.log( 'Blocked selectionChange to avoid breaking table cells selection.' );
-
-				evt.stop();
-			}
-		}, { priority: 'highest' } );
-	}
-
-	/**
 	 * Creates a listener that reacts to changes in {@link #isEnabled} and, if the plugin was disabled,
 	 * it collapses the multi-cell selection to a regular selection placed inside a table cell.
 	 *
@@ -435,27 +286,6 @@ export default class TableSelection extends Plugin {
 	}
 
 	/**
-	 * Returns the model table cell element based on the target element of the passed DOM event.
-	 *
-	 * @private
-	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
-	 * @returns {module:engine/model/element~Element|undefined} Returns the table cell or `undefined`.
-	 */
-	_getModelTableCellFromDomEvent( domEventData ) {
-		// Note: Work with positions (not element mapping) because the target element can be an attribute or other non-mapped element.
-		const viewTargetElement = domEventData.target;
-		const viewPosition = this.editor.editing.view.createPositionAt( viewTargetElement, 0 );
-		const modelPosition = this.editor.editing.mapper.toModelPosition( viewPosition );
-		const modelElement = modelPosition.parent;
-
-		if ( modelElement.is( 'tableCell' ) ) {
-			return modelElement;
-		}
-
-		return findAncestor( 'tableCell', modelElement );
-	}
-
-	/**
 	 * Returns an array of table cells that should be selected based on the
 	 * given anchor cell and target (focus) cell.
 	 *
@@ -508,7 +338,3 @@ export default class TableSelection extends Plugin {
 		};
 	}
 }
-
-function haveSameTableParent( cellA, cellB ) {
-	return cellA.parent.parent == cellB.parent.parent;
-}

+ 5 - 2
packages/ckeditor5-table/tests/table.js

@@ -10,10 +10,13 @@ import TableSelection from '../src/tableselection';
 import TableClipboard from '../src/tableclipboard';
 import TableKeyboard from '../src/tablekeyboard';
 import Widget from '@ckeditor/ckeditor5-widget/src/widget';
+import TableMouse from '../src/tablemouse';
 
 describe( 'Table', () => {
-	it( 'requires TableEditing, TableUI, TableSelection, TableClipboard, TableKeyboard and Widget', () => {
-		expect( Table.requires ).to.deep.equal( [ TableEditing, TableUI, TableSelection, TableClipboard, TableKeyboard, Widget ] );
+	it( 'requires TableEditing, TableUI, TableSelection, TableMouse, TableKeyboard, TableClipboard and Widget', () => {
+		expect( Table.requires ).to.deep.equal( [
+			TableEditing, TableUI, TableSelection, TableMouse, TableKeyboard, TableClipboard, Widget
+		] );
 	} );
 
 	it( 'has proper name', () => {

packages/ckeditor5-table/tests/tablenavigation.js → packages/ckeditor5-table/tests/tablekeyboard.js


+ 569 - 0
packages/ckeditor5-table/tests/tablemouse.js

@@ -0,0 +1,569 @@
+/**
+ * @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 document, console */
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+import TableEditing from '../src/tableediting';
+import TableSelection from '../src/tableselection';
+import TableMouse from '../src/tablemouse';
+import { assertSelectedCells, modelTable } from './_utils/utils';
+import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+
+describe( 'TableMouse', () => {
+	let editorElement, editor, model, tableMouse, modelRoot, view, viewDocument;
+
+	beforeEach( () => {
+		editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+	} );
+
+	afterEach( async () => {
+		editorElement.remove();
+		await editor.destroy();
+	} );
+
+	describe( 'plugin', () => {
+		beforeEach( async () => {
+			editor = await createEditor();
+		} );
+
+		it( 'should have pluginName', () => {
+			expect( TableMouse.pluginName ).to.equal( 'TableMouse' );
+		} );
+	} );
+
+	describe( 'selection by Shift+click', () => {
+		beforeEach( async () => {
+			editor = await createEditor();
+			model = editor.model;
+			modelRoot = model.document.getRoot();
+			view = editor.editing.view;
+			viewDocument = view.document;
+			tableMouse = editor.plugins.get( TableMouse );
+
+			setModelData( model, modelTable( [
+				[ '11[]', '12', '13' ],
+				[ '21', '22', '23' ],
+				[ '31', '32', '33' ]
+			] ) );
+		} );
+
+		it( 'should do nothing if the plugin is disabled', () => {
+			tableMouse.isEnabled = false;
+
+			viewDocument.fire( 'mousedown', new DomEventData( view, {} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if the TableSelection plugin is disabled', () => {
+			editor.plugins.get( 'TableSelection' ).isEnabled = false;
+
+			viewDocument.fire( 'mousedown', new DomEventData( view, {} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should abort if Shift key was not pressed', () => {
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				shiftKey: false,
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
+				)
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should abort if Shift+clicked an element outside a table', () => {
+			const preventDefault = sinon.spy();
+
+			model.change( writer => {
+				const paragraph = writer.createElement( 'paragraph' );
+				const text = writer.createText( 'foo' );
+
+				writer.insert( text, paragraph );
+				writer.insert( paragraph, model.document.getRoot(), 'end' );
+				writer.setSelection( paragraph, 'end' );
+			} );
+
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				shiftKey: true,
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 1 )
+				),
+				preventDefault
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+
+			expect( preventDefault.called ).to.equal( false );
+		} );
+
+		it( 'should abort if clicked a cell that belongs to another table', () => {
+			const preventDefault = sinon.spy();
+
+			setModelData( model, [
+				modelTable( [
+					[ '1.11[]', '1.12' ],
+					[ '1.21', '1.22' ]
+				] ),
+				modelTable( [
+					[ '2.11', '2.12' ],
+					[ '2.21', '2.22' ]
+				] )
+			].join( '' ) );
+
+			const domEventDataMock = new DomEventData( view, {
+				shiftKey: true,
+				target: view.domConverter.mapViewToDom(
+					// The second table: figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 1 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 )
+				),
+				preventDefault
+			} );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 0, 0 ],
+				[ 0, 0 ]
+			] );
+
+			expect( preventDefault.called ).to.equal( false );
+		} );
+
+		it( 'should select all cells in first row', () => {
+			const preventDefault = sinon.spy();
+
+			const domEventDataMock = new DomEventData( view, {
+				shiftKey: true,
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
+				),
+				preventDefault
+			} );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 1, 1, 1 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+
+			expect( preventDefault.called ).to.equal( true );
+		} );
+
+		it( 'should use the anchor cell from the selection if possible', () => {
+			const preventDefault = sinon.spy();
+
+			const domEventDataMock = new DomEventData( view, {
+				shiftKey: true,
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
+				),
+				preventDefault
+			} );
+
+			editor.plugins.get( 'TableSelection' ).setCellSelection(
+				modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
+				modelRoot.getNodeByPath( [ 0, 2, 1 ] )
+			);
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 1, 1, 0 ],
+				[ 1, 1, 0 ]
+			] );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 1, 1, 1 ],
+				[ 1, 1, 1 ],
+				[ 0, 0, 0 ]
+			] );
+
+			expect( preventDefault.called ).to.equal( true );
+		} );
+
+		it( 'should ignore `selectionChange` event when selecting cells', () => {
+			const consoleLog = sinon.stub( console, 'log' );
+			const preventDefault = sinon.spy();
+			const selectionChangeCallback = sinon.spy();
+
+			// Adding a new callback to check whether it will be executed (whether `evt.stop()` is being called).
+			viewDocument.on( 'selectionChange', selectionChangeCallback );
+
+			// Shift+click a cell to create a selection. Should disable listening to `selectionChange`.
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				shiftKey: true,
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
+				),
+				preventDefault
+			} ) );
+
+			// Due to browsers "fixing" the selection (e.g. moving it to text nodes), after we set a selection
+			// the browser fill fire native selectionchange, which triggers our selectionChange. We need to ignore it.
+			// See a broader explanation in tablemouse.js.
+			viewDocument.fire( 'selectionChange' );
+
+			// The callback shouldn't be executed because
+			// `selectionChange` event should be canceled.
+			expect( selectionChangeCallback.called ).to.equal( false );
+			expect( consoleLog.called ).to.equal( true );
+			expect( consoleLog.firstCall.args[ 0 ] ).to.equal( 'Blocked selectionChange to avoid breaking table cells selection.' );
+
+			// Enables listening to `selectionChange` event.
+			viewDocument.fire( 'mouseup' );
+
+			viewDocument.fire( 'selectionChange', {
+				newSelection: view.document.selection
+			} );
+
+			expect( selectionChangeCallback.called ).to.equal( true );
+
+			consoleLog.restore();
+		} );
+	} );
+
+	describe( 'selection by mouse drag', () => {
+		let preventDefault;
+
+		beforeEach( async () => {
+			editor = await createEditor();
+			model = editor.model;
+			modelRoot = model.document.getRoot();
+			view = editor.editing.view;
+			viewDocument = view.document;
+			tableMouse = editor.plugins.get( TableMouse );
+
+			setModelData( model, modelTable( [
+				[ '11[]', '12', '13' ],
+				[ '21', '22', '23' ],
+				[ '31', '32', '33' ]
+			] ) );
+
+			preventDefault = sinon.spy();
+		} );
+
+		it( 'should do nothing if the plugin is disabled', () => {
+			tableMouse.isEnabled = false;
+
+			const domEventDataMock = new DomEventData( view, {} );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if the TableSelection plugin is disabled', () => {
+			editor.plugins.get( 'TableSelection' ).isEnabled = false;
+
+			const domEventDataMock = new DomEventData( view, {} );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should abort if Ctrl is pressed', () => {
+			const domEventDataMock = new DomEventData( view, {
+				ctrlKey: true
+			} );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should abort if Alt is pressed', () => {
+			const domEventDataMock = new DomEventData( view, {
+				altKey: true
+			} );
+
+			viewDocument.fire( 'mousedown', domEventDataMock );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if any of mouse buttons was not clicked', () => {
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				buttons: 0
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if started dragging outside of table', () => {
+			model.change( writer => {
+				const paragraph = writer.createElement( 'paragraph' );
+				const text = writer.createText( 'foo' );
+
+				writer.insert( text, paragraph );
+				writer.insert( paragraph, model.document.getRoot(), 'end' );
+				writer.setSelection( paragraph, 'end' );
+			} );
+
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 1 )
+				)
+			} ) );
+
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				buttons: 1
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if ended dragging outside of table', () => {
+			model.change( writer => {
+				const paragraph = writer.createElement( 'paragraph' );
+				const text = writer.createText( 'foo' );
+
+				writer.insert( text, paragraph );
+				writer.insert( paragraph, model.document.getRoot(), 'end' );
+				writer.setSelection( paragraph, 'end' );
+			} );
+
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				)
+			} ) );
+
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 1 )
+				),
+				buttons: 1
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if ended dragging inside another table', () => {
+			setModelData( model, [
+				modelTable( [
+					[ '1.11[]', '1.12' ],
+					[ '1.21', '1.22' ]
+				] ),
+				modelTable( [
+					[ '2.11', '2.12' ],
+					[ '2.21', '2.22' ]
+				] )
+			].join( '' ) );
+
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				)
+			} ) );
+
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 1 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 )
+				),
+				buttons: 1
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0 ],
+				[ 0, 0 ]
+			] );
+		} );
+
+		it( 'should do nothing if ended in the same cell', () => {
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				)
+			} ) );
+
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				),
+				buttons: 1
+			} ) );
+
+			assertSelectedCells( model, [
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should select started and ended dragging in the same cell but went over its border', () => {
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				)
+			} ) );
+
+			// Select the next one.
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 1 )
+				),
+				buttons: 1,
+				preventDefault: sinon.spy()
+			} ) );
+
+			// And back to the "started" cell.
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				),
+				buttons: 1,
+				preventDefault: sinon.spy()
+			} ) );
+
+			viewDocument.fire( 'mouseup' );
+
+			assertSelectedCells( model, [
+				[ 1, 0, 0 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+		} );
+
+		it( 'should select all cells in first row', () => {
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
+				)
+			} ) );
+
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
+				),
+				buttons: 1,
+				preventDefault
+			} ) );
+
+			viewDocument.fire( 'mouseup' );
+
+			assertSelectedCells( model, [
+				[ 1, 1, 1 ],
+				[ 0, 0, 0 ],
+				[ 0, 0, 0 ]
+			] );
+
+			expect( preventDefault.called ).to.equal( true );
+		} );
+
+		it( 'should ignore `selectionChange` event when selecting cells ', () => {
+			const consoleLog = sinon.stub( console, 'log' );
+			const preventDefault = sinon.spy();
+			const selectionChangeCallback = sinon.spy();
+
+			// Adding a new callback to check whether it will be executed (whether `evt.stop()` is being called).
+			viewDocument.on( 'selectionChange', selectionChangeCallback );
+
+			// Click on a cell.
+			viewDocument.fire( 'mousedown', new DomEventData( view, {
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 1 )
+				)
+			} ) );
+
+			// Then move the mouse to another cell. Disables listening to `selectionChange`.
+			viewDocument.fire( 'mousemove', new DomEventData( view, {
+				buttons: 1,
+				target: view.domConverter.mapViewToDom(
+					// figure > table > tbody > tr > td
+					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
+				),
+				preventDefault
+			} ) );
+
+			// See explanation why do we fire it in the similar test for Shift+click.
+			viewDocument.fire( 'selectionChange' );
+
+			// `selectionChange` event should be canceled.
+			expect( selectionChangeCallback.called ).to.equal( false );
+			expect( consoleLog.called ).to.equal( true );
+			expect( consoleLog.firstCall.args[ 0 ] ).to.equal( 'Blocked selectionChange to avoid breaking table cells selection.' );
+
+			// Enables listening to `selectionChange` event.
+			viewDocument.fire( 'mouseup' );
+
+			viewDocument.fire( 'selectionChange', {
+				newSelection: view.document.selection
+			} );
+
+			expect( selectionChangeCallback.called ).to.equal( true );
+
+			consoleLog.restore();
+		} );
+	} );
+
+	function createEditor() {
+		return ClassicTestEditor.create( editorElement, {
+			plugins: [ TableEditing, TableSelection, TableMouse, Paragraph, Typing ]
+		} );
+	}
+} );

+ 7 - 506
packages/ckeditor5-table/tests/tableselection.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
-/* globals document, console */
+/* globals document */
 
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
@@ -17,12 +17,11 @@ import TableEditing from '../src/tableediting';
 import TableSelection from '../src/tableselection';
 import { assertSelectedCells, modelTable } from './_utils/utils';
 import DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment';
-import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
 import Typing from '@ckeditor/ckeditor5-typing/src/typing';
 import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
 
-describe( 'table selection', () => {
-	let editorElement, editor, model, tableSelection, modelRoot, view, viewDocument;
+describe( 'TableSelection', () => {
+	let editorElement, editor, model, tableSelection, modelRoot;
 
 	beforeEach( () => {
 		editorElement = document.createElement( 'div' );
@@ -48,6 +47,10 @@ describe( 'table selection', () => {
 			] ) );
 		} );
 
+		it( 'should have pluginName', () => {
+			expect( TableSelection.pluginName ).to.equal( 'TableSelection' );
+		} );
+
 		describe( 'plugin disabling support', () => {
 			it( 'should collapse multi-cell selection when the plugin gets disabled', () => {
 				const firstCell = modelRoot.getNodeByPath( [ 0, 0, 0 ] );
@@ -98,509 +101,11 @@ describe( 'table selection', () => {
 		} );
 	} );
 
-	describe( 'selection by Shift+click', () => {
-		beforeEach( async () => {
-			editor = await createEditor();
-			model = editor.model;
-			modelRoot = model.document.getRoot();
-			view = editor.editing.view;
-			viewDocument = view.document;
-			tableSelection = editor.plugins.get( TableSelection );
-
-			setModelData( model, modelTable( [
-				[ '11[]', '12', '13' ],
-				[ '21', '22', '23' ],
-				[ '31', '32', '33' ]
-			] ) );
-		} );
-
-		it( 'should do nothing if the plugin is disabled', () => {
-			tableSelection.isEnabled = false;
-
-			viewDocument.fire( 'mousedown', new DomEventData( view, {} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should abort if Shift key was not pressed', () => {
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				shiftKey: false,
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
-				)
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should abort if Shift+clicked an element outside a table', () => {
-			const preventDefault = sinon.spy();
-
-			model.change( writer => {
-				const paragraph = writer.createElement( 'paragraph' );
-				const text = writer.createText( 'foo' );
-
-				writer.insert( text, paragraph );
-				writer.insert( paragraph, model.document.getRoot(), 'end' );
-				writer.setSelection( paragraph, 'end' );
-			} );
-
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				shiftKey: true,
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 1 )
-				),
-				preventDefault
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-
-			expect( preventDefault.called ).to.equal( false );
-		} );
-
-		it( 'should abort if clicked a cell that belongs to another table', () => {
-			const preventDefault = sinon.spy();
-
-			setModelData( model, [
-				modelTable( [
-					[ '1.11[]', '1.12' ],
-					[ '1.21', '1.22' ]
-				] ),
-				modelTable( [
-					[ '2.11', '2.12' ],
-					[ '2.21', '2.22' ]
-				] )
-			].join( '' ) );
-
-			const domEventDataMock = new DomEventData( view, {
-				shiftKey: true,
-				target: view.domConverter.mapViewToDom(
-					// The second table: figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 1 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 )
-				),
-				preventDefault
-			} );
-
-			viewDocument.fire( 'mousedown', domEventDataMock );
-
-			assertSelectedCells( model, [
-				[ 0, 0 ],
-				[ 0, 0 ]
-			] );
-
-			expect( preventDefault.called ).to.equal( false );
-		} );
-
-		it( 'should select all cells in first row', () => {
-			const preventDefault = sinon.spy();
-
-			const domEventDataMock = new DomEventData( view, {
-				shiftKey: true,
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
-				),
-				preventDefault
-			} );
-
-			viewDocument.fire( 'mousedown', domEventDataMock );
-
-			assertSelectedCells( model, [
-				[ 1, 1, 1 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-
-			expect( preventDefault.called ).to.equal( true );
-		} );
-
-		it( 'should use the anchor cell from the selection if possible', () => {
-			const preventDefault = sinon.spy();
-
-			const domEventDataMock = new DomEventData( view, {
-				shiftKey: true,
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
-				),
-				preventDefault
-			} );
-
-			tableSelection.setCellSelection(
-				modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
-				modelRoot.getNodeByPath( [ 0, 2, 1 ] )
-			);
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 1, 1, 0 ],
-				[ 1, 1, 0 ]
-			] );
-
-			viewDocument.fire( 'mousedown', domEventDataMock );
-
-			assertSelectedCells( model, [
-				[ 1, 1, 1 ],
-				[ 1, 1, 1 ],
-				[ 0, 0, 0 ]
-			] );
-
-			expect( preventDefault.called ).to.equal( true );
-		} );
-
-		it( 'should ignore `selectionChange` event when selecting cells', () => {
-			const consoleLog = sinon.stub( console, 'log' );
-			const preventDefault = sinon.spy();
-			const selectionChangeCallback = sinon.spy();
-
-			// Adding a new callback to check whether it will be executed (whether `evt.stop()` is being called).
-			viewDocument.on( 'selectionChange', selectionChangeCallback );
-
-			// Shift+click a cell to create a selection. Should disable listening to `selectionChange`.
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				shiftKey: true,
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
-				),
-				preventDefault
-			} ) );
-
-			// Due to browsers "fixing" the selection (e.g. moving it to text nodes), after we set a selection
-			// the browser fill fire native selectionchange, which triggers our selectionChange. We need to ignore it.
-			// See a broader explanation in tableselection.js.
-			viewDocument.fire( 'selectionChange' );
-
-			// The callback shouldn't be executed because
-			// `selectionChange` event should be canceled.
-			expect( selectionChangeCallback.called ).to.equal( false );
-			expect( consoleLog.called ).to.equal( true );
-			expect( consoleLog.firstCall.args[ 0 ] ).to.equal( 'Blocked selectionChange to avoid breaking table cells selection.' );
-
-			// Enables listening to `selectionChange` event.
-			viewDocument.fire( 'mouseup' );
-
-			viewDocument.fire( 'selectionChange', {
-				newSelection: view.document.selection
-			} );
-
-			expect( selectionChangeCallback.called ).to.equal( true );
-
-			consoleLog.restore();
-		} );
-	} );
-
-	describe( 'selection by mouse drag', () => {
-		let preventDefault;
-
-		beforeEach( async () => {
-			editor = await createEditor();
-			model = editor.model;
-			modelRoot = model.document.getRoot();
-			view = editor.editing.view;
-			viewDocument = view.document;
-			tableSelection = editor.plugins.get( TableSelection );
-
-			setModelData( model, modelTable( [
-				[ '11[]', '12', '13' ],
-				[ '21', '22', '23' ],
-				[ '31', '32', '33' ]
-			] ) );
-
-			preventDefault = sinon.spy();
-		} );
-
-		it( 'should do nothing if the plugin is disabled', () => {
-			tableSelection.isEnabled = false;
-
-			const domEventDataMock = new DomEventData( view, {} );
-
-			viewDocument.fire( 'mousedown', domEventDataMock );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should abort if Ctrl is pressed', () => {
-			const domEventDataMock = new DomEventData( view, {
-				ctrlKey: true
-			} );
-
-			viewDocument.fire( 'mousedown', domEventDataMock );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should abort if Alt is pressed', () => {
-			const domEventDataMock = new DomEventData( view, {
-				altKey: true
-			} );
-
-			viewDocument.fire( 'mousedown', domEventDataMock );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should do nothing if any of mouse buttons was not clicked', () => {
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				buttons: 0
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should do nothing if started dragging outside of table', () => {
-			model.change( writer => {
-				const paragraph = writer.createElement( 'paragraph' );
-				const text = writer.createText( 'foo' );
-
-				writer.insert( text, paragraph );
-				writer.insert( paragraph, model.document.getRoot(), 'end' );
-				writer.setSelection( paragraph, 'end' );
-			} );
-
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 1 )
-				)
-			} ) );
-
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				buttons: 1
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should do nothing if ended dragging outside of table', () => {
-			model.change( writer => {
-				const paragraph = writer.createElement( 'paragraph' );
-				const text = writer.createText( 'foo' );
-
-				writer.insert( text, paragraph );
-				writer.insert( paragraph, model.document.getRoot(), 'end' );
-				writer.setSelection( paragraph, 'end' );
-			} );
-
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				)
-			} ) );
-
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 1 )
-				),
-				buttons: 1
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should do nothing if ended dragging inside another table', () => {
-			setModelData( model, [
-				modelTable( [
-					[ '1.11[]', '1.12' ],
-					[ '1.21', '1.22' ]
-				] ),
-				modelTable( [
-					[ '2.11', '2.12' ],
-					[ '2.21', '2.22' ]
-				] )
-			].join( '' ) );
-
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				)
-			} ) );
-
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 1 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 )
-				),
-				buttons: 1
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0 ],
-				[ 0, 0 ]
-			] );
-		} );
-
-		it( 'should do nothing if ended in the same cell', () => {
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				)
-			} ) );
-
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				),
-				buttons: 1
-			} ) );
-
-			assertSelectedCells( model, [
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should select started and ended dragging in the same cell but went over its border', () => {
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				)
-			} ) );
-
-			// Select the next one.
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 1 )
-				),
-				buttons: 1,
-				preventDefault: sinon.spy()
-			} ) );
-
-			// And back to the "started" cell.
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				),
-				buttons: 1,
-				preventDefault: sinon.spy()
-			} ) );
-
-			viewDocument.fire( 'mouseup' );
-
-			assertSelectedCells( model, [
-				[ 1, 0, 0 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-		} );
-
-		it( 'should select all cells in first row', () => {
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
-				)
-			} ) );
-
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
-				),
-				buttons: 1,
-				preventDefault
-			} ) );
-
-			viewDocument.fire( 'mouseup' );
-
-			assertSelectedCells( model, [
-				[ 1, 1, 1 ],
-				[ 0, 0, 0 ],
-				[ 0, 0, 0 ]
-			] );
-
-			expect( preventDefault.called ).to.equal( true );
-		} );
-
-		it( 'should ignore `selectionChange` event when selecting cells ', () => {
-			const consoleLog = sinon.stub( console, 'log' );
-			const preventDefault = sinon.spy();
-			const selectionChangeCallback = sinon.spy();
-
-			// Adding a new callback to check whether it will be executed (whether `evt.stop()` is being called).
-			viewDocument.on( 'selectionChange', selectionChangeCallback );
-
-			// Click on a cell.
-			viewDocument.fire( 'mousedown', new DomEventData( view, {
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 1 )
-				)
-			} ) );
-
-			// Then move the mouse to another cell. Disables listening to `selectionChange`.
-			viewDocument.fire( 'mousemove', new DomEventData( view, {
-				buttons: 1,
-				target: view.domConverter.mapViewToDom(
-					// figure > table > tbody > tr > td
-					viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
-				),
-				preventDefault
-			} ) );
-
-			// See explanation why do we fire it in the similar test for Shift+click.
-			viewDocument.fire( 'selectionChange' );
-
-			// `selectionChange` event should be canceled.
-			expect( selectionChangeCallback.called ).to.equal( false );
-			expect( consoleLog.called ).to.equal( true );
-			expect( consoleLog.firstCall.args[ 0 ] ).to.equal( 'Blocked selectionChange to avoid breaking table cells selection.' );
-
-			// Enables listening to `selectionChange` event.
-			viewDocument.fire( 'mouseup' );
-
-			viewDocument.fire( 'selectionChange', {
-				newSelection: view.document.selection
-			} );
-
-			expect( selectionChangeCallback.called ).to.equal( true );
-
-			consoleLog.restore();
-		} );
-	} );
-
 	describe( 'getSelectedTableCells()', () => {
 		beforeEach( async () => {
 			editor = await createEditor();
 			model = editor.model;
 			modelRoot = model.document.getRoot();
-			view = editor.editing.view;
-			viewDocument = view.document;
 			tableSelection = editor.plugins.get( TableSelection );
 
 			setModelData( model, modelTable( [
@@ -684,8 +189,6 @@ describe( 'table selection', () => {
 			editor = await createEditor();
 			model = editor.model;
 			modelRoot = model.document.getRoot();
-			view = editor.editing.view;
-			viewDocument = view.document;
 			tableSelection = editor.plugins.get( TableSelection );
 
 			setModelData( model, modelTable( [
@@ -740,8 +243,6 @@ describe( 'table selection', () => {
 			editor = await createEditor();
 			model = editor.model;
 			modelRoot = model.document.getRoot();
-			view = editor.editing.view;
-			viewDocument = view.document;
 			tableSelection = editor.plugins.get( TableSelection );
 
 			setModelData( model, modelTable( [

+ 1 - 1
packages/ckeditor5-table/tests/tableselection/mouseeventsobserver.js

@@ -6,7 +6,7 @@
 /* globals document */
 
 import View from '@ckeditor/ckeditor5-engine/src/view/view';
-import MouseEventsObserver from '../../src/tableselection/mouseeventsobserver';
+import MouseEventsObserver from '../../src/tablemouse/mouseeventsobserver';
 
 describe( 'table selection', () => {
 	describe( 'MouseEventsObserver', () => {