tableselection-integration.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /* globals document */
  6. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  7. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  8. import Delete from '@ckeditor/ckeditor5-typing/src/delete';
  9. import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
  10. import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline';
  11. import TableEditing from '../src/tableediting';
  12. import TableSelection from '../src/tableselection';
  13. import TableClipboard from '../src/tableclipboard';
  14. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  15. import { modelTable } from './_utils/utils';
  16. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  17. import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
  18. import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
  19. import Input from '@ckeditor/ckeditor5-typing/src/input';
  20. import ViewText from '@ckeditor/ckeditor5-engine/src/view/text';
  21. import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
  22. describe( 'TableSelection - integration', () => {
  23. let editor, model, tableSelection, modelRoot, element, viewDocument;
  24. afterEach( async () => {
  25. element.remove();
  26. await editor.destroy();
  27. } );
  28. describe( 'on delete', () => {
  29. beforeEach( async () => {
  30. await setupEditor( [ Delete ] );
  31. } );
  32. it( 'should clear contents of the selected table cells and put selection in last cell on backward delete', () => {
  33. tableSelection.setCellSelection(
  34. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  35. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  36. );
  37. const domEventData = new DomEventData( viewDocument, {
  38. preventDefault: sinon.spy()
  39. }, {
  40. direction: 'backward',
  41. unit: 'character',
  42. sequence: 1
  43. } );
  44. viewDocument.fire( 'delete', domEventData );
  45. assertEqualMarkup( getModelData( model ), modelTable( [
  46. [ '', '', '13' ],
  47. [ '', '[]', '23' ],
  48. [ '31', '32', '33' ]
  49. ] ) );
  50. } );
  51. it( 'should clear contents of the selected table cells and put selection in last cell on forward delete', () => {
  52. tableSelection.setCellSelection(
  53. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  54. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  55. );
  56. const domEventData = new DomEventData( viewDocument, {
  57. preventDefault: sinon.spy()
  58. }, {
  59. direction: 'forward',
  60. unit: 'character',
  61. sequence: 1
  62. } );
  63. viewDocument.fire( 'delete', domEventData );
  64. assertEqualMarkup( getModelData( model ), modelTable( [
  65. [ '[]', '', '13' ],
  66. [ '', '', '23' ],
  67. [ '31', '32', '33' ]
  68. ] ) );
  69. } );
  70. it( 'should not interfere with default key handler if no table selection', () => {
  71. setModelData( model, modelTable( [
  72. [ '11[]', '12', '13' ],
  73. [ '21', '22', '23' ],
  74. [ '31', '32', '33' ]
  75. ] ) );
  76. const domEventData = new DomEventData( viewDocument, {
  77. preventDefault: sinon.spy()
  78. }, {
  79. direction: 'backward',
  80. unit: 'character',
  81. sequence: 1
  82. } );
  83. viewDocument.fire( 'delete', domEventData );
  84. assertEqualMarkup( getModelData( model ), modelTable( [
  85. [ '1[]', '12', '13' ],
  86. [ '21', '22', '23' ],
  87. [ '31', '32', '33' ]
  88. ] ) );
  89. } );
  90. } );
  91. describe( 'on user input', () => {
  92. beforeEach( async () => {
  93. await setupEditor( [ Input ] );
  94. } );
  95. it( 'should clear contents of the selected table cells and put selection in last cell on user input', () => {
  96. tableSelection.setCellSelection(
  97. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  98. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  99. );
  100. viewDocument.fire( 'keydown', { keyCode: getCode( 'x' ) } );
  101. // Mutate at the place where the document selection was put; it's more realistic
  102. // than mutating at some arbitrary position.
  103. const placeOfMutation = viewDocument.selection.getFirstRange().start.parent;
  104. viewDocument.fire( 'mutations', [
  105. {
  106. type: 'children',
  107. oldChildren: [],
  108. newChildren: [ new ViewText( viewDocument, 'x' ) ],
  109. node: placeOfMutation
  110. }
  111. ] );
  112. assertEqualMarkup( getModelData( model ), modelTable( [
  113. [ '', '', '13' ],
  114. [ '', 'x[]', '23' ],
  115. [ '31', '32', '33' ]
  116. ] ) );
  117. } );
  118. it( 'should not interfere with default key handler if no table selection', () => {
  119. viewDocument.fire( 'keydown', { keyCode: getCode( 'x' ) } );
  120. // Mutate at the place where the document selection was put; it's more realistic
  121. // than mutating at some arbitrary position.
  122. const placeOfMutation = viewDocument.selection.getFirstRange().start.parent;
  123. viewDocument.fire( 'mutations', [
  124. {
  125. type: 'children',
  126. oldChildren: [],
  127. newChildren: [ new ViewText( viewDocument, 'x' ) ],
  128. node: placeOfMutation
  129. }
  130. ] );
  131. assertEqualMarkup( getModelData( model ), modelTable( [
  132. [ 'x[]11', '12', '13' ],
  133. [ '21', '22', '23' ],
  134. [ '31', '32', '33' ]
  135. ] ) );
  136. } );
  137. } );
  138. describe( 'with other features', () => {
  139. beforeEach( async () => {
  140. await setupEditor( [ Clipboard, HorizontalLine ] );
  141. } );
  142. it( 'allows pasting over multi-cell selection', () => {
  143. tableSelection.setCellSelection(
  144. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  145. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  146. );
  147. const dataTransferMock = {
  148. getData: sinon.stub().withArgs( 'text/plain' ).returns( 'foo' )
  149. };
  150. editor.editing.view.document.fire( 'clipboardInput', {
  151. dataTransfer: dataTransferMock,
  152. stop: sinon.spy()
  153. } );
  154. assertEqualMarkup( getModelData( model ), modelTable( [
  155. [ 'foo[]', '', '13' ],
  156. [ '', '', '23' ],
  157. [ '31', '32', '33' ]
  158. ] ) );
  159. } );
  160. it( 'allows inserting a horizontal line over a multi-range selection', () => {
  161. tableSelection.setCellSelection(
  162. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  163. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  164. );
  165. editor.execute( 'horizontalLine' );
  166. assertEqualMarkup(
  167. getModelData( model ),
  168. '<table>' +
  169. '<tableRow>' +
  170. '<tableCell><horizontalLine></horizontalLine><paragraph>[]</paragraph></tableCell>' +
  171. '<tableCell><paragraph></paragraph></tableCell>' +
  172. '<tableCell><paragraph>13</paragraph></tableCell>' +
  173. '</tableRow>' +
  174. '<tableRow>' +
  175. '<tableCell><paragraph></paragraph></tableCell>' +
  176. '<tableCell><paragraph></paragraph></tableCell>' +
  177. '<tableCell><paragraph>23</paragraph></tableCell>' +
  178. '</tableRow>' +
  179. '<tableRow>' +
  180. '<tableCell><paragraph>31</paragraph></tableCell>' +
  181. '<tableCell><paragraph>32</paragraph></tableCell>' +
  182. '<tableCell><paragraph>33</paragraph></tableCell>' +
  183. '</tableRow>' +
  184. '</table>'
  185. );
  186. } );
  187. } );
  188. describe( 'with undo', () => {
  189. beforeEach( async () => {
  190. await setupEditor( [ UndoEditing ] );
  191. } );
  192. // See https://github.com/ckeditor/ckeditor5/issues/6634.
  193. it( 'works with merge cells command', () => {
  194. setModelData( editor.model, modelTable( [ [ '00', '01' ] ] ) );
  195. tableSelection.setCellSelection(
  196. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  197. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  198. );
  199. editor.execute( 'mergeTableCells' );
  200. assertEqualMarkup( getModelData( model ), modelTable( [
  201. [ { colspan: 2, contents: '<paragraph>[00</paragraph><paragraph>01]</paragraph>' } ]
  202. ] ) );
  203. editor.execute( 'undo' );
  204. assertEqualMarkup( getModelData( model ), modelTable( [
  205. [ '[]00', '01' ]
  206. ] ) );
  207. } );
  208. } );
  209. async function setupEditor( plugins ) {
  210. element = document.createElement( 'div' );
  211. document.body.appendChild( element );
  212. editor = await ClassicTestEditor.create( element, {
  213. plugins: [ TableEditing, TableSelection, TableClipboard, Paragraph, ...plugins ]
  214. } );
  215. model = editor.model;
  216. modelRoot = model.document.getRoot();
  217. viewDocument = editor.editing.view.document;
  218. tableSelection = editor.plugins.get( TableSelection );
  219. setModelData( model, modelTable( [
  220. [ '[]11', '12', '13' ],
  221. [ '21', '22', '23' ],
  222. [ '31', '32', '33' ]
  223. ] ) );
  224. }
  225. } );