tableselection-integration.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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. describe( 'TableSelection - integration', () => {
  22. let editor, model, tableSelection, modelRoot, element, viewDocument;
  23. afterEach( async () => {
  24. element.remove();
  25. await editor.destroy();
  26. } );
  27. describe( 'on delete', () => {
  28. beforeEach( async () => {
  29. await setupEditor( [ Delete ] );
  30. } );
  31. it( 'should clear contents of the selected table cells and put selection in last cell on backward delete', () => {
  32. tableSelection._setCellSelection(
  33. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  34. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  35. );
  36. const domEventData = new DomEventData( viewDocument, {
  37. preventDefault: sinon.spy()
  38. }, {
  39. direction: 'backward',
  40. unit: 'character',
  41. sequence: 1
  42. } );
  43. viewDocument.fire( 'delete', domEventData );
  44. assertEqualMarkup( getModelData( model ), modelTable( [
  45. [ '', '', '13' ],
  46. [ '', '[]', '23' ],
  47. [ '31', '32', '33' ]
  48. ] ) );
  49. } );
  50. it( 'should clear contents of the selected table cells and put selection in last cell on forward delete', () => {
  51. tableSelection._setCellSelection(
  52. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  53. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  54. );
  55. const domEventData = new DomEventData( viewDocument, {
  56. preventDefault: sinon.spy()
  57. }, {
  58. direction: 'forward',
  59. unit: 'character',
  60. sequence: 1
  61. } );
  62. viewDocument.fire( 'delete', domEventData );
  63. assertEqualMarkup( getModelData( model ), modelTable( [
  64. [ '[]', '', '13' ],
  65. [ '', '', '23' ],
  66. [ '31', '32', '33' ]
  67. ] ) );
  68. } );
  69. it( 'should not interfere with default key handler if no table selection', () => {
  70. setModelData( model, modelTable( [
  71. [ '11[]', '12', '13' ],
  72. [ '21', '22', '23' ],
  73. [ '31', '32', '33' ]
  74. ] ) );
  75. const domEventData = new DomEventData( viewDocument, {
  76. preventDefault: sinon.spy()
  77. }, {
  78. direction: 'backward',
  79. unit: 'character',
  80. sequence: 1
  81. } );
  82. viewDocument.fire( 'delete', domEventData );
  83. assertEqualMarkup( getModelData( model ), modelTable( [
  84. [ '1[]', '12', '13' ],
  85. [ '21', '22', '23' ],
  86. [ '31', '32', '33' ]
  87. ] ) );
  88. } );
  89. it( 'should work with any arbitrary selection passed to Model#deleteContent() (delete backwards)', () => {
  90. const selection = model.createSelection( [
  91. model.createRange(
  92. model.createPositionFromPath( modelRoot, [ 0, 0, 0 ] ),
  93. model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] )
  94. ),
  95. model.createRange(
  96. model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] ),
  97. model.createPositionFromPath( modelRoot, [ 0, 0, 2 ] )
  98. )
  99. ] );
  100. model.change( writer => {
  101. model.deleteContent( selection );
  102. writer.setSelection( selection );
  103. } );
  104. assertEqualMarkup( getModelData( model ), modelTable( [
  105. [ '', '[]', '13' ],
  106. [ '21', '22', '23' ],
  107. [ '31', '32', '33' ]
  108. ] ) );
  109. } );
  110. it( 'should work with any arbitrary selection passed to Model#deleteContent() (delete forwards)', () => {
  111. const selection = model.createSelection( [
  112. model.createRange(
  113. model.createPositionFromPath( modelRoot, [ 0, 0, 0 ] ),
  114. model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] )
  115. ),
  116. model.createRange(
  117. model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] ),
  118. model.createPositionFromPath( modelRoot, [ 0, 0, 2 ] )
  119. )
  120. ] );
  121. model.change( writer => {
  122. model.deleteContent( selection, {
  123. direction: 'forward'
  124. } );
  125. writer.setSelection( selection );
  126. } );
  127. assertEqualMarkup( getModelData( model ), modelTable( [
  128. [ '[]', '', '13' ],
  129. [ '21', '22', '23' ],
  130. [ '31', '32', '33' ]
  131. ] ) );
  132. } );
  133. } );
  134. describe( 'on user input', () => {
  135. beforeEach( async () => {
  136. await setupEditor( [ Input ] );
  137. } );
  138. it( 'should clear contents of the selected table cells and put selection in last cell on user input', () => {
  139. tableSelection._setCellSelection(
  140. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  141. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  142. );
  143. viewDocument.fire( 'keydown', { keyCode: getCode( 'x' ) } );
  144. // Mutate at the place where the document selection was put; it's more realistic
  145. // than mutating at some arbitrary position.
  146. const placeOfMutation = viewDocument.selection.getFirstRange().start.parent;
  147. viewDocument.fire( 'mutations', [
  148. {
  149. type: 'children',
  150. oldChildren: [],
  151. newChildren: [ new ViewText( viewDocument, 'x' ) ],
  152. node: placeOfMutation
  153. }
  154. ] );
  155. assertEqualMarkup( getModelData( model ), modelTable( [
  156. [ '', '', '13' ],
  157. [ '', 'x[]', '23' ],
  158. [ '31', '32', '33' ]
  159. ] ) );
  160. } );
  161. it( 'should not interfere with default key handler if no table selection', () => {
  162. viewDocument.fire( 'keydown', { keyCode: getCode( 'x' ) } );
  163. // Mutate at the place where the document selection was put; it's more realistic
  164. // than mutating at some arbitrary position.
  165. const placeOfMutation = viewDocument.selection.getFirstRange().start.parent;
  166. viewDocument.fire( 'mutations', [
  167. {
  168. type: 'children',
  169. oldChildren: [],
  170. newChildren: [ new ViewText( viewDocument, 'x' ) ],
  171. node: placeOfMutation
  172. }
  173. ] );
  174. assertEqualMarkup( getModelData( model ), modelTable( [
  175. [ 'x[]11', '12', '13' ],
  176. [ '21', '22', '23' ],
  177. [ '31', '32', '33' ]
  178. ] ) );
  179. } );
  180. } );
  181. describe( 'with other features', () => {
  182. beforeEach( async () => {
  183. await setupEditor( [ Clipboard, HorizontalLine ] );
  184. } );
  185. it( 'allows pasting over multi-cell selection', () => {
  186. tableSelection._setCellSelection(
  187. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  188. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  189. );
  190. const dataTransferMock = {
  191. getData: sinon.stub().withArgs( 'text/plain' ).returns( 'foo' )
  192. };
  193. editor.editing.view.document.fire( 'clipboardInput', {
  194. dataTransfer: dataTransferMock,
  195. stop: sinon.spy()
  196. } );
  197. assertEqualMarkup( getModelData( model ), modelTable( [
  198. [ 'foo[]', '', '13' ],
  199. [ '', '', '23' ],
  200. [ '31', '32', '33' ]
  201. ] ) );
  202. } );
  203. it( 'allows inserting a horizontal line over a multi-range selection', () => {
  204. tableSelection._setCellSelection(
  205. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  206. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  207. );
  208. editor.execute( 'horizontalLine' );
  209. assertEqualMarkup(
  210. getModelData( model ),
  211. '<table>' +
  212. '<tableRow>' +
  213. '<tableCell><horizontalLine></horizontalLine><paragraph>[]</paragraph></tableCell>' +
  214. '<tableCell><paragraph></paragraph></tableCell>' +
  215. '<tableCell><paragraph>13</paragraph></tableCell>' +
  216. '</tableRow>' +
  217. '<tableRow>' +
  218. '<tableCell><paragraph></paragraph></tableCell>' +
  219. '<tableCell><paragraph></paragraph></tableCell>' +
  220. '<tableCell><paragraph>23</paragraph></tableCell>' +
  221. '</tableRow>' +
  222. '<tableRow>' +
  223. '<tableCell><paragraph>31</paragraph></tableCell>' +
  224. '<tableCell><paragraph>32</paragraph></tableCell>' +
  225. '<tableCell><paragraph>33</paragraph></tableCell>' +
  226. '</tableRow>' +
  227. '</table>'
  228. );
  229. } );
  230. } );
  231. async function setupEditor( plugins ) {
  232. element = document.createElement( 'div' );
  233. document.body.appendChild( element );
  234. editor = await ClassicTestEditor.create( element, {
  235. plugins: [ TableEditing, TableSelection, TableClipboard, Paragraph, ...plugins ]
  236. } );
  237. model = editor.model;
  238. modelRoot = model.document.getRoot();
  239. viewDocument = editor.editing.view.document;
  240. tableSelection = editor.plugins.get( TableSelection );
  241. setModelData( model, modelTable( [
  242. [ '[]11', '12', '13' ],
  243. [ '21', '22', '23' ],
  244. [ '31', '32', '33' ]
  245. ] ) );
  246. }
  247. } );