setheaderrowcommand.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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. import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
  6. import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  7. import SetHeaderRowCommand from '../../src/commands/setheaderrowcommand';
  8. import { assertSelectedCells, defaultConversion, defaultSchema, modelTable } from '../_utils/utils';
  9. import TableSelection from '../../src/tableselection';
  10. import TableUtils from '../../src/tableutils';
  11. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  12. describe( 'SetHeaderRowCommand', () => {
  13. let editor, model, command;
  14. beforeEach( () => {
  15. return ModelTestEditor
  16. .create( {
  17. plugins: [ TableUtils, TableSelection ]
  18. } )
  19. .then( newEditor => {
  20. editor = newEditor;
  21. model = editor.model;
  22. command = new SetHeaderRowCommand( editor );
  23. defaultSchema( model.schema );
  24. defaultConversion( editor.conversion );
  25. } );
  26. } );
  27. afterEach( () => {
  28. return editor.destroy();
  29. } );
  30. describe( 'isEnabled', () => {
  31. it( 'should be false if selection is not in a table', () => {
  32. setData( model, '<paragraph>foo[]</paragraph>' );
  33. expect( command.isEnabled ).to.be.false;
  34. } );
  35. it( 'should be true if selection is in table', () => {
  36. setData( model, '<table><tableRow><tableCell><paragraph>foo[]</paragraph></tableCell></tableRow></table>' );
  37. expect( command.isEnabled ).to.be.true;
  38. } );
  39. it( 'should be true if multiple cells are selected', () => {
  40. setData( model, modelTable( [
  41. [ '01', '02', '03' ]
  42. ] ) );
  43. const tableSelection = editor.plugins.get( TableSelection );
  44. const modelRoot = model.document.getRoot();
  45. tableSelection._setCellSelection(
  46. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  47. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  48. );
  49. expect( command.isEnabled ).to.be.true;
  50. } );
  51. it( 'should be true if multiple cells in a header row are selected', () => {
  52. setData( model, modelTable( [
  53. [ '01', '02', '03' ]
  54. ], { headingRows: 1 } ) );
  55. const tableSelection = editor.plugins.get( TableSelection );
  56. const modelRoot = model.document.getRoot();
  57. tableSelection._setCellSelection(
  58. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  59. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  60. );
  61. expect( command.isEnabled ).to.be.true;
  62. } );
  63. } );
  64. describe( 'value', () => {
  65. it( 'should be false if selection is not in a table without heading row', () => {
  66. setData( model, modelTable( [
  67. [ '01[]', '02' ],
  68. [ '11', '12' ]
  69. ] ) );
  70. expect( command.value ).to.be.false;
  71. } );
  72. it( 'should be false if selection is not in a heading row', () => {
  73. setData( model, modelTable( [
  74. [ '01', '02' ],
  75. [ '11', '12[]' ]
  76. ], { headingRows: 1 } ) );
  77. expect( command.value ).to.be.false;
  78. } );
  79. it( 'should be true if selection is in a heading row', () => {
  80. setData( model, modelTable( [
  81. [ '01[]', '02' ],
  82. [ '11', '12' ]
  83. ], { headingRows: 1 } ) );
  84. expect( command.value ).to.be.true;
  85. } );
  86. it( 'should be false if selection is in a heading column', () => {
  87. setData( model, modelTable( [
  88. [ '01', '02' ],
  89. [ '11[]', '12' ]
  90. ], { headingRows: 1, headingColumns: 1 } ) );
  91. expect( command.value ).to.be.false;
  92. } );
  93. it( 'should be true if multiple header rows are selected', () => {
  94. setData( model, modelTable( [
  95. [ '01', '02' ],
  96. [ '11', '12' ]
  97. ], { headingRows: 2 } ) );
  98. const tableSelection = editor.plugins.get( TableSelection );
  99. const modelRoot = model.document.getRoot();
  100. tableSelection._setCellSelection(
  101. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  102. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  103. );
  104. expect( command.value ).to.be.true;
  105. } );
  106. it( 'should be true if multiple header columns are selected in reversed order', () => {
  107. setData( model, modelTable( [
  108. [ '01', '02' ],
  109. [ '11', '12' ]
  110. ], { headingRows: 2 } ) );
  111. const tableSelection = editor.plugins.get( TableSelection );
  112. const modelRoot = model.document.getRoot();
  113. tableSelection._setCellSelection(
  114. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  115. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  116. );
  117. expect( command.value ).to.be.true;
  118. } );
  119. it( 'should be false if only part of selected columns are headers', () => {
  120. setData( model, modelTable( [
  121. [ '01', '02' ],
  122. [ '11', '12' ]
  123. ], { headingRows: 1 } ) );
  124. const tableSelection = editor.plugins.get( TableSelection );
  125. const modelRoot = model.document.getRoot();
  126. tableSelection._setCellSelection(
  127. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  128. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  129. );
  130. expect( command.value ).to.be.false;
  131. } );
  132. } );
  133. describe( 'execute()', () => {
  134. it( 'should set heading rows attribute that cover row in which is selection', () => {
  135. setData( model, modelTable( [
  136. [ '00' ],
  137. [ '[]10' ],
  138. [ '20' ],
  139. [ '30' ]
  140. ] ) );
  141. command.execute();
  142. assertEqualMarkup( getData( model ), modelTable( [
  143. [ '00' ],
  144. [ '[]10' ],
  145. [ '20' ],
  146. [ '30' ]
  147. ], { headingRows: 2 } ) );
  148. } );
  149. it( 'should toggle heading rows attribute', () => {
  150. setData( model, modelTable( [
  151. [ '00' ],
  152. [ '[]10' ],
  153. [ '20' ],
  154. [ '30' ]
  155. ], { headingRows: 2 } ) );
  156. command.execute();
  157. assertEqualMarkup( getData( model ), modelTable( [
  158. [ '00' ],
  159. [ '[]10' ],
  160. [ '20' ],
  161. [ '30' ]
  162. ], { headingRows: 1 } ) );
  163. command.execute();
  164. setData( model, modelTable( [
  165. [ '00' ],
  166. [ '[]10' ],
  167. [ '20' ],
  168. [ '30' ]
  169. ], { headingRows: 2 } ) );
  170. } );
  171. it( 'should set heading rows attribute if currently selected row is a heading so the heading section is below this row', () => {
  172. setData( model, modelTable( [
  173. [ '00' ],
  174. [ '[]10' ],
  175. [ '20' ],
  176. [ '30' ]
  177. ], { headingRows: 3 } ) );
  178. command.execute();
  179. assertEqualMarkup( getData( model ), modelTable( [
  180. [ '00' ],
  181. [ '[]10' ],
  182. [ '20' ],
  183. [ '30' ]
  184. ], { headingRows: 1 } ) );
  185. } );
  186. it( 'should unsetset heading rows attribute', () => {
  187. setData( model, modelTable( [
  188. [ '[]00' ],
  189. [ '10' ],
  190. [ '20' ],
  191. [ '30' ]
  192. ], { headingRows: 3 } ) );
  193. command.execute();
  194. assertEqualMarkup( getData( model ), modelTable( [
  195. [ '[]00' ],
  196. [ '10' ],
  197. [ '20' ],
  198. [ '30' ]
  199. ] ) );
  200. } );
  201. describe( 'multi-cell selection', () => {
  202. it( 'should set it correctly in a middle of multi-row table', () => {
  203. setData( model, modelTable( [
  204. [ '00' ],
  205. [ '10' ],
  206. [ '20' ],
  207. [ '30' ]
  208. ] ) );
  209. const tableSelection = editor.plugins.get( TableSelection );
  210. const modelRoot = model.document.getRoot();
  211. tableSelection._setCellSelection(
  212. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  213. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  214. );
  215. command.execute();
  216. assertEqualMarkup( getData( model, { withoutSelection: true } ), modelTable( [
  217. [ '00' ],
  218. [ '10' ],
  219. [ '20' ],
  220. [ '30' ]
  221. ], {
  222. headingRows: 3
  223. } ) );
  224. assertSelectedCells( model, [
  225. [ 0 ],
  226. [ 1 ],
  227. [ 1 ],
  228. [ 0 ]
  229. ] );
  230. } );
  231. it( 'should set it correctly in a middle of multi-row table - reversed selection', () => {
  232. setData( model, modelTable( [
  233. [ '00' ],
  234. [ '10' ],
  235. [ '20' ],
  236. [ '30' ]
  237. ] ) );
  238. const tableSelection = editor.plugins.get( TableSelection );
  239. const modelRoot = model.document.getRoot();
  240. tableSelection._setCellSelection(
  241. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  242. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  243. );
  244. command.execute();
  245. assertEqualMarkup( getData( model, { withoutSelection: true } ), modelTable( [
  246. [ '00' ],
  247. [ '10' ],
  248. [ '20' ],
  249. [ '30' ]
  250. ], {
  251. headingRows: 3
  252. } ) );
  253. assertSelectedCells( model, [
  254. [ 0 ],
  255. [ 1 ],
  256. [ 1 ],
  257. [ 0 ]
  258. ] );
  259. } );
  260. it( 'should set it correctly in a middle of multi-row, multiple cell selection', () => {
  261. setData( model, modelTable( [
  262. [ '00', '01', '02', '03' ],
  263. [ '10', '11', '12', '13' ],
  264. [ '20', '21', '22', '23' ],
  265. [ '30', '31', '32', '33' ]
  266. ] ) );
  267. const tableSelection = editor.plugins.get( TableSelection );
  268. const modelRoot = model.document.getRoot();
  269. tableSelection._setCellSelection(
  270. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  271. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  272. );
  273. command.execute();
  274. assertEqualMarkup( getData( model, { withoutSelection: true } ), modelTable( [
  275. [ '00', '01', '02', '03' ],
  276. [ '10', '11', '12', '13' ],
  277. [ '20', '21', '22', '23' ],
  278. [ '30', '31', '32', '33' ]
  279. ], {
  280. headingRows: 3
  281. } ) );
  282. assertSelectedCells( model, [
  283. [ 0, 0, 0, 0 ],
  284. [ 0, 1, 1, 0 ],
  285. [ 0, 1, 1, 0 ],
  286. [ 0, 0, 0, 0 ]
  287. ] );
  288. } );
  289. it( 'should remove header rows in case of multiple cell selection', () => {
  290. setData( model, modelTable( [
  291. [ '00' ],
  292. [ '10' ],
  293. [ '20' ],
  294. [ '30' ]
  295. ], { headingRows: 4 } ) );
  296. const tableSelection = editor.plugins.get( TableSelection );
  297. const modelRoot = model.document.getRoot();
  298. tableSelection._setCellSelection(
  299. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  300. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  301. );
  302. command.execute();
  303. assertEqualMarkup( getData( model, { withoutSelection: true } ), modelTable( [
  304. [ '00' ],
  305. [ '10' ],
  306. [ '20' ],
  307. [ '30' ]
  308. ], {
  309. headingRows: 1
  310. } ) );
  311. assertSelectedCells( model, [
  312. [ 0 ],
  313. [ 1 ],
  314. [ 1 ],
  315. [ 0 ]
  316. ] );
  317. } );
  318. it( 'should respect forceValue=true in case of multiple row selection', () => {
  319. setData( model, modelTable( [
  320. [ '00' ],
  321. [ '10' ],
  322. [ '20' ],
  323. [ '30' ]
  324. ], {
  325. headingRows: 3
  326. } ) );
  327. const tableSelection = editor.plugins.get( TableSelection );
  328. const modelRoot = model.document.getRoot();
  329. tableSelection._setCellSelection(
  330. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  331. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  332. );
  333. command.execute( { forceValue: true } );
  334. assertEqualMarkup( getData( model, {
  335. withoutSelection: true
  336. } ), modelTable( [
  337. [ '00' ],
  338. [ '10' ],
  339. [ '20' ],
  340. [ '30' ]
  341. ], {
  342. headingRows: 3
  343. } ) );
  344. assertSelectedCells( model, [
  345. [ 0 ],
  346. [ 1 ],
  347. [ 1 ],
  348. [ 0 ]
  349. ] );
  350. } );
  351. it( 'should respect forceValue=false in case of multiple cell selection', () => {
  352. setData( model, modelTable( [
  353. [ '00' ],
  354. [ '10' ],
  355. [ '20' ],
  356. [ '30' ]
  357. ], {
  358. headingRows: 1
  359. } ) );
  360. const tableSelection = editor.plugins.get( TableSelection );
  361. const modelRoot = model.document.getRoot();
  362. tableSelection._setCellSelection(
  363. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  364. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  365. );
  366. command.execute( { forceValue: false } );
  367. assertEqualMarkup( getData( model, {
  368. withoutSelection: true
  369. } ), modelTable( [
  370. [ '00' ],
  371. [ '10' ],
  372. [ '20' ],
  373. [ '30' ]
  374. ], {
  375. headingRows: 1
  376. } ) );
  377. assertSelectedCells( model, [
  378. [ 0 ],
  379. [ 1 ],
  380. [ 1 ],
  381. [ 0 ]
  382. ] );
  383. } );
  384. } );
  385. it( 'should respect forceValue parameter #1', () => {
  386. setData( model, modelTable( [
  387. [ '00' ],
  388. [ '[]10' ],
  389. [ '20' ],
  390. [ '30' ]
  391. ], { headingRows: 3 } ) );
  392. command.execute( { forceValue: true } );
  393. assertEqualMarkup( getData( model ), modelTable( [
  394. [ '00' ],
  395. [ '[]10' ],
  396. [ '20' ],
  397. [ '30' ]
  398. ], { headingRows: 3 } ) );
  399. } );
  400. it( 'should respect forceValue parameter #2', () => {
  401. setData( model, modelTable( [
  402. [ '00' ],
  403. [ '[]10' ],
  404. [ '20' ],
  405. [ '30' ]
  406. ], { headingRows: 1 } ) );
  407. command.execute( { forceValue: false } );
  408. assertEqualMarkup( getData( model ), modelTable( [
  409. [ '00' ],
  410. [ '[]10' ],
  411. [ '20' ],
  412. [ '30' ]
  413. ], { headingRows: 1 } ) );
  414. } );
  415. it( 'should fix rowspaned cells on the edge of an table head section', () => {
  416. setData( model, modelTable( [
  417. [ '00', '01', '02' ],
  418. [ { colspan: 2, rowspan: 2, contents: '10[]' }, '12' ],
  419. [ '22' ]
  420. ], { headingColumns: 2, headingRows: 1 } ) );
  421. command.execute();
  422. assertEqualMarkup( getData( model ), modelTable( [
  423. [ '00', '01', '02' ],
  424. [ { colspan: 2, contents: '10[]' }, '12' ],
  425. [ { colspan: 2, contents: '' }, '22' ]
  426. ], { headingColumns: 2, headingRows: 2 } ) );
  427. } );
  428. it( 'should split to at most 2 table cells when fixing rowspaned cells on the edge of an table head section', () => {
  429. setData( model, modelTable( [
  430. [ '00', '01', '02' ],
  431. [ { colspan: 2, rowspan: 5, contents: '10' }, '12' ],
  432. [ '22[]' ],
  433. [ '32' ],
  434. [ '42' ],
  435. [ '52' ]
  436. ], { headingColumns: 2, headingRows: 1 } ) );
  437. command.execute();
  438. assertEqualMarkup( getData( model ), modelTable( [
  439. [ '00', '01', '02' ],
  440. [ { colspan: 2, rowspan: 2, contents: '10' }, '12' ],
  441. [ '22[]' ],
  442. [ { colspan: 2, rowspan: 3, contents: '' }, '32' ],
  443. [ '42' ],
  444. [ '52' ]
  445. ], { headingColumns: 2, headingRows: 3 } ) );
  446. } );
  447. it( 'should fix rowspaned cells on the edge of an table head section when creating section', () => {
  448. setData( model, modelTable( [
  449. [ { rowspan: 2, contents: '00' }, '01' ],
  450. [ '[]11' ]
  451. ], { headingRows: 2 } ) );
  452. command.execute();
  453. assertEqualMarkup( getData( model ), modelTable( [
  454. [ '00', '01' ],
  455. [ '', '[]11' ]
  456. ], { headingRows: 1 } ) );
  457. } );
  458. it( 'should fix rowspaned cells inside a row', () => {
  459. setData( model, modelTable( [
  460. [ '00', { rowspan: 2, contents: '01' } ],
  461. [ '[]10' ]
  462. ], { headingRows: 2 } ) );
  463. command.execute();
  464. assertEqualMarkup( getData( model ), modelTable( [
  465. [ '00', '01' ],
  466. [ '[]10', '' ]
  467. ], { headingRows: 1 } ) );
  468. } );
  469. it( 'should work properly in the first row of a table', () => {
  470. setData( model, modelTable( [
  471. [ '00', '[]01', '02' ],
  472. [ { colspan: 2, rowspan: 2, contents: '10' }, '12' ],
  473. [ '22' ]
  474. ] ) );
  475. command.execute();
  476. assertEqualMarkup( getData( model ), modelTable( [
  477. [ '00', '[]01', '02' ],
  478. [ { colspan: 2, rowspan: 2, contents: '10' }, '12' ],
  479. [ '22' ]
  480. ], { headingRows: 1 } ) );
  481. } );
  482. } );
  483. } );