mergecellcommand.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  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 MergeCellCommand from '../../src/commands/mergecellcommand';
  8. import { defaultConversion, defaultSchema, modelTable } from '../_utils/utils';
  9. import TableUtils from '../../src/tableutils';
  10. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  11. describe( 'MergeCellCommand', () => {
  12. let editor, model, command, root;
  13. beforeEach( () => {
  14. return ModelTestEditor
  15. .create( {
  16. plugins: [ TableUtils ]
  17. } )
  18. .then( newEditor => {
  19. editor = newEditor;
  20. model = editor.model;
  21. root = model.document.getRoot( 'main' );
  22. defaultSchema( model.schema );
  23. defaultConversion( editor.conversion );
  24. } );
  25. } );
  26. afterEach( () => {
  27. return editor.destroy();
  28. } );
  29. describe( 'direction=right', () => {
  30. beforeEach( () => {
  31. command = new MergeCellCommand( editor, { direction: 'right' } );
  32. } );
  33. describe( 'isEnabled', () => {
  34. it( 'should be true if in cell that has sibling on the right', () => {
  35. setData( model, modelTable( [
  36. [ '00[]', '01' ]
  37. ] ) );
  38. expect( command.isEnabled ).to.be.true;
  39. } );
  40. it( 'should be false if last cell of a row', () => {
  41. setData( model, modelTable( [
  42. [ '00', '01[]' ]
  43. ] ) );
  44. expect( command.isEnabled ).to.be.false;
  45. } );
  46. it( 'should be true if in a cell that has sibling on the right with the same rowspan', () => {
  47. setData( model, modelTable( [
  48. [ { rowspan: 2, contents: '00[]' }, { rowspan: 2, contents: '01' } ]
  49. ] ) );
  50. expect( command.isEnabled ).to.be.true;
  51. } );
  52. it( 'should be false if in a cell that has sibling but with different rowspan', () => {
  53. setData( model, modelTable( [
  54. [ { rowspan: 2, contents: '00[]' }, { rowspan: 3, contents: '01' } ]
  55. ] ) );
  56. expect( command.isEnabled ).to.be.false;
  57. } );
  58. it( 'should be false when next cell is rowspanned', () => {
  59. setData( model, modelTable( [
  60. [ '00', { rowspan: 3, contents: '01' }, '02' ],
  61. [ '10[]', '12' ],
  62. [ '20', '22' ]
  63. ] ) );
  64. expect( command.isEnabled ).to.be.false;
  65. } );
  66. it( 'should be true when current cell is colspanned', () => {
  67. setData( model, modelTable( [
  68. [ { colspan: 2, contents: '00[]' }, '02' ]
  69. ] ) );
  70. expect( command.isEnabled ).to.be.true;
  71. } );
  72. it( 'should be false if not in a cell', () => {
  73. setData( model, '<paragraph>11[]</paragraph>' );
  74. expect( command.isEnabled ).to.be.false;
  75. } );
  76. it( 'should be false if mergeable cell is in other table section then current cell', () => {
  77. setData( model, modelTable( [
  78. [ '00[]', '01' ]
  79. ], { headingColumns: 1 } ) );
  80. expect( command.isEnabled ).to.be.false;
  81. } );
  82. it( 'should be false if merged cell would cross heading section (mergeable cell with colspan)', () => {
  83. setData( model, modelTable( [
  84. [ '00[]', { colspan: 2, contents: '01' }, '02', '03' ]
  85. ], { headingColumns: 2 } ) );
  86. expect( command.isEnabled ).to.be.false;
  87. } );
  88. it( 'should be true if merged cell would not cross heading section (mergeable cell with colspan)', () => {
  89. setData( model, modelTable( [
  90. [ '00[]', { colspan: 2, contents: '01' }, '02', '03' ]
  91. ], { headingColumns: 3 } ) );
  92. expect( command.isEnabled ).to.be.true;
  93. } );
  94. it( 'should be false if merged cell would cross heading section (current cell with colspan)', () => {
  95. setData( model, modelTable( [
  96. [ { colspan: 2, contents: '00[]' }, '01', '02', '03' ]
  97. ], { headingColumns: 2 } ) );
  98. expect( command.isEnabled ).to.be.false;
  99. } );
  100. it( 'should be true if merged cell would not cross heading section (current cell with colspan)', () => {
  101. setData( model, modelTable( [
  102. [ { colspan: 2, contents: '00[]' }, '01', '02', '03' ]
  103. ], { headingColumns: 3 } ) );
  104. expect( command.isEnabled ).to.be.true;
  105. } );
  106. } );
  107. describe( 'value', () => {
  108. it( 'should be set to mergeable sibling if in cell that has sibling on the right', () => {
  109. setData( model, modelTable( [
  110. [ '00[]', '01' ]
  111. ] ) );
  112. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 1 ] ) );
  113. } );
  114. it( 'should be set to mergeable sibling if in cell that has sibling on the right (selection in block content)', () => {
  115. setData( model, modelTable( [
  116. [ '00', '<paragraph>[]01</paragraph>', '02' ]
  117. ] ) );
  118. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 2 ] ) );
  119. } );
  120. it( 'should be undefined if last cell of a row', () => {
  121. setData( model, modelTable( [
  122. [ '00', '01[]' ]
  123. ] ) );
  124. expect( command.value ).to.be.undefined;
  125. } );
  126. it( 'should be set to mergeable sibling if in a cell that has sibling on the right with the same rowspan', () => {
  127. setData( model, modelTable( [
  128. [ { rowspan: 2, contents: '00[]' }, { rowspan: 2, contents: '01' } ]
  129. ] ) );
  130. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 1 ] ) );
  131. } );
  132. it( 'should be undefined if in a cell that has sibling but with different rowspan', () => {
  133. setData( model, modelTable( [
  134. [ { rowspan: 2, contents: '00[]' }, { rowspan: 3, contents: '01' } ]
  135. ] ) );
  136. expect( command.value ).to.be.undefined;
  137. } );
  138. it( 'should be undefined if not in a cell', () => {
  139. setData( model, '<paragraph>11[]</paragraph>' );
  140. expect( command.value ).to.be.undefined;
  141. } );
  142. } );
  143. describe( 'execute()', () => {
  144. it( 'should merge table cells', () => {
  145. setData( model, modelTable( [
  146. [ '[]00', '01' ]
  147. ] ) );
  148. command.execute();
  149. assertEqualMarkup( getData( model ), modelTable( [
  150. [ { colspan: 2, contents: '<paragraph>[00</paragraph><paragraph>01]</paragraph>' } ]
  151. ] ) );
  152. } );
  153. it( 'should result in single empty paragraph if both cells are empty', () => {
  154. setData( model, modelTable( [
  155. [ '[]', '' ]
  156. ] ) );
  157. command.execute();
  158. assertEqualMarkup( getData( model ), modelTable( [
  159. [ { colspan: 2, contents: '<paragraph>[]</paragraph>' } ]
  160. ] ) );
  161. } );
  162. it( 'should result in single paragraph (other cell is empty)', () => {
  163. setData( model, modelTable( [
  164. [ 'foo[]', '' ]
  165. ] ) );
  166. command.execute();
  167. assertEqualMarkup( getData( model ), modelTable( [
  168. [ { colspan: 2, contents: '<paragraph>[foo]</paragraph>' } ]
  169. ] ) );
  170. } );
  171. it( 'should result in single paragraph (selection cell is empty)', () => {
  172. setData( model, modelTable( [
  173. [ '[]', 'foo' ]
  174. ] ) );
  175. command.execute();
  176. assertEqualMarkup( getData( model ), modelTable( [
  177. [ { colspan: 2, contents: '<paragraph>[foo]</paragraph>' } ]
  178. ] ) );
  179. } );
  180. it( 'should not merge other empty blocks to single block', () => {
  181. model.schema.register( 'block', {
  182. allowWhere: '$block',
  183. allowContentOf: '$block',
  184. isBlock: true
  185. } );
  186. setData( model, modelTable( [
  187. [ '<block>[]</block>', '<block></block>' ]
  188. ] ) );
  189. command.execute();
  190. assertEqualMarkup( getData( model ), modelTable( [
  191. [ { colspan: 2, contents: '<block>[</block><block>]</block>' } ]
  192. ] ) );
  193. } );
  194. } );
  195. } );
  196. describe( 'direction=left', () => {
  197. beforeEach( () => {
  198. command = new MergeCellCommand( editor, { direction: 'left' } );
  199. } );
  200. describe( 'isEnabled', () => {
  201. it( 'should be true if in cell that has sibling on the left', () => {
  202. setData( model, modelTable( [
  203. [ '00', '01[]' ]
  204. ] ) );
  205. expect( command.isEnabled ).to.be.true;
  206. } );
  207. it( 'should be false if first cell of a row', () => {
  208. setData( model, modelTable( [
  209. [ '00[]', '01' ]
  210. ] ) );
  211. expect( command.isEnabled ).to.be.false;
  212. } );
  213. it( 'should be true if in a cell that has sibling on the left with the same rowspan', () => {
  214. setData( model, modelTable( [
  215. [ { rowspan: 2, contents: '00' }, { rowspan: 2, contents: '01[]' } ]
  216. ] ) );
  217. expect( command.isEnabled ).to.be.true;
  218. } );
  219. it( 'should be false if in a cell that has sibling but with different rowspan', () => {
  220. setData( model, modelTable( [
  221. [ { rowspan: 2, contents: '00' }, { rowspan: 3, contents: '01[]' } ]
  222. ] ) );
  223. expect( command.isEnabled ).to.be.false;
  224. } );
  225. it( 'should be false when next cell is rowspanned', () => {
  226. setData( model, modelTable( [
  227. [ '00', { rowspan: 3, contents: '01' }, '02' ],
  228. [ '10', '12[]' ],
  229. [ '20', '22' ]
  230. ] ) );
  231. expect( command.isEnabled ).to.be.false;
  232. } );
  233. it( 'should be true when mergeable cell is colspanned', () => {
  234. setData( model, modelTable( [
  235. [ { colspan: 2, contents: '00' }, '02[]' ]
  236. ] ) );
  237. expect( command.isEnabled ).to.be.true;
  238. } );
  239. it( 'should be false if not in a cell', () => {
  240. setData( model, '<paragraph>11[]</paragraph>' );
  241. expect( command.isEnabled ).to.be.false;
  242. } );
  243. it( 'should be false if mergeable cell is in other table section then current cell', () => {
  244. setData( model, modelTable( [
  245. [ '00', '01[]' ]
  246. ], { headingColumns: 1 } ) );
  247. expect( command.isEnabled ).to.be.false;
  248. } );
  249. it( 'should be false if merged cell would cross heading section (mergeable cell with colspan)', () => {
  250. setData( model, modelTable( [
  251. [ { colspan: 2, contents: '00' }, '02[]', '03' ]
  252. ], { headingColumns: 2 } ) );
  253. expect( command.isEnabled ).to.be.false;
  254. } );
  255. } );
  256. describe( 'value', () => {
  257. it( 'should be set to mergeable sibling if in cell that has sibling on the left', () => {
  258. setData( model, modelTable( [
  259. [ '00', '01[]' ]
  260. ] ) );
  261. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 0 ] ) );
  262. } );
  263. it( 'should be set to mergeable sibling if in cell that has sibling on the left (selection in block content)', () => {
  264. setData( model, modelTable( [
  265. [ '00', '<paragraph>01[]</paragraph>', '02' ]
  266. ] ) );
  267. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 0 ] ) );
  268. } );
  269. it( 'should be undefined if first cell of a row', () => {
  270. setData( model, modelTable( [
  271. [ '00[]', '01' ]
  272. ] ) );
  273. expect( command.value ).to.be.undefined;
  274. } );
  275. it( 'should be set to mergeable sibling if in a cell that has sibling on the left with the same rowspan', () => {
  276. setData( model, modelTable( [
  277. [ { rowspan: 2, contents: '00' }, { rowspan: 2, contents: '01[]' } ]
  278. ] ) );
  279. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 0 ] ) );
  280. } );
  281. it( 'should be undefined if in a cell that has sibling but with different rowspan', () => {
  282. setData( model, modelTable( [
  283. [ { rowspan: 2, contents: '00' }, { rowspan: 3, contents: '01[]' } ]
  284. ] ) );
  285. expect( command.value ).to.be.undefined;
  286. } );
  287. it( 'should be undefined if not in a cell', () => {
  288. setData( model, '<paragraph>11[]</paragraph>' );
  289. expect( command.value ).to.be.undefined;
  290. } );
  291. } );
  292. describe( 'execute()', () => {
  293. it( 'should merge table cells', () => {
  294. setData( model, modelTable( [
  295. [ '00', '[]01' ]
  296. ] ) );
  297. command.execute();
  298. assertEqualMarkup( getData( model ), modelTable( [
  299. [ { colspan: 2, contents: '<paragraph>[00</paragraph><paragraph>01]</paragraph>' } ]
  300. ] ) );
  301. } );
  302. it( 'should result in single empty paragraph if both cells are empty', () => {
  303. setData( model, modelTable( [
  304. [ '', '[]' ]
  305. ] ) );
  306. command.execute();
  307. assertEqualMarkup( getData( model ), modelTable( [
  308. [ { colspan: 2, contents: '<paragraph>[]</paragraph>' } ]
  309. ] ) );
  310. } );
  311. it( 'should result in single paragraph (other cell is empty)', () => {
  312. setData( model, modelTable( [
  313. [ '', 'foo[]' ]
  314. ] ) );
  315. command.execute();
  316. assertEqualMarkup( getData( model ), modelTable( [
  317. [ { colspan: 2, contents: '<paragraph>[foo]</paragraph>' } ]
  318. ] ) );
  319. } );
  320. it( 'should result in single paragraph (selection cell is empty)', () => {
  321. setData( model, modelTable( [
  322. [ 'foo', '[]' ]
  323. ] ) );
  324. command.execute();
  325. assertEqualMarkup( getData( model ), modelTable( [
  326. [ { colspan: 2, contents: '<paragraph>[foo]</paragraph>' } ]
  327. ] ) );
  328. } );
  329. it( 'should not merge other empty blocks to single block', () => {
  330. model.schema.register( 'block', {
  331. allowWhere: '$block',
  332. allowContentOf: '$block',
  333. isBlock: true
  334. } );
  335. setData( model, modelTable( [
  336. [ '<block></block>', '<block>[]</block>' ]
  337. ] ) );
  338. command.execute();
  339. assertEqualMarkup( getData( model ), modelTable( [
  340. [ { colspan: 2, contents: '<block>[</block><block>]</block>' } ]
  341. ] ) );
  342. } );
  343. } );
  344. } );
  345. describe( 'direction=down', () => {
  346. beforeEach( () => {
  347. command = new MergeCellCommand( editor, { direction: 'down' } );
  348. } );
  349. describe( 'isEnabled', () => {
  350. it( 'should be true if in cell that has mergeable cell in next row', () => {
  351. setData( model, modelTable( [
  352. [ '00', '01[]' ],
  353. [ '10', '11' ]
  354. ] ) );
  355. expect( command.isEnabled ).to.be.true;
  356. } );
  357. it( 'should be false if in last row', () => {
  358. setData( model, modelTable( [
  359. [ '00', '01' ],
  360. [ '10[]', '11' ]
  361. ] ) );
  362. expect( command.isEnabled ).to.be.false;
  363. } );
  364. it( 'should be true if in a cell that has mergeable cell with the same colspan', () => {
  365. setData( model, modelTable( [
  366. [ { colspan: 2, contents: '00[]' }, '02' ],
  367. [ { colspan: 2, contents: '01' }, '12' ]
  368. ] ) );
  369. expect( command.isEnabled ).to.be.true;
  370. } );
  371. it( 'should be false if in a cell that potential mergeable cell has different colspan', () => {
  372. setData( model, modelTable( [
  373. [ { colspan: 2, contents: '00[]' }, '02' ],
  374. [ { colspan: 3, contents: '01' } ]
  375. ] ) );
  376. expect( command.isEnabled ).to.be.false;
  377. } );
  378. it( 'should be false if not in a cell', () => {
  379. setData( model, '<paragraph>11[]</paragraph>' );
  380. expect( command.isEnabled ).to.be.false;
  381. } );
  382. it( 'should be false if mergeable cell is in other table section then current cell', () => {
  383. setData( model, modelTable( [
  384. [ '00[]', '01' ],
  385. [ '10', '11' ]
  386. ], { headingRows: 1 } ) );
  387. expect( command.isEnabled ).to.be.false;
  388. } );
  389. } );
  390. describe( 'value', () => {
  391. it( 'should be set to mergeable cell', () => {
  392. setData( model, modelTable( [
  393. [ '00', '01[]' ],
  394. [ '10', '11' ]
  395. ] ) );
  396. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 1, 1 ] ) );
  397. } );
  398. it( 'should be set to mergeable cell (selection in block content)', () => {
  399. setData( model, modelTable( [
  400. [ '00' ],
  401. [ '<paragraph>10[]</paragraph>' ],
  402. [ '20' ]
  403. ] ) );
  404. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 2, 0 ] ) );
  405. } );
  406. it( 'should be undefined if in last row', () => {
  407. setData( model, modelTable( [
  408. [ '00', '01' ],
  409. [ '10[]', '11' ]
  410. ] ) );
  411. expect( command.value ).to.be.undefined;
  412. } );
  413. it( 'should be set to mergeable cell with the same rowspan', () => {
  414. setData( model, modelTable( [
  415. [ { colspan: 2, contents: '00[]' }, '02' ],
  416. [ { colspan: 2, contents: '01' }, '12' ]
  417. ] ) );
  418. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 1, 0 ] ) );
  419. } );
  420. it( 'should be undefined if in a cell that potential mergeable cell has different rowspan', () => {
  421. setData( model, modelTable( [
  422. [ { colspan: 2, contents: '00[]' }, '02' ],
  423. [ { colspan: 3, contents: '01' } ]
  424. ] ) );
  425. expect( command.value ).to.be.undefined;
  426. } );
  427. it( 'should be undefined if mergable cell is in other table section', () => {
  428. setData( model, modelTable( [
  429. [ { rowspan: 2, contents: '00[]' }, '02' ],
  430. [ '12' ],
  431. [ '21', '22' ]
  432. ], { headingRows: 2 } ) );
  433. expect( command.value ).to.be.undefined;
  434. } );
  435. it( 'should be undefined if not in a cell', () => {
  436. setData( model, '<paragraph>11[]</paragraph>' );
  437. expect( command.value ).to.be.undefined;
  438. } );
  439. } );
  440. describe( 'execute()', () => {
  441. it( 'should merge table cells', () => {
  442. setData( model, modelTable( [
  443. [ '00', '01[]' ],
  444. [ '10', '11' ]
  445. ] ) );
  446. command.execute();
  447. assertEqualMarkup( getData( model ), modelTable( [
  448. [ '00', { rowspan: 2, contents: '<paragraph>[01</paragraph><paragraph>11]</paragraph>' } ],
  449. [ '10' ]
  450. ] ) );
  451. } );
  452. it( 'should result in single empty paragraph if both cells are empty', () => {
  453. setData( model, modelTable( [
  454. [ '[]', '' ],
  455. [ '', '' ]
  456. ] ) );
  457. command.execute();
  458. assertEqualMarkup( getData( model ), modelTable( [
  459. [ { rowspan: 2, contents: '[]' }, '' ],
  460. [ '' ]
  461. ] ) );
  462. } );
  463. it( 'should result in single paragraph (other cell is empty)', () => {
  464. setData( model, modelTable( [
  465. [ 'foo[]', '' ],
  466. [ '', '' ]
  467. ] ) );
  468. command.execute();
  469. assertEqualMarkup( getData( model ), modelTable( [
  470. [ { rowspan: 2, contents: '[foo]' }, '' ],
  471. [ '' ]
  472. ] ) );
  473. } );
  474. it( 'should result in single paragraph (selection cell is empty)', () => {
  475. setData( model, modelTable( [
  476. [ '[]', '' ],
  477. [ 'foo', '' ]
  478. ] ) );
  479. command.execute();
  480. assertEqualMarkup( getData( model ), modelTable( [
  481. [ { rowspan: 2, contents: '[foo]' }, '' ],
  482. [ '' ]
  483. ] ) );
  484. } );
  485. it( 'should not merge other empty blocks to single block', () => {
  486. model.schema.register( 'block', {
  487. allowWhere: '$block',
  488. allowContentOf: '$block',
  489. isBlock: true
  490. } );
  491. setData( model, modelTable( [
  492. [ '<block>[]</block>', '' ],
  493. [ '<block></block>', '' ]
  494. ] ) );
  495. command.execute();
  496. assertEqualMarkup( getData( model ), modelTable( [
  497. [ { rowspan: 2, contents: '<block>[</block><block>]</block>' }, '' ],
  498. [ '' ]
  499. ] ) );
  500. } );
  501. it( 'should remove empty row if merging table cells ', () => {
  502. setData( model, modelTable( [
  503. [ { rowspan: 2, contents: '00' }, '01[]', { rowspan: 3, contents: '02' } ],
  504. [ '11' ],
  505. [ '20', '21' ]
  506. ] ) );
  507. command.execute();
  508. assertEqualMarkup( getData( model ), modelTable( [
  509. [ '00', '<paragraph>[01</paragraph><paragraph>11]</paragraph>', { rowspan: 2, contents: '02' } ],
  510. [ '20', '21' ]
  511. ] ) );
  512. } );
  513. it( 'should not reduce rowspan on cells above removed empty row when merging table cells ', () => {
  514. setData( model, modelTable( [
  515. [ { rowspan: 2, contents: '00' }, '01', '02' ],
  516. [ '11', '12' ],
  517. [ { rowspan: 2, contents: '20' }, '21[]', { rowspan: 3, contents: '22' } ],
  518. [ '31' ],
  519. [ '40', '41' ]
  520. ] ) );
  521. command.execute();
  522. assertEqualMarkup( getData( model ), modelTable( [
  523. [ { rowspan: 2, contents: '00' }, '01', '02' ],
  524. [ '11', '12' ],
  525. [ '20', '<paragraph>[21</paragraph><paragraph>31]</paragraph>', { rowspan: 2, contents: '22' } ],
  526. [ '40', '41' ]
  527. ] ) );
  528. } );
  529. } );
  530. } );
  531. describe( 'direction=up', () => {
  532. beforeEach( () => {
  533. command = new MergeCellCommand( editor, { direction: 'up' } );
  534. } );
  535. describe( 'isEnabled', () => {
  536. it( 'should be true if in cell that has mergeable cell in previous row', () => {
  537. setData( model, modelTable( [
  538. [ '00', '01' ],
  539. [ '10', '11[]' ]
  540. ] ) );
  541. expect( command.isEnabled ).to.be.true;
  542. } );
  543. it( 'should be false if in first row', () => {
  544. setData( model, modelTable( [
  545. [ '00[]', '01' ],
  546. [ '10', '11' ]
  547. ] ) );
  548. expect( command.isEnabled ).to.be.false;
  549. } );
  550. it( 'should be true if in a cell that has mergeable cell with the same colspan', () => {
  551. setData( model, modelTable( [
  552. [ { colspan: 2, contents: '00' }, '02' ],
  553. [ { colspan: 2, contents: '01[]' }, '12' ]
  554. ] ) );
  555. expect( command.isEnabled ).to.be.true;
  556. } );
  557. it( 'should be false if in a cell that potential mergeable cell has different colspan', () => {
  558. setData( model, modelTable( [
  559. [ { colspan: 2, contents: '00' }, '02' ],
  560. [ { colspan: 3, contents: '01[]' } ]
  561. ] ) );
  562. expect( command.isEnabled ).to.be.false;
  563. } );
  564. it( 'should be false if not in a cell', () => {
  565. setData( model, '<paragraph>11[]</paragraph>' );
  566. expect( command.isEnabled ).to.be.false;
  567. } );
  568. it( 'should be false if mergeable cell is in other table section then current cell', () => {
  569. setData( model, modelTable( [
  570. [ '00', '01' ],
  571. [ '10[]', '11' ]
  572. ], { headingRows: 1 } ) );
  573. expect( command.isEnabled ).to.be.false;
  574. } );
  575. } );
  576. describe( 'value', () => {
  577. it( 'should be set to mergeable cell', () => {
  578. setData( model, modelTable( [
  579. [ '00', '01' ],
  580. [ '10', '11[]' ]
  581. ] ) );
  582. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 1 ] ) );
  583. } );
  584. it( 'should be set to mergeable cell (selection in block content)', () => {
  585. setData( model, modelTable( [
  586. [ '00' ],
  587. [ '<paragraph>10[]</paragraph>' ],
  588. [ '20' ]
  589. ] ) );
  590. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 0 ] ) );
  591. } );
  592. it( 'should be undefined if in first row', () => {
  593. setData( model, modelTable( [
  594. [ '00[]', '01' ],
  595. [ '10', '11' ]
  596. ] ) );
  597. expect( command.value ).to.be.undefined;
  598. } );
  599. it( 'should be set to mergeable cell with the same rowspan', () => {
  600. setData( model, modelTable( [
  601. [ { colspan: 2, contents: '00' }, '02' ],
  602. [ { colspan: 2, contents: '01[]' }, '12' ]
  603. ] ) );
  604. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 0, 0 ] ) );
  605. } );
  606. it( 'should be set to mergeable cell in rows with spanned cells', () => {
  607. setData( model, modelTable( [
  608. [ { rowspan: 3, contents: '00' }, '11', '12', '13' ],
  609. [ { rowspan: 2, contents: '21' }, '22', '23' ],
  610. [ '32', { rowspan: 2, contents: '33[]' } ],
  611. [ { colspan: 2, contents: '40' }, '42' ]
  612. ] ) );
  613. expect( command.value ).to.equal( root.getNodeByPath( [ 0, 1, 2 ] ) );
  614. } );
  615. it( 'should be undefined if in a cell that potential mergeable cell has different rowspan', () => {
  616. setData( model, modelTable( [
  617. [ { colspan: 2, contents: '00' }, '02' ],
  618. [ { colspan: 3, contents: '01[]' } ]
  619. ] ) );
  620. expect( command.value ).to.be.undefined;
  621. } );
  622. it( 'should be undefined if not in a cell', () => {
  623. setData( model, '<paragraph>11[]</paragraph>' );
  624. expect( command.value ).to.be.undefined;
  625. } );
  626. } );
  627. describe( 'execute()', () => {
  628. it( 'should merge table cells', () => {
  629. setData( model, modelTable( [
  630. [ '00', '01' ],
  631. [ '10', '[]11' ]
  632. ] ) );
  633. command.execute();
  634. assertEqualMarkup( getData( model ), modelTable( [
  635. [ '00', { rowspan: 2, contents: '<paragraph>[01</paragraph><paragraph>11]</paragraph>' } ],
  636. [ '10' ]
  637. ] ) );
  638. } );
  639. it( 'should result in single empty paragraph if both cells are empty', () => {
  640. setData( model, modelTable( [
  641. [ '', '' ],
  642. [ '[]', '' ]
  643. ] ) );
  644. command.execute();
  645. assertEqualMarkup( getData( model ), modelTable( [
  646. [ { rowspan: 2, contents: '[]' }, '' ],
  647. [ '' ]
  648. ] ) );
  649. } );
  650. it( 'should result in single paragraph (other cell is empty)', () => {
  651. setData( model, modelTable( [
  652. [ '', '' ],
  653. [ 'foo[]', '' ]
  654. ] ) );
  655. command.execute();
  656. assertEqualMarkup( getData( model ), modelTable( [
  657. [ { rowspan: 2, contents: '[foo]' }, '' ],
  658. [ '' ]
  659. ] ) );
  660. } );
  661. it( 'should result in single paragraph (selection cell is empty)', () => {
  662. setData( model, modelTable( [
  663. [ 'foo', '' ],
  664. [ '[]', '' ]
  665. ] ) );
  666. command.execute();
  667. assertEqualMarkup( getData( model ), modelTable( [
  668. [ { rowspan: 2, contents: '[foo]' }, '' ],
  669. [ '' ]
  670. ] ) );
  671. } );
  672. it( 'should not merge other empty blocks to single block', () => {
  673. model.schema.register( 'block', {
  674. allowWhere: '$block',
  675. allowContentOf: '$block',
  676. isBlock: true
  677. } );
  678. setData( model, modelTable( [
  679. [ '<block></block>', '' ],
  680. [ '<block>[]</block>', '' ]
  681. ] ) );
  682. command.execute();
  683. assertEqualMarkup( getData( model ), modelTable( [
  684. [ { rowspan: 2, contents: '<block>[</block><block>]</block>' }, '' ],
  685. [ '' ]
  686. ] ) );
  687. } );
  688. it( 'should properly merge cells in rows with spaned cells', () => {
  689. setData( model, modelTable( [
  690. [ { rowspan: 3, contents: '00' }, '11', '12', '13' ],
  691. [ { rowspan: 2, contents: '21' }, '22', '23' ],
  692. [ '32', { rowspan: 2, contents: '33[]' } ],
  693. [ { colspan: 2, contents: '40' }, '42' ]
  694. ] ) );
  695. command.execute();
  696. assertEqualMarkup( getData( model ), modelTable( [
  697. [ { rowspan: 3, contents: '00' }, '11', '12', '13' ],
  698. [
  699. { rowspan: 2, contents: '21' },
  700. '22',
  701. { rowspan: 3, contents: '<paragraph>[23</paragraph><paragraph>33]</paragraph>' }
  702. ],
  703. [ '32' ],
  704. [ { colspan: 2, contents: '40' }, '42' ]
  705. ] ) );
  706. } );
  707. it( 'should remove empty row if merging table cells ', () => {
  708. setData( model, modelTable( [
  709. [ { rowspan: 2, contents: '00' }, '01', { rowspan: 3, contents: '02' } ],
  710. [ '11[]' ],
  711. [ '20', '21' ]
  712. ] ) );
  713. command.execute();
  714. assertEqualMarkup( getData( model ), modelTable( [
  715. [ '00', '<paragraph>[01</paragraph><paragraph>11]</paragraph>', { rowspan: 2, contents: '02' } ],
  716. [ '20', '21' ]
  717. ] ) );
  718. } );
  719. it( 'should not reduce rowspan on cells above removed empty row when merging table cells ', () => {
  720. setData( model, modelTable( [
  721. [ { rowspan: 2, contents: '00' }, '01', '02' ],
  722. [ '11', '12' ],
  723. [ { rowspan: 2, contents: '20' }, '21', { rowspan: 3, contents: '22' } ],
  724. [ '31[]' ],
  725. [ '40', '41' ]
  726. ] ) );
  727. command.execute();
  728. assertEqualMarkup( getData( model ), modelTable( [
  729. [ { rowspan: 2, contents: '00' }, '01', '02' ],
  730. [ '11', '12' ],
  731. [ '20', '<paragraph>[21</paragraph><paragraph>31]</paragraph>', { rowspan: 2, contents: '22' } ],
  732. [ '40', '41' ]
  733. ] ) );
  734. } );
  735. } );
  736. } );
  737. } );