8
0

downcast.js 43 KB

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