8
0

downcast.js 43 KB

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