tableselection.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  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, console */
  6. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  7. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  8. import {
  9. getData as getModelData,
  10. setData as setModelData,
  11. stringify as stringifyModel
  12. } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  13. import TableEditing from '../src/tableediting';
  14. import TableSelection from '../src/tableselection';
  15. import { assertSelectedCells, modelTable } from './_utils/utils';
  16. import DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment';
  17. import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
  18. import Typing from '@ckeditor/ckeditor5-typing/src/typing';
  19. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  20. describe( 'table selection', () => {
  21. let editorElement, editor, model, tableSelection, modelRoot, view, viewDocument;
  22. beforeEach( () => {
  23. editorElement = document.createElement( 'div' );
  24. document.body.appendChild( editorElement );
  25. } );
  26. afterEach( async () => {
  27. editorElement.remove();
  28. await editor.destroy();
  29. } );
  30. describe( 'selection by shift+click', () => {
  31. beforeEach( async () => {
  32. // Disables attaching drag mouse events.
  33. sinon.stub( TableSelection.prototype, '_enableMouseDragSelection' );
  34. editor = await createEditor();
  35. model = editor.model;
  36. modelRoot = model.document.getRoot();
  37. view = editor.editing.view;
  38. viewDocument = view.document;
  39. tableSelection = editor.plugins.get( TableSelection );
  40. setModelData( model, modelTable( [
  41. [ '11[]', '12', '13' ],
  42. [ '21', '22', '23' ],
  43. [ '31', '32', '33' ]
  44. ] ) );
  45. } );
  46. afterEach( () => {
  47. TableSelection.prototype._enableMouseDragSelection.restore();
  48. } );
  49. it( 'should do nothing if the plugin is disabled', () => {
  50. tableSelection.isEnabled = false;
  51. viewDocument.fire( 'mousedown', new DomEventData( view, {} ) );
  52. assertSelectedCells( model, [
  53. [ 0, 0, 0 ],
  54. [ 0, 0, 0 ],
  55. [ 0, 0, 0 ]
  56. ] );
  57. } );
  58. it( 'should abort if Shift key was not pressed', () => {
  59. viewDocument.fire( 'mousedown', new DomEventData( view, {
  60. shiftKey: false
  61. } ) );
  62. assertSelectedCells( model, [
  63. [ 0, 0, 0 ],
  64. [ 0, 0, 0 ],
  65. [ 0, 0, 0 ]
  66. ] );
  67. } );
  68. it( 'should abort if started selecting elements outside the table', () => {
  69. const preventDefault = sinon.spy();
  70. model.change( writer => {
  71. const paragraph = writer.createElement( 'paragraph' );
  72. const text = writer.createText( 'foo' );
  73. writer.insert( text, paragraph );
  74. writer.insert( paragraph, model.document.getRoot(), 'end' );
  75. writer.setSelection( paragraph, 'end' );
  76. } );
  77. viewDocument.fire( 'mousedown', new DomEventData( view, {
  78. shiftKey: true,
  79. target: view.domConverter.mapViewToDom(
  80. viewDocument.getRoot().getChild( 1 )
  81. ),
  82. preventDefault
  83. } ) );
  84. assertSelectedCells( model, [
  85. [ 0, 0, 0 ],
  86. [ 0, 0, 0 ],
  87. [ 0, 0, 0 ]
  88. ] );
  89. expect( preventDefault.called ).to.equal( false );
  90. } );
  91. it( 'should abort if clicked a cell that belongs to another table', () => {
  92. const preventDefault = sinon.spy();
  93. setModelData( model, [
  94. modelTable( [
  95. [ '1.11[]', '1.12' ],
  96. [ '1.21', '1.22' ]
  97. ] ),
  98. modelTable( [
  99. [ '2.11', '2.12' ],
  100. [ '2.21', '2.22' ]
  101. ] )
  102. ].join( '' ) );
  103. const domEventDataMock = new DomEventData( view, {
  104. shiftKey: true,
  105. target: view.domConverter.mapViewToDom(
  106. // The second table: figure > table > tbody > tr > td
  107. viewDocument.getRoot().getChild( 1 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 )
  108. ),
  109. preventDefault
  110. } );
  111. viewDocument.fire( 'mousedown', domEventDataMock );
  112. assertSelectedCells( model, [
  113. [ 0, 0 ],
  114. [ 0, 0 ]
  115. ] );
  116. expect( preventDefault.called ).to.equal( false );
  117. } );
  118. it( 'should select all cells in first row', () => {
  119. const preventDefault = sinon.spy();
  120. const domEventDataMock = new DomEventData( view, {
  121. shiftKey: true,
  122. target: view.domConverter.mapViewToDom(
  123. // figure > table > tbody > tr > td
  124. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
  125. ),
  126. preventDefault
  127. } );
  128. viewDocument.fire( 'mousedown', domEventDataMock );
  129. assertSelectedCells( model, [
  130. [ 1, 1, 1 ],
  131. [ 0, 0, 0 ],
  132. [ 0, 0, 0 ]
  133. ] );
  134. expect( preventDefault.called ).to.equal( true );
  135. } );
  136. it( 'should ignore `selectionChange` event when selecting cells ', () => {
  137. const consoleLog = sinon.stub( console, 'log' );
  138. const preventDefault = sinon.spy();
  139. const selectionChangeCallback = sinon.spy();
  140. // Adding a new callback to check whether it will be executed (whether `evt.stop()` is being called).
  141. viewDocument.on( 'selectionChange', selectionChangeCallback );
  142. // No changes were made.
  143. expect( selectionChangeCallback.called ).to.equal( false );
  144. // Start selecting a cell. Disables listening to `selectionChange`.
  145. viewDocument.fire( 'mousedown', new DomEventData( view, {
  146. shiftKey: true,
  147. target: view.domConverter.mapViewToDom(
  148. // figure > table > tbody > tr > td
  149. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
  150. ),
  151. preventDefault
  152. } ) );
  153. // The callback shouldn't be executed.
  154. viewDocument.fire( 'selectionChange' );
  155. expect( selectionChangeCallback.called ).to.equal( false );
  156. // `selectionChange` event should be canceled.
  157. expect( selectionChangeCallback.called ).to.equal( false );
  158. // Enables listening to `selectionChange` event.
  159. viewDocument.fire( 'mouseup' );
  160. viewDocument.fire( 'selectionChange', {
  161. newSelection: view.document.selection
  162. } );
  163. expect( selectionChangeCallback.called ).to.equal( true );
  164. expect( consoleLog.called ).to.equal( true );
  165. expect( consoleLog.firstCall.args[ 0 ] ).to.equal( 'Blocked selectionChange to avoid breaking table cells selection.' );
  166. consoleLog.restore();
  167. } );
  168. } );
  169. describe( 'selection by mouse drag', () => {
  170. let preventDefault;
  171. beforeEach( async () => {
  172. // Disables attaching mouse+Shift events.
  173. sinon.stub( TableSelection.prototype, '_enableShiftClickSelection' );
  174. editor = await createEditor();
  175. model = editor.model;
  176. modelRoot = model.document.getRoot();
  177. view = editor.editing.view;
  178. viewDocument = view.document;
  179. tableSelection = editor.plugins.get( TableSelection );
  180. setModelData( model, modelTable( [
  181. [ '11[]', '12', '13' ],
  182. [ '21', '22', '23' ],
  183. [ '31', '32', '33' ]
  184. ] ) );
  185. preventDefault = sinon.spy();
  186. } );
  187. afterEach( () => {
  188. TableSelection.prototype._enableShiftClickSelection.restore();
  189. } );
  190. it( 'should do nothing if the plugin is disabled', () => {
  191. tableSelection.isEnabled = false;
  192. const domEventDataMock = new DomEventData( view, {} );
  193. viewDocument.fire( 'mousedown', domEventDataMock );
  194. assertSelectedCells( model, [
  195. [ 0, 0, 0 ],
  196. [ 0, 0, 0 ],
  197. [ 0, 0, 0 ]
  198. ] );
  199. } );
  200. it( 'should abort if Shift is pressed', () => {
  201. const domEventDataMock = new DomEventData( view, {
  202. shiftKey: true
  203. } );
  204. viewDocument.fire( 'mousedown', domEventDataMock );
  205. assertSelectedCells( model, [
  206. [ 0, 0, 0 ],
  207. [ 0, 0, 0 ],
  208. [ 0, 0, 0 ]
  209. ] );
  210. } );
  211. it( 'should abort if Ctrl is pressed', () => {
  212. const domEventDataMock = new DomEventData( view, {
  213. ctrlKey: true
  214. } );
  215. viewDocument.fire( 'mousedown', domEventDataMock );
  216. assertSelectedCells( model, [
  217. [ 0, 0, 0 ],
  218. [ 0, 0, 0 ],
  219. [ 0, 0, 0 ]
  220. ] );
  221. } );
  222. it( 'should abort if Alt is pressed', () => {
  223. const domEventDataMock = new DomEventData( view, {
  224. altKey: true
  225. } );
  226. viewDocument.fire( 'mousedown', domEventDataMock );
  227. assertSelectedCells( model, [
  228. [ 0, 0, 0 ],
  229. [ 0, 0, 0 ],
  230. [ 0, 0, 0 ]
  231. ] );
  232. } );
  233. it( 'should do nothing if any of mouse buttons was not clicked', () => {
  234. viewDocument.fire( 'mousemove', new DomEventData( view, {
  235. buttons: 0
  236. } ) );
  237. assertSelectedCells( model, [
  238. [ 0, 0, 0 ],
  239. [ 0, 0, 0 ],
  240. [ 0, 0, 0 ]
  241. ] );
  242. } );
  243. it( 'should do nothing if started dragging outside of table', () => {
  244. model.change( writer => {
  245. const paragraph = writer.createElement( 'paragraph' );
  246. const text = writer.createText( 'foo' );
  247. writer.insert( text, paragraph );
  248. writer.insert( paragraph, model.document.getRoot(), 'end' );
  249. writer.setSelection( paragraph, 'end' );
  250. } );
  251. viewDocument.fire( 'mousedown', new DomEventData( view, {
  252. target: view.domConverter.mapViewToDom(
  253. viewDocument.getRoot().getChild( 1 )
  254. )
  255. } ) );
  256. viewDocument.fire( 'mousemove', new DomEventData( view, {
  257. buttons: 1
  258. } ) );
  259. assertSelectedCells( model, [
  260. [ 0, 0, 0 ],
  261. [ 0, 0, 0 ],
  262. [ 0, 0, 0 ]
  263. ] );
  264. } );
  265. it( 'should do nothing if ended dragging outside of table', () => {
  266. model.change( writer => {
  267. const paragraph = writer.createElement( 'paragraph' );
  268. const text = writer.createText( 'foo' );
  269. writer.insert( text, paragraph );
  270. writer.insert( paragraph, model.document.getRoot(), 'end' );
  271. writer.setSelection( paragraph, 'end' );
  272. } );
  273. viewDocument.fire( 'mousedown', new DomEventData( view, {
  274. target: view.domConverter.mapViewToDom(
  275. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  276. )
  277. } ) );
  278. viewDocument.fire( 'mousemove', new DomEventData( view, {
  279. target: view.domConverter.mapViewToDom(
  280. viewDocument.getRoot().getChild( 1 )
  281. ),
  282. buttons: 1
  283. } ) );
  284. assertSelectedCells( model, [
  285. [ 0, 0, 0 ],
  286. [ 0, 0, 0 ],
  287. [ 0, 0, 0 ]
  288. ] );
  289. } );
  290. it( 'should do nothing if ended dragging inside another table', () => {
  291. setModelData( model, [
  292. modelTable( [
  293. [ '1.11[]', '1.12' ],
  294. [ '1.21', '1.22' ]
  295. ] ),
  296. modelTable( [
  297. [ '2.11', '2.12' ],
  298. [ '2.21', '2.22' ]
  299. ] )
  300. ].join( '' ) );
  301. viewDocument.fire( 'mousedown', new DomEventData( view, {
  302. target: view.domConverter.mapViewToDom(
  303. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  304. )
  305. } ) );
  306. viewDocument.fire( 'mousemove', new DomEventData( view, {
  307. target: view.domConverter.mapViewToDom(
  308. viewDocument.getRoot().getChild( 1 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 )
  309. ),
  310. buttons: 1
  311. } ) );
  312. assertSelectedCells( model, [
  313. [ 0, 0 ],
  314. [ 0, 0 ]
  315. ] );
  316. } );
  317. it( 'should do nothing if ended in the same cell', () => {
  318. viewDocument.fire( 'mousedown', new DomEventData( view, {
  319. target: view.domConverter.mapViewToDom(
  320. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  321. )
  322. } ) );
  323. viewDocument.fire( 'mousemove', new DomEventData( view, {
  324. target: view.domConverter.mapViewToDom(
  325. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  326. ),
  327. buttons: 1
  328. } ) );
  329. assertSelectedCells( model, [
  330. [ 0, 0, 0 ],
  331. [ 0, 0, 0 ],
  332. [ 0, 0, 0 ]
  333. ] );
  334. } );
  335. it( 'should select started and ended dragging in the same cell but went over its border', () => {
  336. viewDocument.fire( 'mousedown', new DomEventData( view, {
  337. target: view.domConverter.mapViewToDom(
  338. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  339. )
  340. } ) );
  341. // Select the next one.
  342. viewDocument.fire( 'mousemove', new DomEventData( view, {
  343. target: view.domConverter.mapViewToDom(
  344. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 1 )
  345. ),
  346. buttons: 1,
  347. preventDefault: sinon.spy()
  348. } ) );
  349. // And back to the "started" cell.
  350. viewDocument.fire( 'mousemove', new DomEventData( view, {
  351. target: view.domConverter.mapViewToDom(
  352. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  353. ),
  354. buttons: 1,
  355. preventDefault: sinon.spy()
  356. } ) );
  357. viewDocument.fire( 'mouseup' );
  358. assertSelectedCells( model, [
  359. [ 1, 0, 0 ],
  360. [ 0, 0, 0 ],
  361. [ 0, 0, 0 ]
  362. ] );
  363. } );
  364. it( 'should select all cells in first row', () => {
  365. viewDocument.fire( 'mousedown', new DomEventData( view, {
  366. target: view.domConverter.mapViewToDom(
  367. // figure > table > tbody > tr > td
  368. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 )
  369. )
  370. } ) );
  371. viewDocument.fire( 'mousemove', new DomEventData( view, {
  372. target: view.domConverter.mapViewToDom(
  373. // figure > table > tbody > tr > td
  374. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
  375. ),
  376. buttons: 1,
  377. preventDefault
  378. } ) );
  379. viewDocument.fire( 'mouseup' );
  380. assertSelectedCells( model, [
  381. [ 1, 1, 1 ],
  382. [ 0, 0, 0 ],
  383. [ 0, 0, 0 ]
  384. ] );
  385. expect( preventDefault.called ).to.equal( true );
  386. } );
  387. it( 'should ignore `selectionChange` event when selecting cells ', () => {
  388. const consoleLog = sinon.stub( console, 'log' );
  389. const preventDefault = sinon.spy();
  390. const selectionChangeCallback = sinon.spy();
  391. // Adding a new callback to check whether it will be executed (whether `evt.stop()` is being called).
  392. viewDocument.on( 'selectionChange', selectionChangeCallback );
  393. // No changes were made.
  394. expect( selectionChangeCallback.called ).to.equal( false );
  395. // Click on a cell.
  396. viewDocument.fire( 'mousedown', new DomEventData( view, {
  397. target: view.domConverter.mapViewToDom(
  398. // figure > table > tbody > tr > td
  399. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 1 )
  400. )
  401. } ) );
  402. // Then move the mouse to another cell. Disables listening to `selectionChange`.
  403. viewDocument.fire( 'mousemove', new DomEventData( view, {
  404. buttons: 1,
  405. target: view.domConverter.mapViewToDom(
  406. // figure > table > tbody > tr > td
  407. viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 2 )
  408. ),
  409. preventDefault
  410. } ) );
  411. // The callback shouldn't be executed.
  412. viewDocument.fire( 'selectionChange' );
  413. // `selectionChange` event should be canceled.
  414. expect( selectionChangeCallback.called ).to.equal( false );
  415. // Enables listening to `selectionChange` event.
  416. viewDocument.fire( 'mouseup' );
  417. viewDocument.fire( 'selectionChange', {
  418. newSelection: view.document.selection
  419. } );
  420. expect( selectionChangeCallback.called ).to.equal( true );
  421. expect( consoleLog.called ).to.equal( true );
  422. expect( consoleLog.firstCall.args[ 0 ] ).to.equal( 'Blocked selectionChange to avoid breaking table cells selection.' );
  423. consoleLog.restore();
  424. } );
  425. } );
  426. describe( 'getSelectedTableCells()', () => {
  427. beforeEach( async () => {
  428. editor = await createEditor();
  429. model = editor.model;
  430. modelRoot = model.document.getRoot();
  431. view = editor.editing.view;
  432. viewDocument = view.document;
  433. tableSelection = editor.plugins.get( TableSelection );
  434. setModelData( model, modelTable( [
  435. [ '11[]', '12', '13' ],
  436. [ '21', '22', '23' ],
  437. [ '31', '32', '33' ]
  438. ] ) );
  439. } );
  440. it( 'should return nothing if selection is not started', () => {
  441. expect( tableSelection.getSelectedTableCells() ).to.be.null;
  442. } );
  443. it( 'should return two table cells', () => {
  444. const firstCell = modelRoot.getNodeByPath( [ 0, 0, 0 ] );
  445. const lastCell = modelRoot.getNodeByPath( [ 0, 0, 1 ] );
  446. tableSelection._setCellSelection(
  447. firstCell,
  448. lastCell
  449. );
  450. expect( tableSelection.getSelectedTableCells() ).to.deep.equal( [
  451. firstCell, lastCell
  452. ] );
  453. } );
  454. it( 'should return four table cells for diagonal selection', () => {
  455. const firstCell = modelRoot.getNodeByPath( [ 0, 0, 0 ] );
  456. const lastCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] );
  457. tableSelection._setCellSelection(
  458. firstCell,
  459. lastCell
  460. );
  461. expect( tableSelection.getSelectedTableCells() ).to.deep.equal( [
  462. firstCell, modelRoot.getNodeByPath( [ 0, 0, 1 ] ), modelRoot.getNodeByPath( [ 0, 1, 0 ] ), lastCell
  463. ] );
  464. } );
  465. it( 'should return row table cells', () => {
  466. const firstCell = modelRoot.getNodeByPath( [ 0, 0, 0 ] );
  467. const lastCell = modelRoot.getNodeByPath( [ 0, 0, 2 ] );
  468. tableSelection._setCellSelection(
  469. firstCell,
  470. lastCell
  471. );
  472. expect( tableSelection.getSelectedTableCells() ).to.deep.equal( [
  473. firstCell, modelRoot.getNodeByPath( [ 0, 0, 1 ] ), lastCell
  474. ] );
  475. } );
  476. it( 'should return column table cells', () => {
  477. const firstCell = modelRoot.getNodeByPath( [ 0, 0, 1 ] );
  478. const lastCell = modelRoot.getNodeByPath( [ 0, 2, 1 ] );
  479. tableSelection._setCellSelection( firstCell, lastCell );
  480. expect( tableSelection.getSelectedTableCells() ).to.deep.equal( [
  481. firstCell, modelRoot.getNodeByPath( [ 0, 1, 1 ] ), lastCell
  482. ] );
  483. } );
  484. it( 'should return cells in source order despite backward selection', () => {
  485. const firstCell = modelRoot.getNodeByPath( [ 0, 0, 2 ] );
  486. const lastCell = modelRoot.getNodeByPath( [ 0, 0, 1 ] );
  487. tableSelection._setCellSelection( firstCell, lastCell );
  488. expect( tableSelection.getSelectedTableCells() ).to.deep.equal( [
  489. lastCell, firstCell
  490. ] );
  491. } );
  492. } );
  493. describe( 'getSelectionAsFragment()', () => {
  494. beforeEach( async () => {
  495. editor = await createEditor();
  496. model = editor.model;
  497. modelRoot = model.document.getRoot();
  498. view = editor.editing.view;
  499. viewDocument = view.document;
  500. tableSelection = editor.plugins.get( TableSelection );
  501. setModelData( model, modelTable( [
  502. [ '11[]', '12', '13' ],
  503. [ '21', '22', '23' ],
  504. [ '31', '32', '33' ]
  505. ] ) );
  506. } );
  507. it( 'should return undefined if no table cells are selected', () => {
  508. expect( tableSelection.getSelectionAsFragment() ).to.be.null;
  509. } );
  510. it( 'should return document fragment for selected table cells', () => {
  511. tableSelection._setCellSelection(
  512. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  513. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  514. );
  515. expect( tableSelection.getSelectionAsFragment() ).to.be.instanceOf( DocumentFragment );
  516. } );
  517. it( 'should return cells in the source order in case of forward selection', () => {
  518. tableSelection._setCellSelection(
  519. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  520. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  521. );
  522. expect( stringifyModel( tableSelection.getSelectionAsFragment() ) ).to.equal( modelTable( [
  523. [ '11', '12' ],
  524. [ '21', '22' ]
  525. ] ) );
  526. } );
  527. it( 'should return cells in the source order in case of backward selection', () => {
  528. tableSelection._setCellSelection(
  529. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  530. modelRoot.getNodeByPath( [ 0, 0, 0 ] )
  531. );
  532. expect( editor.model.document.selection.isBackward ).to.be.true;
  533. expect( stringifyModel( tableSelection.getSelectionAsFragment() ) ).to.equal( modelTable( [
  534. [ '11', '12' ],
  535. [ '21', '22' ]
  536. ] ) );
  537. } );
  538. } );
  539. describe( 'delete content', () => {
  540. beforeEach( async () => {
  541. editor = await createEditor();
  542. model = editor.model;
  543. modelRoot = model.document.getRoot();
  544. view = editor.editing.view;
  545. viewDocument = view.document;
  546. tableSelection = editor.plugins.get( TableSelection );
  547. setModelData( model, modelTable( [
  548. [ '11[]', '12', '13' ],
  549. [ '21', '22', '23' ],
  550. [ '31', '32', '33' ]
  551. ] ) );
  552. } );
  553. it( 'should put selection in the last selected cell after removing content (backward delete)', () => {
  554. tableSelection._setCellSelection(
  555. modelRoot.getChild( 0 ).getChild( 0 ).getChild( 0 ),
  556. modelRoot.getChild( 0 ).getChild( 1 ).getChild( 1 )
  557. );
  558. editor.execute( 'delete' );
  559. assertEqualMarkup( getModelData( model ), modelTable( [
  560. [ '', '', '13' ],
  561. [ '', '[]', '23' ],
  562. [ '31', '32', '33' ]
  563. ] ) );
  564. } );
  565. it( 'should put selection in the last selected cell after removing content (forward delete)', () => {
  566. tableSelection._setCellSelection(
  567. modelRoot.getChild( 0 ).getChild( 0 ).getChild( 0 ),
  568. modelRoot.getChild( 0 ).getChild( 1 ).getChild( 1 )
  569. );
  570. editor.execute( 'delete' );
  571. assertEqualMarkup( getModelData( model ), modelTable( [
  572. [ '', '', '13' ],
  573. [ '', '[]', '23' ],
  574. [ '31', '32', '33' ]
  575. ] ) );
  576. } );
  577. it( 'should clear single cell if selected', () => {
  578. tableSelection._setCellSelection(
  579. modelRoot.getChild( 0 ).getChild( 0 ).getChild( 0 ),
  580. modelRoot.getChild( 0 ).getChild( 0 ).getChild( 0 ),
  581. );
  582. editor.execute( 'forwardDelete' );
  583. assertEqualMarkup( getModelData( model ), modelTable( [
  584. [ '[]', '12', '13' ],
  585. [ '21', '22', '23' ],
  586. [ '31', '32', '33' ]
  587. ] ) );
  588. } );
  589. } );
  590. function createEditor() {
  591. return ClassicTestEditor.create( editorElement, {
  592. plugins: [ TableEditing, TableSelection, Paragraph, Typing ]
  593. } );
  594. }
  595. } );