8
0

downcasttable.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  6. import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
  7. import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  8. import downcastTable, {
  9. downcastAttributeChange,
  10. downcastInsertCell,
  11. downcastInsertRow
  12. } from '../../src/converters/downcasttable';
  13. import { formatModelTable, formattedViewTable, modelTable, viewTable } from '../_utils/utils';
  14. describe( 'downcastTable()', () => {
  15. let editor, model, doc, root, viewDocument;
  16. beforeEach( () => {
  17. return VirtualTestEditor.create()
  18. .then( newEditor => {
  19. editor = newEditor;
  20. model = editor.model;
  21. doc = model.document;
  22. root = doc.getRoot( 'main' );
  23. viewDocument = editor.editing.view;
  24. const conversion = editor.conversion;
  25. const schema = model.schema;
  26. schema.register( 'table', {
  27. allowWhere: '$block',
  28. allowAttributes: [ 'headingRows', 'headingColumns' ],
  29. isBlock: true,
  30. isObject: true
  31. } );
  32. schema.register( 'tableRow', {
  33. allowIn: 'table',
  34. allowAttributes: [],
  35. isBlock: true,
  36. isLimit: true
  37. } );
  38. schema.register( 'tableCell', {
  39. allowIn: 'tableRow',
  40. allowContentOf: '$block',
  41. allowAttributes: [ 'colspan', 'rowspan' ],
  42. isBlock: true,
  43. isLimit: true
  44. } );
  45. conversion.for( 'downcast' ).add( downcastTable() );
  46. conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
  47. conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );
  48. // Insert conversion
  49. conversion.for( 'downcast' ).add( downcastInsertRow() );
  50. conversion.for( 'downcast' ).add( downcastInsertCell() );
  51. conversion.for( 'downcast' ).add( downcastAttributeChange( 'headingRows' ) );
  52. conversion.for( 'downcast' ).add( downcastAttributeChange( 'headingColumns' ) );
  53. } );
  54. } );
  55. it( 'should create table with tbody', () => {
  56. setModelData( model,
  57. '<table>' +
  58. '<tableRow><tableCell></tableCell></tableRow>' +
  59. '</table>'
  60. );
  61. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  62. '<table>' +
  63. '<tbody>' +
  64. '<tr><td></td></tr>' +
  65. '</tbody>' +
  66. '</table>'
  67. );
  68. } );
  69. it( 'should create table with tbody and thead', () => {
  70. setModelData( model,
  71. '<table headingRows="1">' +
  72. '<tableRow><tableCell>1</tableCell></tableRow>' +
  73. '<tableRow><tableCell>2</tableCell></tableRow>' +
  74. '</table>'
  75. );
  76. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  77. '<table>' +
  78. '<thead>' +
  79. '<tr><th>1</th></tr>' +
  80. '</thead>' +
  81. '<tbody>' +
  82. '<tr><td>2</td></tr>' +
  83. '</tbody>' +
  84. '</table>'
  85. );
  86. } );
  87. it( 'should create table with thead', () => {
  88. setModelData( model,
  89. '<table headingRows="2">' +
  90. '<tableRow><tableCell>1</tableCell></tableRow>' +
  91. '<tableRow><tableCell>2</tableCell></tableRow>' +
  92. '</table>'
  93. );
  94. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  95. '<table>' +
  96. '<thead>' +
  97. '<tr><th>1</th></tr>' +
  98. '<tr><th>2</th></tr>' +
  99. '</thead>' +
  100. '</table>'
  101. );
  102. } );
  103. it( 'should create table with heading columns and rows', () => {
  104. setModelData( model,
  105. '<table headingColumns="3" headingRows="1">' +
  106. '<tableRow>' +
  107. '<tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell><tableCell>14</tableCell>' +
  108. '</tableRow>' +
  109. '<tableRow>' +
  110. '<tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell><tableCell>24</tableCell>' +
  111. '</tableRow>' +
  112. '</table>'
  113. );
  114. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  115. '<table>' +
  116. '<thead>' +
  117. '<tr><th>11</th><th>12</th><th>13</th><th>14</th></tr>' +
  118. '</thead>' +
  119. '<tbody>' +
  120. '<tr><th>21</th><th>22</th><th>23</th><td>24</td></tr>' +
  121. '</tbody>' +
  122. '</table>'
  123. );
  124. } );
  125. it( 'should be possible to overwrite', () => {
  126. editor.conversion.elementToElement( { model: 'tableRow', view: 'tr', priority: 'high' } );
  127. editor.conversion.elementToElement( { model: 'tableCell', view: 'td', priority: 'high' } );
  128. editor.conversion.for( 'downcast' ).add( dispatcher => {
  129. dispatcher.on( 'insert:table', ( evt, data, conversionApi ) => {
  130. conversionApi.consumable.consume( data.item, 'insert' );
  131. const tableElement = conversionApi.writer.createContainerElement( 'table', { foo: 'bar' } );
  132. const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
  133. conversionApi.mapper.bindElements( data.item, tableElement );
  134. conversionApi.writer.insert( viewPosition, tableElement );
  135. }, { priority: 'high' } );
  136. } );
  137. setModelData( model,
  138. '<table>' +
  139. '<tableRow><tableCell></tableCell></tableRow>' +
  140. '</table>'
  141. );
  142. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  143. '<table foo="bar">' +
  144. '<tr><td></td></tr>' +
  145. '</table>'
  146. );
  147. } );
  148. describe( 'headingColumns attribute', () => {
  149. it( 'should mark heading columns table cells', () => {
  150. setModelData( model,
  151. '<table headingColumns="2">' +
  152. '<tableRow><tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell></tableRow>' +
  153. '<tableRow><tableCell>21</tableCell><tableCell>22</tableCell><tableCell>23</tableCell></tableRow>' +
  154. '</table>'
  155. );
  156. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  157. '<table>' +
  158. '<tbody>' +
  159. '<tr><th>11</th><th>12</th><td>13</td></tr>' +
  160. '<tr><th>21</th><th>22</th><td>23</td></tr>' +
  161. '</tbody>' +
  162. '</table>'
  163. );
  164. } );
  165. it( 'should mark heading columns table cells when one has colspan attribute', () => {
  166. setModelData( model,
  167. '<table headingColumns="3">' +
  168. '<tableRow>' +
  169. '<tableCell>11</tableCell><tableCell>12</tableCell><tableCell>13</tableCell><tableCell>14</tableCell>' +
  170. '</tableRow>' +
  171. '<tableRow><tableCell colspan="2">21</tableCell><tableCell>23</tableCell><tableCell>24</tableCell></tableRow>' +
  172. '</table>'
  173. );
  174. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  175. '<table>' +
  176. '<tbody>' +
  177. '<tr><th>11</th><th>12</th><th>13</th><td>14</td></tr>' +
  178. '<tr><th colspan="2">21</th><th>23</th><td>24</td></tr>' +
  179. '</tbody>' +
  180. '</table>'
  181. );
  182. } );
  183. it( 'should work with colspan and rowspan attributes on table cells', () => {
  184. // The table in this test looks like a table below:
  185. //
  186. // Row headings | Normal cells
  187. // |
  188. // +----+----+----+----+
  189. // | 11 | 12 | 13 | 14 |
  190. // | +----+ +----+
  191. // | | 22 | | 24 |
  192. // |----+----+ +----+
  193. // | 31 | | 34 |
  194. // | +----+----+
  195. // | | 43 | 44 |
  196. // +----+----+----+----+
  197. setModelData( model,
  198. '<table headingColumns="3">' +
  199. '<tableRow>' +
  200. '<tableCell rowspan="2">11</tableCell>' +
  201. '<tableCell>12</tableCell>' +
  202. '<tableCell rowspan="3">13</tableCell>' +
  203. '<tableCell>14</tableCell>' +
  204. '</tableRow>' +
  205. '<tableRow><tableCell>22</tableCell><tableCell>24</tableCell></tableRow>' +
  206. '<tableRow><tableCell colspan="2" rowspan="2">31</tableCell><tableCell>34</tableCell></tableRow>' +
  207. '<tableRow><tableCell>43</tableCell><tableCell>44</tableCell></tableRow>' +
  208. '</table>'
  209. );
  210. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  211. '<table>' +
  212. '<tbody>' +
  213. '<tr><th rowspan="2">11</th><th>12</th><th rowspan="3">13</th><td>14</td></tr>' +
  214. '<tr><th>22</th><td>24</td></tr>' +
  215. '<tr><th colspan="2" rowspan="2">31</th><td>34</td></tr>' +
  216. '<tr><th>43</th><td>44</td></tr>' +
  217. '</tbody>' +
  218. '</table>'
  219. );
  220. } );
  221. } );
  222. describe( 'downcastInsertRow()', () => {
  223. it( 'should react to changed rows', () => {
  224. setModelData( model, modelTable( [
  225. [ '11', '12' ]
  226. ] ) );
  227. const table = root.getChild( 0 );
  228. model.change( writer => {
  229. const row = writer.createElement( 'tableRow' );
  230. writer.insert( row, table, 1 );
  231. writer.insertElement( 'tableCell', row, 'end' );
  232. writer.insertElement( 'tableCell', row, 'end' );
  233. } );
  234. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  235. [ '11', '12' ],
  236. [ '', '' ]
  237. ] ) );
  238. } );
  239. it( 'should insert row on proper index', () => {
  240. setModelData( model, modelTable( [
  241. [ '11', '12' ],
  242. [ '21', '22' ],
  243. [ '31', '32' ]
  244. ] ) );
  245. const table = root.getChild( 0 );
  246. model.change( writer => {
  247. const row = writer.createElement( 'tableRow' );
  248. writer.insert( row, table, 1 );
  249. writer.insertElement( 'tableCell', row, 'end' );
  250. writer.insertElement( 'tableCell', row, 'end' );
  251. } );
  252. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  253. [ '11', '12' ],
  254. [ '', '' ],
  255. [ '21', '22' ],
  256. [ '31', '32' ]
  257. ] ) );
  258. } );
  259. it( 'should insert row on proper index when table has heading rows defined - insert in body', () => {
  260. setModelData( model, modelTable( [
  261. [ '11', '12' ],
  262. [ '21', '22' ],
  263. [ '31', '32' ]
  264. ], { headingRows: 1 } ) );
  265. const table = root.getChild( 0 );
  266. model.change( writer => {
  267. const row = writer.createElement( 'tableRow' );
  268. writer.insert( row, table, 1 );
  269. writer.insertElement( 'tableCell', row, 'end' );
  270. writer.insertElement( 'tableCell', row, 'end' );
  271. } );
  272. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  273. [ '11', '12' ],
  274. [ '', '' ],
  275. [ '21', '22' ],
  276. [ '31', '32' ]
  277. ], { headingRows: 1 } ) );
  278. } );
  279. it( 'should insert row on proper index when table has heading rows defined - insert in heading', () => {
  280. setModelData( model, modelTable( [
  281. [ '11', '12' ],
  282. [ '21', '22' ],
  283. [ '31', '32' ]
  284. ], { headingRows: 2 } ) );
  285. const table = root.getChild( 0 );
  286. model.change( writer => {
  287. const row = writer.createElement( 'tableRow' );
  288. writer.insert( row, table, 1 );
  289. writer.insertElement( 'tableCell', row, 'end' );
  290. writer.insertElement( 'tableCell', row, 'end' );
  291. } );
  292. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  293. [ '11', '12' ],
  294. [ '', '' ],
  295. [ '21', '22' ],
  296. [ '31', '32' ]
  297. ], { headingRows: 3 } ) );
  298. } );
  299. it( 'should react to changed rows when previous rows\' cells has rowspans', () => {
  300. setModelData( model, modelTable( [
  301. [ { rowspan: 3, contents: '11' }, '12' ],
  302. [ '22' ]
  303. ] ) );
  304. const table = root.getChild( 0 );
  305. model.change( writer => {
  306. const row = writer.createElement( 'tableRow' );
  307. writer.insert( row, table, 2 );
  308. writer.insertElement( 'tableCell', row, 'end' );
  309. } );
  310. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  311. [ { rowspan: 3, contents: '11' }, '12' ],
  312. [ '22' ],
  313. [ '' ]
  314. ] ) );
  315. } );
  316. it( 'should properly create row headings', () => {
  317. setModelData( model, modelTable( [
  318. [ { rowspan: 3, contents: '11' }, '12' ],
  319. [ '22' ]
  320. ], { headingColumns: 1 } ) );
  321. const table = root.getChild( 0 );
  322. model.change( writer => {
  323. const firstRow = writer.createElement( 'tableRow' );
  324. writer.insert( firstRow, table, 2 );
  325. writer.insert( writer.createElement( 'tableCell' ), firstRow, 'end' );
  326. const secondRow = writer.createElement( 'tableRow' );
  327. writer.insert( secondRow, table, 3 );
  328. writer.insert( writer.createElement( 'tableCell' ), secondRow, 'end' );
  329. writer.insert( writer.createElement( 'tableCell' ), secondRow, 'end' );
  330. } );
  331. expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
  332. [ { rowspan: 3, contents: '11', isHeading: true }, '12' ],
  333. [ '22' ],
  334. [ '' ],
  335. [ { contents: '', isHeading: true }, '' ]
  336. ] ) );
  337. } );
  338. } );
  339. describe( 'downcastInsertCell()', () => {
  340. it( 'should add tableCell on proper index in tr', () => {
  341. setModelData( model, modelTable( [
  342. [ '11', '12' ]
  343. ] ) );
  344. const table = root.getChild( 0 );
  345. model.change( writer => {
  346. const row = table.getChild( 0 );
  347. writer.insertElement( 'tableCell', row, 1 );
  348. } );
  349. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  350. [ '11', '', '12' ]
  351. ] ) );
  352. } );
  353. it( 'should add tableCell on proper index in tr when previous have colspans', () => {
  354. setModelData( model, modelTable( [
  355. [ { colspan: 2, contents: '11' }, '13' ]
  356. ] ) );
  357. const table = root.getChild( 0 );
  358. model.change( writer => {
  359. const row = table.getChild( 0 );
  360. writer.insertElement( 'tableCell', row, 1 );
  361. } );
  362. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  363. [ { colspan: 2, contents: '11' }, '', '13' ]
  364. ] ) );
  365. } );
  366. it( 'should add tableCell on proper index in tr when previous row have rowspans', () => {
  367. setModelData( model, modelTable( [
  368. [ { rowspan: 2, contents: '11' }, '13' ],
  369. [ '22', '23' ]
  370. ] ) );
  371. const table = root.getChild( 0 );
  372. model.change( writer => {
  373. writer.insertElement( 'tableCell', table.getChild( 0 ), 1 );
  374. writer.insertElement( 'tableCell', table.getChild( 1 ), 0 );
  375. } );
  376. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  377. [ { rowspan: 2, contents: '11' }, '', '13' ],
  378. [ '', '22', '23' ]
  379. ] ) );
  380. } );
  381. it( 'split cell simulation - simple', () => {
  382. setModelData( model, modelTable( [
  383. [ '11', '12' ],
  384. [ '21', '22' ]
  385. ] ) );
  386. const table = root.getChild( 0 );
  387. model.change( writer => {
  388. const firstRow = table.getChild( 0 );
  389. const secondRow = table.getChild( 1 );
  390. writer.insertElement( 'tableCell', firstRow, 1 );
  391. writer.setAttribute( 'colspan', 2, secondRow.getChild( 0 ) );
  392. } );
  393. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  394. [ '11', '', '12' ],
  395. [ { colspan: 2, contents: '21' }, '22' ]
  396. ] ) );
  397. } );
  398. it( 'merge simulation - simple', () => {
  399. setModelData( model, modelTable( [
  400. [ '11', '12' ],
  401. [ '21', '22' ]
  402. ] ) );
  403. const table = root.getChild( 0 );
  404. model.change( writer => {
  405. const firstRow = table.getChild( 0 );
  406. writer.setAttribute( 'colspan', 2, firstRow.getChild( 0 ) );
  407. writer.remove( firstRow.getChild( 1 ) );
  408. } );
  409. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  410. [ { colspan: 2, contents: '11' } ],
  411. [ '21', '22' ]
  412. ] ) );
  413. } );
  414. } );
  415. describe( 'table attribute change', () => {
  416. it( 'should work for adding heading rows', () => {
  417. setModelData( model, modelTable( [
  418. [ '11', '12' ],
  419. [ '21', '22' ],
  420. [ '31', '32' ]
  421. ] ) );
  422. const table = root.getChild( 0 );
  423. model.change( writer => {
  424. writer.setAttribute( 'headingRows', 2, table );
  425. } );
  426. expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
  427. [ '11', '12' ],
  428. [ '21', '22' ],
  429. [ '31', '32' ]
  430. ], { headingRows: 2 } ) );
  431. } );
  432. it( 'should work for changing number of heading rows to a bigger number', () => {
  433. setModelData( model, modelTable( [
  434. [ '11', '12' ],
  435. [ '21', '22' ],
  436. [ '31', '32' ]
  437. ], { headingRows: 1 } ) );
  438. const table = root.getChild( 0 );
  439. model.change( writer => {
  440. writer.setAttribute( 'headingRows', 2, table );
  441. } );
  442. expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
  443. [ '11', '12' ],
  444. [ '21', '22' ],
  445. [ '31', '32' ]
  446. ], { headingRows: 2 } ) );
  447. } );
  448. it( 'should work for changing number of heading rows to a smaller number', () => {
  449. setModelData( model, modelTable( [
  450. [ '11', '12' ],
  451. [ '21', '22' ],
  452. [ '31', '32' ],
  453. [ '41', '42' ]
  454. ], { headingRows: 3 } ) );
  455. const table = root.getChild( 0 );
  456. model.change( writer => {
  457. writer.setAttribute( 'headingRows', 2, table );
  458. } );
  459. expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
  460. [ '11', '12' ],
  461. [ '21', '22' ],
  462. [ '31', '32' ],
  463. [ '41', '42' ]
  464. ], { headingRows: 2 } ) );
  465. } );
  466. it( 'should work for removing heading rows', () => {
  467. setModelData( model, modelTable( [
  468. [ '11', '12' ],
  469. [ '21', '22' ]
  470. ], { headingRows: 2 } ) );
  471. const table = root.getChild( 0 );
  472. model.change( writer => {
  473. writer.removeAttribute( 'headingRows', table );
  474. } );
  475. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  476. [ '11', '12' ],
  477. [ '21', '22' ]
  478. ] ) );
  479. } );
  480. it( 'should work for making heading rows without tbody', () => {
  481. setModelData( model, modelTable( [
  482. [ '11', '12' ],
  483. [ '21', '22' ]
  484. ] ) );
  485. const table = root.getChild( 0 );
  486. model.change( writer => {
  487. writer.setAttribute( 'headingRows', 2, table );
  488. } );
  489. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  490. [ '11', '12' ],
  491. [ '21', '22' ]
  492. ], { headingRows: 2 } ) );
  493. } );
  494. it( 'should work for adding heading columns', () => {
  495. setModelData( model, modelTable( [
  496. [ '11', '12' ],
  497. [ '21', '22' ]
  498. ] ) );
  499. const table = root.getChild( 0 );
  500. model.change( writer => {
  501. writer.setAttribute( 'headingColumns', 1, table );
  502. } );
  503. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  504. [ { isHeading: true, contents: '11' }, '12' ],
  505. [ { isHeading: true, contents: '21' }, '22' ]
  506. ] ) );
  507. } );
  508. it( 'should work for changing heading columns to a bigger number', () => {
  509. setModelData( model, modelTable( [
  510. [ '11', '12', '13', '14' ],
  511. [ '21', '22', '23', '24' ]
  512. ], { headingColumns: 1 } ) );
  513. const table = root.getChild( 0 );
  514. model.change( writer => {
  515. writer.setAttribute( 'headingColumns', 3, table );
  516. } );
  517. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  518. [ { isHeading: true, contents: '11' }, { isHeading: true, contents: '12' }, { isHeading: true, contents: '13' }, '14' ],
  519. [ { isHeading: true, contents: '21' }, { isHeading: true, contents: '22' }, { isHeading: true, contents: '23' }, '24' ]
  520. ] ) );
  521. } );
  522. it( 'should work for removing heading columns', () => {
  523. setModelData( model, modelTable( [
  524. [ '11', '12' ],
  525. [ '21', '22' ]
  526. ], { headingColumns: 1 } ) );
  527. const table = root.getChild( 0 );
  528. model.change( writer => {
  529. writer.removeAttribute( 'headingColumns', table );
  530. } );
  531. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal( viewTable( [
  532. [ '11', '12' ],
  533. [ '21', '22' ]
  534. ] ) );
  535. } );
  536. it( 'should be possible to overwrite', () => {
  537. editor.conversion.attributeToAttribute( { model: 'headingColumns', view: 'headingColumns', priority: 'high' } );
  538. setModelData( model, modelTable( [ [ '11' ] ] ) );
  539. const table = root.getChild( 0 );
  540. model.change( writer => {
  541. writer.setAttribute( 'headingColumns', 1, table );
  542. } );
  543. expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
  544. '<table headingColumns="1"><tbody><tr><td>11</td></tr></tbody></table>'
  545. );
  546. } );
  547. it( 'should work with adding table cells', () => {
  548. setModelData( model, modelTable( [
  549. [ { rowspan: 2, contents: '11' }, '12', '13', '14' ],
  550. [ '22', '23', '24' ],
  551. [ { colspan: 2, contents: '31' }, '33', '34' ]
  552. ], { headingColumns: 2 } ) );
  553. const table = root.getChild( 0 );
  554. model.change( writer => {
  555. // Inserting column in heading columns so update table's attribute also
  556. writer.setAttribute( 'headingColumns', 3, table );
  557. writer.insertElement( 'tableCell', table.getChild( 0 ), 2 );
  558. writer.insertElement( 'tableCell', table.getChild( 1 ), 1 );
  559. writer.insertElement( 'tableCell', table.getChild( 2 ), 1 );
  560. } );
  561. expect( formatModelTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
  562. [
  563. { isHeading: true, rowspan: 2, contents: '11' },
  564. { isHeading: true, contents: '12' },
  565. { isHeading: true, contents: '' },
  566. '13',
  567. '14'
  568. ],
  569. [
  570. { isHeading: true, contents: '22' },
  571. { isHeading: true, contents: '' },
  572. '23',
  573. '24'
  574. ],
  575. [
  576. { isHeading: true, colspan: 2, contents: '31' },
  577. { isHeading: true, contents: '' },
  578. '33',
  579. '34'
  580. ]
  581. ] ) );
  582. } );
  583. } );
  584. } );