downcast.js 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315
  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 VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  6. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  7. import TableEditing from '../../src/tableediting';
  8. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  9. import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
  10. import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  11. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  12. import { modelTable, viewTable } from '../_utils/utils';
  13. describe( 'downcast converters', () => {
  14. let editor, model, doc, root, view;
  15. testUtils.createSinonSandbox();
  16. beforeEach( async () => {
  17. editor = await VirtualTestEditor.create( { plugins: [ Paragraph, TableEditing ] } );
  18. model = editor.model;
  19. root = model.document.getRoot( 'main' );
  20. view = editor.editing.view;
  21. } );
  22. afterEach( () => {
  23. return editor.destroy();
  24. } );
  25. describe( 'downcastInsertTable()', () => {
  26. beforeEach( async () => {
  27. editor = await VirtualTestEditor.create( { plugins: [ Paragraph, TableEditing ] } );
  28. model = editor.model;
  29. doc = model.document;
  30. root = doc.getRoot( 'main' );
  31. view = editor.editing.view;
  32. } );
  33. describe( 'editing pipeline', () => {
  34. it( 'should create table as a widget', () => {
  35. setModelData( model, modelTable( [ [ '' ] ] ) );
  36. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  37. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  38. '<div class="ck ck-widget__selection-handle"></div>' +
  39. '<table>' +
  40. '<tbody>' +
  41. '<tr>' +
  42. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  43. '<span style="display:inline-block"></span>' +
  44. '</td>' +
  45. '</tr>' +
  46. '</tbody>' +
  47. '</table>' +
  48. '</figure>'
  49. );
  50. } );
  51. } );
  52. describe( 'data pipeline', () => {
  53. it( 'should create table with tbody and thead', () => {
  54. setModelData( model, modelTable( [
  55. [ '00' ],
  56. [ '10' ]
  57. ], { headingRows: 1 } ) );
  58. assertEqualMarkup( editor.getData(),
  59. '<figure class="table">' +
  60. '<table>' +
  61. '<thead>' +
  62. '<tr><th>00</th></tr>' +
  63. '</thead>' +
  64. '<tbody>' +
  65. '<tr><td>10</td></tr>' +
  66. '</tbody>' +
  67. '</table>' +
  68. '</figure>'
  69. );
  70. } );
  71. it( 'should create table with thead', () => {
  72. setModelData( model, modelTable( [
  73. [ '00' ],
  74. [ '10' ]
  75. ], { headingRows: 2 } ) );
  76. assertEqualMarkup( editor.getData(),
  77. '<figure class="table">' +
  78. '<table>' +
  79. '<thead>' +
  80. '<tr><th>00</th></tr>' +
  81. '<tr><th>10</th></tr>' +
  82. '</thead>' +
  83. '</table>' +
  84. '</figure>'
  85. );
  86. } );
  87. it( 'should create table with heading columns and rows', () => {
  88. setModelData( model, modelTable( [
  89. [ '00', '01', '02', '03' ],
  90. [ '10', '11', '12', '13' ]
  91. ], { headingColumns: 3, headingRows: 1 } ) );
  92. assertEqualMarkup( editor.getData(),
  93. '<figure class="table">' +
  94. '<table>' +
  95. '<thead>' +
  96. '<tr><th>00</th><th>01</th><th>02</th><th>03</th></tr>' +
  97. '</thead>' +
  98. '<tbody>' +
  99. '<tr><th>10</th><th>11</th><th>12</th><td>13</td></tr>' +
  100. '</tbody>' +
  101. '</table>' +
  102. '</figure>'
  103. );
  104. } );
  105. it( 'should create table with block content', () => {
  106. setModelData( model, modelTable( [
  107. [ '<paragraph>00</paragraph><paragraph>foo</paragraph>', '01' ]
  108. ] ) );
  109. assertEqualMarkup( editor.getData(),
  110. '<figure class="table">' +
  111. '<table>' +
  112. '<tbody>' +
  113. '<tr>' +
  114. '<td><p>00</p><p>foo</p></td>' +
  115. '<td>01</td>' +
  116. '</tr>' +
  117. '</tbody>' +
  118. '</table>' +
  119. '</figure>'
  120. );
  121. } );
  122. it( 'should create table with block content (attribute on paragraph)', () => {
  123. editor.conversion.attributeToAttribute(
  124. {
  125. model: { key: 'alignment', values: [ 'right', 'center', 'justify' ] },
  126. view: {
  127. right: { key: 'style', value: { 'text-align': 'right' } },
  128. center: { key: 'style', value: { 'text-align': 'center' } },
  129. justify: { key: 'style', value: { 'text-align': 'justify' } }
  130. }
  131. }
  132. );
  133. setModelData( model, modelTable( [
  134. [ '<paragraph alignment="right">00</paragraph>' ]
  135. ] ) );
  136. assertEqualMarkup( editor.getData(),
  137. '<figure class="table">' +
  138. '<table>' +
  139. '<tbody>' +
  140. '<tr>' +
  141. '<td><p style="text-align:right;">00</p></td>' +
  142. '</tr>' +
  143. '</tbody>' +
  144. '</table>' +
  145. '</figure>'
  146. );
  147. } );
  148. it( 'should be possible to overwrite', () => {
  149. editor.conversion.elementToElement( { model: 'tableRow', view: 'tr', converterPriority: 'high' } );
  150. editor.conversion.elementToElement( { model: 'tableCell', view: 'td', converterPriority: 'high' } );
  151. editor.conversion.for( 'downcast' ).add( dispatcher => {
  152. dispatcher.on( 'insert:table', ( evt, data, conversionApi ) => {
  153. conversionApi.consumable.consume( data.item, 'insert' );
  154. const tableElement = conversionApi.writer.createContainerElement( 'table', { foo: 'bar' } );
  155. const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
  156. conversionApi.mapper.bindElements( data.item, tableElement );
  157. conversionApi.writer.insert( viewPosition, tableElement );
  158. }, { priority: 'high' } );
  159. } );
  160. setModelData( model, modelTable( [ [ '' ] ] ) );
  161. assertEqualMarkup( editor.getData(),
  162. '<table foo="bar">' +
  163. '<tr><td><p>&nbsp;</p></td></tr>' +
  164. '</table>'
  165. );
  166. } );
  167. it( 'should re-create table on reinsert', () => {
  168. model.schema.register( 'wrapper', {
  169. allowWhere: '$block',
  170. allowContentOf: '$root'
  171. } );
  172. editor.conversion.elementToElement( { model: 'wrapper', view: 'div' } );
  173. setModelData( model, modelTable( [ [ '[]' ] ] ) );
  174. assertEqualMarkup( editor.getData(),
  175. '<figure class="table">' +
  176. '<table>' +
  177. '<tbody>' +
  178. '<tr><td>&nbsp;</td></tr>' +
  179. '</tbody>' +
  180. '</table>' +
  181. '</figure>'
  182. );
  183. model.change( writer => {
  184. const table = model.document.getRoot().getChild( 0 );
  185. const range = writer.createRange( writer.createPositionBefore( table ), writer.createPositionAfter( table ) );
  186. const wrapper = writer.createElement( 'wrapper' );
  187. writer.wrap( range, wrapper );
  188. } );
  189. assertEqualMarkup( editor.getData(),
  190. '<div>' +
  191. '<figure class="table">' +
  192. '<table>' +
  193. '<tbody>' +
  194. '<tr><td>&nbsp;</td></tr>' +
  195. '</tbody>' +
  196. '</table>' +
  197. '</figure>' +
  198. '</div>'
  199. );
  200. } );
  201. describe( 'headingColumns attribute', () => {
  202. it( 'should mark heading columns table cells', () => {
  203. setModelData( model, modelTable( [
  204. [ '00', '01', '02' ],
  205. [ '10', '11', '12' ]
  206. ], { headingColumns: 2 } ) );
  207. assertEqualMarkup( editor.getData(),
  208. '<figure class="table">' +
  209. '<table>' +
  210. '<tbody>' +
  211. '<tr><th>00</th><th>01</th><td>02</td></tr>' +
  212. '<tr><th>10</th><th>11</th><td>12</td></tr>' +
  213. '</tbody>' +
  214. '</table>' +
  215. '</figure>'
  216. );
  217. } );
  218. it( 'should mark heading columns table cells when one has colspan attribute', () => {
  219. setModelData( model, modelTable( [
  220. [ '00', '01', '02', '03' ],
  221. [ { colspan: 2, contents: '10' }, '12', '13' ]
  222. ], { headingColumns: 3 } ) );
  223. assertEqualMarkup( editor.getData(),
  224. '<figure class="table">' +
  225. '<table>' +
  226. '<tbody>' +
  227. '<tr><th>00</th><th>01</th><th>02</th><td>03</td></tr>' +
  228. '<tr><th colspan="2">10</th><th>12</th><td>13</td></tr>' +
  229. '</tbody>' +
  230. '</table>' +
  231. '</figure>'
  232. );
  233. } );
  234. it( 'should work with colspan and rowspan attributes on table cells', () => {
  235. // The table in this test looks like a table below:
  236. //
  237. // Row headings | Normal cells
  238. // |
  239. // +----+----+----+----+
  240. // | 00 | 01 | 02 | 03 |
  241. // | +----+ +----+
  242. // | | 11 | | 13 |
  243. // |----+----+ +----+
  244. // | 20 | | 23 |
  245. // | +----+----+
  246. // | | 32 | 33 |
  247. // +----+----+----+----+
  248. setModelData( model, modelTable( [
  249. [ { rowspan: 2, contents: '00' }, '01', { rowspan: 3, contents: '02' }, '03' ],
  250. [ '11', '13' ],
  251. [ { colspan: 2, rowspan: 2, contents: '20' }, '23' ],
  252. [ '32', '33' ]
  253. ], { headingColumns: 3 } ) );
  254. assertEqualMarkup( editor.getData(),
  255. '<figure class="table">' +
  256. '<table>' +
  257. '<tbody>' +
  258. '<tr><th rowspan="2">00</th><th>01</th><th rowspan="3">02</th><td>03</td></tr>' +
  259. '<tr><th>11</th><td>13</td></tr>' +
  260. '<tr><th rowspan="2" colspan="2">20</th><td>23</td></tr>' +
  261. '<tr><th>32</th><td>33</td></tr>' +
  262. '</tbody>' +
  263. '</table>' +
  264. '</figure>'
  265. );
  266. } );
  267. } );
  268. it( 'should create table with tbody', () => {
  269. setModelData( model, modelTable( [ [ '' ] ] ) );
  270. assertEqualMarkup( editor.getData(),
  271. '<figure class="table">' +
  272. '<table>' +
  273. '<tbody>' +
  274. '<tr><td>&nbsp;</td></tr>' +
  275. '</tbody>' +
  276. '</table>' +
  277. '</figure>'
  278. );
  279. } );
  280. } );
  281. } );
  282. describe( 'downcastInsertRow()', () => {
  283. // The insert row downcast conversion is not executed in data pipeline.
  284. describe( 'editing pipeline', () => {
  285. it( 'should react to changed rows', () => {
  286. setModelData( model, modelTable( [
  287. [ '00', '01' ]
  288. ] ) );
  289. const table = root.getChild( 0 );
  290. model.change( writer => {
  291. const row = writer.createElement( 'tableRow' );
  292. writer.insert( row, table, 1 );
  293. writer.insertElement( 'tableCell', row, 'end' );
  294. writer.insertElement( 'tableCell', row, 'end' );
  295. } );
  296. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  297. [ '00', '01' ],
  298. [ '', '' ]
  299. ], { asWidget: true } ) );
  300. } );
  301. it( 'should properly consume already added rows', () => {
  302. setModelData( model, modelTable( [
  303. [ '00', '01' ]
  304. ] ) );
  305. const table = root.getChild( 0 );
  306. model.change( writer => {
  307. const row = writer.createElement( 'tableRow' );
  308. writer.insert( row, table, 1 );
  309. writer.insertElement( 'tableCell', row, 'end' );
  310. writer.insertElement( 'tableCell', row, 'end' );
  311. } );
  312. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  313. [ '00', '01' ],
  314. [ '', '' ]
  315. ], { asWidget: true } ) );
  316. model.change( writer => {
  317. const row = writer.createElement( 'tableRow' );
  318. writer.insert( row, table, 2 );
  319. writer.insertElement( 'tableCell', row, 'end' );
  320. writer.insertElement( 'tableCell', row, 'end' );
  321. } );
  322. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  323. [ '00', '01' ],
  324. [ '', '' ],
  325. [ '', '' ]
  326. ], { asWidget: true } ) );
  327. } );
  328. it( 'should insert row on proper index', () => {
  329. setModelData( model, modelTable( [
  330. [ '00', '01' ],
  331. [ '21', '22' ],
  332. [ '31', '32' ]
  333. ] ) );
  334. const table = root.getChild( 0 );
  335. model.change( writer => {
  336. const row = writer.createElement( 'tableRow' );
  337. writer.insert( row, table, 1 );
  338. writer.insertElement( 'tableCell', row, 'end' );
  339. writer.insertElement( 'tableCell', row, 'end' );
  340. } );
  341. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  342. [ '00', '01' ],
  343. [ '', '' ],
  344. [ '21', '22' ],
  345. [ '31', '32' ]
  346. ], { asWidget: true } ) );
  347. } );
  348. it( 'should insert row on proper index when table has heading rows defined - insert in body', () => {
  349. setModelData( model, modelTable( [
  350. [ '00', '01' ],
  351. [ '21', '22' ],
  352. [ '31', '32' ]
  353. ], { headingRows: 1, asWidget: true } ) );
  354. const table = root.getChild( 0 );
  355. model.change( writer => {
  356. const row = writer.createElement( 'tableRow' );
  357. writer.insert( row, table, 1 );
  358. writer.insertElement( 'tableCell', row, 'end' );
  359. writer.insertElement( 'tableCell', row, 'end' );
  360. } );
  361. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  362. [ '00', '01' ],
  363. [ '', '' ],
  364. [ '21', '22' ],
  365. [ '31', '32' ]
  366. ], { headingRows: 1, asWidget: true } ) );
  367. } );
  368. it( 'should insert row on proper index when table has heading rows defined - insert in heading', () => {
  369. setModelData( model, modelTable( [
  370. [ '00', '01' ],
  371. [ '21', '22' ],
  372. [ '31', '32' ]
  373. ], { headingRows: 2 } ) );
  374. const table = root.getChild( 0 );
  375. model.change( writer => {
  376. const row = writer.createElement( 'tableRow' );
  377. writer.insert( row, table, 1 );
  378. writer.insertElement( 'tableCell', row, 'end' );
  379. writer.insertElement( 'tableCell', row, 'end' );
  380. } );
  381. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  382. [ '00', '01' ],
  383. [ '', '' ],
  384. [ '21', '22' ],
  385. [ '31', '32' ]
  386. ], { headingRows: 3, asWidget: true } ) );
  387. } );
  388. it( 'should react to changed rows when previous rows\' cells has rowspans', () => {
  389. setModelData( model, modelTable( [
  390. [ { rowspan: 2, contents: '00' }, '01' ],
  391. [ '22' ]
  392. ] ) );
  393. const table = root.getChild( 0 );
  394. model.change( writer => {
  395. const row = writer.createElement( 'tableRow' );
  396. writer.insert( row, table, 2 );
  397. writer.insertElement( 'tableCell', row, 'end' );
  398. writer.insertElement( 'tableCell', row, 'end' );
  399. } );
  400. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  401. [ { rowspan: 2, contents: '00' }, '01' ],
  402. [ '22' ],
  403. [ '', '' ]
  404. ], { asWidget: true } ) );
  405. } );
  406. it( 'should properly create row headings', () => {
  407. setModelData( model, modelTable( [
  408. [ { rowspan: 2, contents: '00' }, '01' ],
  409. [ '22' ]
  410. ], { headingColumns: 1 } ) );
  411. const table = root.getChild( 0 );
  412. model.change( writer => {
  413. const firstRow = writer.createElement( 'tableRow' );
  414. writer.insert( firstRow, table, 2 );
  415. writer.insert( writer.createElement( 'tableCell' ), firstRow, 'end' );
  416. const secondRow = writer.createElement( 'tableRow' );
  417. writer.insert( secondRow, table, 3 );
  418. writer.insert( writer.createElement( 'tableCell' ), secondRow, 'end' );
  419. writer.insert( writer.createElement( 'tableCell' ), secondRow, 'end' );
  420. } );
  421. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  422. [ { rowspan: 2, contents: '00', isHeading: true }, '01' ],
  423. [ '22' ],
  424. [ { contents: '', isHeading: true }, '' ],
  425. [ { contents: '', isHeading: true }, '' ]
  426. ], { asWidget: true } ) );
  427. } );
  428. it( 'should create table cell inside inserted row as a widget', () => {
  429. setModelData( model, modelTable( [ [ '00' ] ] ) );
  430. const table = root.getChild( 0 );
  431. model.change( writer => {
  432. const firstRow = writer.createElement( 'tableRow' );
  433. writer.insert( firstRow, table, 1 );
  434. writer.insert( writer.createElement( 'tableCell' ), firstRow, 'end' );
  435. } );
  436. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  437. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  438. '<div class="ck ck-widget__selection-handle"></div>' +
  439. '<table>' +
  440. '<tbody>' +
  441. '<tr>' +
  442. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  443. '<span style="display:inline-block">00</span>' +
  444. '</td>' +
  445. '</tr>' +
  446. '<tr>' +
  447. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  448. '<span style="display:inline-block"></span>' +
  449. '</td>' +
  450. '</tr>' +
  451. '</tbody>' +
  452. '</table>' +
  453. '</figure>'
  454. );
  455. } );
  456. } );
  457. } );
  458. describe( 'downcastInsertCell()', () => {
  459. // The insert table cell downcast conversion is not executed in data pipeline.
  460. describe( 'editing pipeline', () => {
  461. it( 'should add tableCell on proper index in tr', () => {
  462. setModelData( model, modelTable( [
  463. [ '00', '01' ]
  464. ] ) );
  465. const table = root.getChild( 0 );
  466. model.change( writer => {
  467. const row = table.getChild( 0 );
  468. writer.insertElement( 'tableCell', row, 1 );
  469. } );
  470. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  471. [ '00', '', '01' ]
  472. ], { asWidget: true } ) );
  473. } );
  474. it( 'should add tableCell on proper index in tr when previous have colspans', () => {
  475. setModelData( model, modelTable( [
  476. [ { colspan: 2, contents: '00' }, '13' ]
  477. ] ) );
  478. const table = root.getChild( 0 );
  479. model.change( writer => {
  480. const row = table.getChild( 0 );
  481. writer.insertElement( 'tableCell', row, 1 );
  482. } );
  483. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  484. [ { colspan: 2, contents: '00' }, '', '13' ]
  485. ], { asWidget: true } ) );
  486. } );
  487. it( 'should add tableCell on proper index in tr when previous row have rowspans', () => {
  488. setModelData( model, modelTable( [
  489. [ { rowspan: 2, contents: '00' }, '01', '02' ],
  490. [ '11', '12' ]
  491. ] ) );
  492. const table = root.getChild( 0 );
  493. model.change( writer => {
  494. writer.insertElement( 'tableCell', table.getChild( 0 ), 1 );
  495. writer.insertElement( 'tableCell', table.getChild( 1 ), 0 );
  496. } );
  497. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  498. [ { rowspan: 2, contents: '00' }, '', '01', '02' ],
  499. [ '', '11', '12' ]
  500. ], { asWidget: true } ) );
  501. } );
  502. it( 'split cell simulation - simple', () => {
  503. setModelData( model, modelTable( [
  504. [ '00', '01' ],
  505. [ '10', '11' ]
  506. ] ) );
  507. const table = root.getChild( 0 );
  508. model.change( writer => {
  509. const firstRow = table.getChild( 0 );
  510. const secondRow = table.getChild( 1 );
  511. writer.insertElement( 'tableCell', firstRow, 1 );
  512. writer.setAttribute( 'colspan', 2, secondRow.getChild( 0 ) );
  513. } );
  514. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  515. [ '00', '', '01' ],
  516. [ { colspan: 2, contents: '10' }, '11' ]
  517. ], { asWidget: true } ) );
  518. } );
  519. it( 'merge simulation - simple', () => {
  520. setModelData( model, modelTable( [
  521. [ '00', '01' ],
  522. [ '10', '11' ]
  523. ] ) );
  524. const table = root.getChild( 0 );
  525. model.change( writer => {
  526. const firstRow = table.getChild( 0 );
  527. writer.setAttribute( 'colspan', 2, firstRow.getChild( 0 ) );
  528. writer.remove( firstRow.getChild( 1 ) );
  529. } );
  530. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  531. [ { colspan: 2, contents: '00' } ],
  532. [ '10', '11' ]
  533. ], { asWidget: true } ) );
  534. } );
  535. it( 'should create inserted table cell as a widget', () => {
  536. setModelData( model, modelTable( [ [ '00' ] ] ) );
  537. const table = root.getChild( 0 );
  538. model.change( writer => {
  539. const row = table.getChild( 0 );
  540. writer.insert( writer.createElement( 'tableCell' ), row, 'end' );
  541. } );
  542. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  543. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  544. '<div class="ck ck-widget__selection-handle"></div>' +
  545. '<table>' +
  546. '<tbody>' +
  547. '<tr>' +
  548. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  549. '<span style="display:inline-block">00</span>' +
  550. '</td>' +
  551. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  552. '<span style="display:inline-block"></span>' +
  553. '</td>' +
  554. '</tr>' +
  555. '</tbody>' +
  556. '</table>' +
  557. '</figure>'
  558. );
  559. } );
  560. } );
  561. } );
  562. describe( 'downcastTableHeadingColumnsChange()', () => {
  563. beforeEach( () => {
  564. return VirtualTestEditor.create( { plugins: [ Paragraph, TableEditing ] } )
  565. .then( newEditor => {
  566. editor = newEditor;
  567. model = editor.model;
  568. doc = model.document;
  569. root = doc.getRoot( 'main' );
  570. view = editor.editing.view;
  571. } );
  572. } );
  573. // The heading columns change downcast conversion is not executed in data pipeline.
  574. describe( 'editing pipeline', () => {
  575. it( 'should work for adding heading columns', () => {
  576. setModelData( model, modelTable( [
  577. [ '00', '01' ],
  578. [ '10', '11' ]
  579. ] ) );
  580. const table = root.getChild( 0 );
  581. model.change( writer => {
  582. writer.setAttribute( 'headingColumns', 1, table );
  583. } );
  584. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  585. [ { isHeading: true, contents: '00' }, '01' ],
  586. [ { isHeading: true, contents: '10' }, '11' ]
  587. ], { headingColumns: 1, asWidget: true } ) );
  588. } );
  589. it( 'should work for changing heading columns to a bigger number', () => {
  590. setModelData( model, modelTable( [
  591. [ '00', '01', '02', '03' ],
  592. [ '10', '11', '12', '13' ]
  593. ], { headingColumns: 1 } ) );
  594. const table = root.getChild( 0 );
  595. model.change( writer => {
  596. writer.setAttribute( 'headingColumns', 3, table );
  597. } );
  598. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  599. [ { isHeading: true, contents: '00' }, { isHeading: true, contents: '01' }, { isHeading: true, contents: '02' }, '03' ],
  600. [ { isHeading: true, contents: '10' }, { isHeading: true, contents: '11' }, { isHeading: true, contents: '12' }, '13' ]
  601. ], { asWidget: true } ) );
  602. } );
  603. it( 'should work for changing heading columns to a smaller number', () => {
  604. setModelData( model, modelTable( [
  605. [ { isHeading: true, contents: '00' }, { isHeading: true, contents: '01' }, { isHeading: true, contents: '02' }, '03' ],
  606. [ { isHeading: true, contents: '10' }, { isHeading: true, contents: '11' }, { isHeading: true, contents: '12' }, '13' ]
  607. ], { headingColumns: 3 } ) );
  608. const table = root.getChild( 0 );
  609. model.change( writer => {
  610. writer.setAttribute( 'headingColumns', 1, table );
  611. } );
  612. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  613. [ { isHeading: true, contents: '00' }, '01', '02', '03' ],
  614. [ { isHeading: true, contents: '10' }, '11', '12', '13' ]
  615. ], { headingColumns: 3, asWidget: true } ) );
  616. } );
  617. it( 'should work for removing heading columns', () => {
  618. setModelData( model, modelTable( [
  619. [ '00', '01' ],
  620. [ '10', '11' ]
  621. ], { headingColumns: 1 } ) );
  622. const table = root.getChild( 0 );
  623. model.change( writer => {
  624. writer.removeAttribute( 'headingColumns', table );
  625. } );
  626. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  627. [ '00', '01' ],
  628. [ '10', '11' ]
  629. ], { asWidget: true } ) );
  630. } );
  631. it( 'should be possible to overwrite', () => {
  632. editor.conversion.attributeToAttribute( { model: 'headingColumns', view: 'headingColumns', converterPriority: 'high' } );
  633. setModelData( model, modelTable( [ [ '00[] ' ] ] ) );
  634. const table = root.getChild( 0 );
  635. model.change( writer => {
  636. writer.setAttribute( 'headingColumns', 1, table );
  637. } );
  638. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  639. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false" headingColumns="1">' +
  640. '<div class="ck ck-widget__selection-handle"></div>' +
  641. '<table>' +
  642. '<tbody>' +
  643. '<tr>' +
  644. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  645. '<span style="display:inline-block">00</span>' +
  646. '</td>' +
  647. '</tr>' +
  648. '</tbody>' +
  649. '</table>' +
  650. '</figure>'
  651. );
  652. } );
  653. it( 'should work with adding table cells', () => {
  654. setModelData( model, modelTable( [
  655. [ { rowspan: 2, contents: '00' }, '01', '13', '14' ],
  656. [ '11', '12', '13' ],
  657. [ { colspan: 2, contents: '20' }, '22', '23' ]
  658. ], { headingColumns: 2 } ) );
  659. const table = root.getChild( 0 );
  660. model.change( writer => {
  661. // Inserting column in heading columns so update table's attribute also
  662. writer.setAttribute( 'headingColumns', 3, table );
  663. writer.insertElement( 'tableCell', table.getChild( 0 ), 2 );
  664. writer.insertElement( 'tableCell', table.getChild( 1 ), 1 );
  665. writer.insertElement( 'tableCell', table.getChild( 2 ), 1 );
  666. } );
  667. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  668. [
  669. { isHeading: true, rowspan: 2, contents: '00' },
  670. { isHeading: true, contents: '01' },
  671. { isHeading: true, contents: '' },
  672. '13',
  673. '14'
  674. ],
  675. [
  676. { isHeading: true, contents: '11' },
  677. { isHeading: true, contents: '' },
  678. '12',
  679. '13'
  680. ],
  681. [
  682. { isHeading: true, colspan: 2, contents: '20' },
  683. { isHeading: true, contents: '' },
  684. '22',
  685. '23'
  686. ]
  687. ], { asWidget: true } ) );
  688. } );
  689. it( 'should create renamed cell as a widget', () => {
  690. setModelData( model, modelTable( [ [ '00' ] ] ) );
  691. const table = root.getChild( 0 );
  692. model.change( writer => {
  693. writer.setAttribute( 'headingRows', 1, table );
  694. } );
  695. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  696. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  697. '<div class="ck ck-widget__selection-handle"></div>' +
  698. '<table>' +
  699. '<thead>' +
  700. '<tr>' +
  701. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  702. '<span style="display:inline-block">00</span>' +
  703. '</th>' +
  704. '</tr>' +
  705. '</thead>' +
  706. '</table>' +
  707. '</figure>'
  708. );
  709. } );
  710. } );
  711. } );
  712. describe( 'downcastTableHeadingRowsChange()', () => {
  713. // The heading rows change downcast conversion is not executed in data pipeline.
  714. describe( 'editing pipeline', () => {
  715. it( 'should work for adding heading rows', () => {
  716. setModelData( model, modelTable( [
  717. [ '00', '01' ],
  718. [ '10', '11' ],
  719. [ '20', '21' ]
  720. ] ) );
  721. const table = root.getChild( 0 );
  722. model.change( writer => {
  723. writer.setAttribute( 'headingRows', 2, table );
  724. } );
  725. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  726. [ '00', '01' ],
  727. [ '10', '11' ],
  728. [ '20', '21' ]
  729. ], { headingRows: 2, asWidget: true } ) );
  730. } );
  731. it( 'should work for changing number of heading rows to a bigger number', () => {
  732. setModelData( model, modelTable( [
  733. [ '00', '01' ],
  734. [ '10', '11' ],
  735. [ '20', '21' ]
  736. ], { headingRows: 1 } ) );
  737. const table = root.getChild( 0 );
  738. model.change( writer => {
  739. writer.setAttribute( 'headingRows', 2, table );
  740. } );
  741. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  742. [ '00', '01' ],
  743. [ '10', '11' ],
  744. [ '20', '21' ]
  745. ], { headingRows: 2, asWidget: true } ) );
  746. } );
  747. it( 'should work for changing number of heading rows to a smaller number', () => {
  748. setModelData( model, modelTable( [
  749. [ '00', '01' ],
  750. [ '10', '11' ],
  751. [ '20', '21' ],
  752. [ '30', '31' ]
  753. ], { headingRows: 3 } ) );
  754. const table = root.getChild( 0 );
  755. model.change( writer => {
  756. writer.setAttribute( 'headingRows', 2, table );
  757. } );
  758. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  759. [ '00', '01' ],
  760. [ '10', '11' ],
  761. [ '20', '21' ],
  762. [ '30', '31' ]
  763. ], { headingRows: 2, asWidget: true } ) );
  764. } );
  765. it( 'should work for removing heading rows', () => {
  766. setModelData( model, modelTable( [
  767. [ '00', '01' ],
  768. [ '10', '11' ]
  769. ], { headingRows: 2 } ) );
  770. const table = root.getChild( 0 );
  771. model.change( writer => {
  772. writer.removeAttribute( 'headingRows', table );
  773. } );
  774. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  775. [ '00', '01' ],
  776. [ '10', '11' ]
  777. ], { asWidget: true } ) );
  778. } );
  779. it( 'should work for making heading rows without tbody', () => {
  780. setModelData( model, modelTable( [
  781. [ '00', '01' ],
  782. [ '10', '11' ]
  783. ] ) );
  784. const table = root.getChild( 0 );
  785. model.change( writer => {
  786. writer.setAttribute( 'headingRows', 2, table );
  787. } );
  788. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  789. [ '00', '01' ],
  790. [ '10', '11' ]
  791. ], { headingRows: 2, asWidget: true } ) );
  792. } );
  793. it( 'should be possible to overwrite', () => {
  794. editor.conversion.attributeToAttribute( {
  795. model: 'headingRows',
  796. view: 'headingRows',
  797. converterPriority: 'high'
  798. } );
  799. setModelData( model, modelTable( [ [ '00' ] ] ) );
  800. const table = root.getChild( 0 );
  801. model.change( writer => {
  802. writer.setAttribute( 'headingRows', 1, table );
  803. } );
  804. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  805. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false" headingRows="1">' +
  806. '<div class="ck ck-widget__selection-handle"></div>' +
  807. '<table>' +
  808. '<tbody>' +
  809. '<tr>' +
  810. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  811. '<span style="display:inline-block">00</span>' +
  812. '</td>' +
  813. '</tr>' +
  814. '</tbody>' +
  815. '</table>' +
  816. '</figure>'
  817. );
  818. } );
  819. it( 'should work with adding table rows at the beginning of a table', () => {
  820. setModelData( model, modelTable( [
  821. [ '00', '01' ],
  822. [ '10', '11' ]
  823. ], { headingRows: 1 } ) );
  824. const table = root.getChild( 0 );
  825. model.change( writer => {
  826. writer.setAttribute( 'headingRows', 2, table );
  827. const tableRow = writer.createElement( 'tableRow' );
  828. writer.insert( tableRow, table, 0 );
  829. writer.insertElement( 'tableCell', tableRow, 'end' );
  830. writer.insertElement( 'tableCell', tableRow, 'end' );
  831. } );
  832. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  833. [ '', '' ],
  834. [ '00', '01' ],
  835. [ '10', '11' ]
  836. ], { headingRows: 2, asWidget: true } ) );
  837. } );
  838. it( 'should work with adding a table row and expanding heading', () => {
  839. setModelData( model, modelTable( [
  840. [ '00', '01' ],
  841. [ '10', '11' ],
  842. [ '20', '21' ]
  843. ], { headingRows: 1 } ) );
  844. const table = root.getChild( 0 );
  845. model.change( writer => {
  846. writer.setAttribute( 'headingRows', 2, table );
  847. const tableRow = writer.createElement( 'tableRow' );
  848. writer.insert( tableRow, table, 1 );
  849. writer.insertElement( 'tableCell', tableRow, 'end' );
  850. writer.insertElement( 'tableCell', tableRow, 'end' );
  851. } );
  852. assertEqualMarkup( getViewData( view, { withoutSelection: true } ), viewTable( [
  853. [ '00', '01' ],
  854. [ '', '' ],
  855. [ '10', '11' ],
  856. [ '20', '21' ]
  857. ], { headingRows: 2, asWidget: true } ) );
  858. } );
  859. it( 'should create renamed cell as a widget', () => {
  860. setModelData( model, modelTable( [ [ '00' ] ] ) );
  861. const table = root.getChild( 0 );
  862. model.change( writer => {
  863. writer.setAttribute( 'headingColumns', 1, table );
  864. } );
  865. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  866. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  867. '<div class="ck ck-widget__selection-handle"></div>' +
  868. '<table>' +
  869. '<tbody>' +
  870. '<tr>' +
  871. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  872. '<span style="display:inline-block">00</span>' +
  873. '</th>' +
  874. '</tr>' +
  875. '</tbody>' +
  876. '</table>' +
  877. '</figure>'
  878. );
  879. } );
  880. } );
  881. } );
  882. describe( 'downcastRemoveRow()', () => {
  883. // The remove row downcast conversion is not executed in data pipeline.
  884. describe( 'editing pipeline', () => {
  885. it( 'should react to removed row from the beginning of a body rows (no heading rows)', () => {
  886. setModelData( model, modelTable( [
  887. [ '00[]', '01' ],
  888. [ '10', '11' ]
  889. ] ) );
  890. const table = root.getChild( 0 );
  891. model.change( writer => {
  892. writer.remove( table.getChild( 1 ) );
  893. } );
  894. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  895. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  896. '<div class="ck ck-widget__selection-handle"></div>' +
  897. '<table>' +
  898. '<tbody>' +
  899. '<tr>' +
  900. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  901. '<span style="display:inline-block">00</span>' +
  902. '</td>' +
  903. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  904. '<span style="display:inline-block">01</span>' +
  905. '</td>' +
  906. '</tr>' +
  907. '</tbody>' +
  908. '</table>' +
  909. '</figure>'
  910. );
  911. } );
  912. it( 'should react to removed row form the end of a body rows (no heading rows)', () => {
  913. setModelData( model, modelTable( [
  914. [ '00[]', '01' ],
  915. [ '10', '11' ]
  916. ] ) );
  917. const table = root.getChild( 0 );
  918. model.change( writer => {
  919. writer.remove( table.getChild( 0 ) );
  920. } );
  921. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  922. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  923. '<div class="ck ck-widget__selection-handle"></div>' +
  924. '<table>' +
  925. '<tbody>' +
  926. '<tr>' +
  927. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  928. '<span style="display:inline-block">10</span>' +
  929. '</td>' +
  930. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  931. '<span style="display:inline-block">11</span>' +
  932. '</td>' +
  933. '</tr>' +
  934. '</tbody>' +
  935. '</table>' +
  936. '</figure>'
  937. );
  938. } );
  939. it( 'should react to removed row from the beginning of a heading rows (no body rows)', () => {
  940. setModelData( model, modelTable( [
  941. [ '00[]', '01' ],
  942. [ '10', '11' ]
  943. ], { headingRows: 2 } ) );
  944. const table = root.getChild( 0 );
  945. model.change( writer => {
  946. // Removing row from a heading section changes requires changing heading rows attribute.
  947. writer.setAttribute( 'headingRows', 1, table );
  948. writer.remove( table.getChild( 0 ) );
  949. } );
  950. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  951. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  952. '<div class="ck ck-widget__selection-handle"></div>' +
  953. '<table>' +
  954. '<thead>' +
  955. '<tr>' +
  956. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  957. '<span style="display:inline-block">10</span>' +
  958. '</th>' +
  959. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  960. '<span style="display:inline-block">11</span>' +
  961. '</th>' +
  962. '</tr>' +
  963. '</thead>' +
  964. '</table>' +
  965. '</figure>'
  966. );
  967. } );
  968. it( 'should react to removed row form the end of a heading rows (no body rows)', () => {
  969. setModelData( model, modelTable( [
  970. [ '00[]', '01' ],
  971. [ '10', '11' ]
  972. ], { headingRows: 2 } ) );
  973. const table = root.getChild( 0 );
  974. model.change( writer => {
  975. // Removing row from a heading section changes requires changing heading rows attribute.
  976. writer.setAttribute( 'headingRows', 1, table );
  977. writer.remove( table.getChild( 1 ) );
  978. } );
  979. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  980. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  981. '<div class="ck ck-widget__selection-handle"></div>' +
  982. '<table>' +
  983. '<thead>' +
  984. '<tr>' +
  985. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  986. '<span style="display:inline-block">00</span>' +
  987. '</th>' +
  988. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  989. '<span style="display:inline-block">01</span>' +
  990. '</th>' +
  991. '</tr>' +
  992. '</thead>' +
  993. '</table>' +
  994. '</figure>'
  995. );
  996. } );
  997. it( 'should react to removed row form the end of a heading rows (first cell in body has colspan)', () => {
  998. setModelData( model, modelTable( [
  999. [ '00[]', '01', '02', '03' ],
  1000. [ { rowspan: 2, colspan: 2, contents: '10' }, '12', '13' ],
  1001. [ '22', '23' ]
  1002. ], { headingRows: 1 } ) );
  1003. const table = root.getChild( 0 );
  1004. model.change( writer => {
  1005. // Removing row from a heading section changes requires changing heading rows attribute.
  1006. writer.remove( table.getChild( 0 ) );
  1007. writer.setAttribute( 'headingRows', 0, table );
  1008. } );
  1009. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  1010. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  1011. '<div class="ck ck-widget__selection-handle"></div>' +
  1012. '<table>' +
  1013. '<tbody>' +
  1014. '<tr>' +
  1015. '<td class="ck-editor__editable ck-editor__nested-editable" ' +
  1016. 'colspan="2" contenteditable="true" rowspan="2">' +
  1017. '<span style="display:inline-block">10</span>' +
  1018. '</td>' +
  1019. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1020. '<span style="display:inline-block">12</span>' +
  1021. '</td>' +
  1022. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1023. '<span style="display:inline-block">13</span>' +
  1024. '</td>' +
  1025. '</tr>' +
  1026. '<tr>' +
  1027. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1028. '<span style="display:inline-block">22</span>' +
  1029. '</td>' +
  1030. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1031. '<span style="display:inline-block">23</span>' +
  1032. '</td>' +
  1033. '</tr>' +
  1034. '</tbody>' +
  1035. '</table>' +
  1036. '</figure>'
  1037. );
  1038. } );
  1039. it( 'should remove empty thead if a last row was removed from a heading rows (has heading and body)', () => {
  1040. setModelData( model, modelTable( [
  1041. [ '00[]', '01' ],
  1042. [ '10', '11' ]
  1043. ], { headingRows: 1 } ) );
  1044. const table = root.getChild( 0 );
  1045. model.change( writer => {
  1046. // Removing row from a heading section changes requires changing heading rows attribute.
  1047. writer.removeAttribute( 'headingRows', table );
  1048. writer.remove( table.getChild( 0 ) );
  1049. } );
  1050. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  1051. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  1052. '<div class="ck ck-widget__selection-handle"></div>' +
  1053. '<table>' +
  1054. '<tbody>' +
  1055. '<tr>' +
  1056. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1057. '<span style="display:inline-block">10</span>' +
  1058. '</td>' +
  1059. '<td class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1060. '<span style="display:inline-block">11</span>' +
  1061. '</td>' +
  1062. '</tr>' +
  1063. '</tbody>' +
  1064. '</table>' +
  1065. '</figure>'
  1066. );
  1067. } );
  1068. it( 'should remove empty tbody if a last row was removed a body rows (has heading and body)', () => {
  1069. setModelData( model, modelTable( [
  1070. [ '00[]', '01' ],
  1071. [ '10', '11' ]
  1072. ], { headingRows: 1 } ) );
  1073. const table = root.getChild( 0 );
  1074. model.change( writer => {
  1075. writer.remove( table.getChild( 1 ) );
  1076. } );
  1077. assertEqualMarkup( getViewData( view, { withoutSelection: true } ),
  1078. '<figure class="ck-widget ck-widget_with-selection-handle table" contenteditable="false">' +
  1079. '<div class="ck ck-widget__selection-handle"></div>' +
  1080. '<table>' +
  1081. '<thead>' +
  1082. '<tr>' +
  1083. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1084. '<span style="display:inline-block">00</span>' +
  1085. '</th>' +
  1086. '<th class="ck-editor__editable ck-editor__nested-editable" contenteditable="true">' +
  1087. '<span style="display:inline-block">01</span>' +
  1088. '</th>' +
  1089. '</tr>' +
  1090. '</thead>' +
  1091. '</table>' +
  1092. '</figure>'
  1093. );
  1094. } );
  1095. } );
  1096. } );
  1097. } );