From a1d414d615ced18a073462aebed3914ad44c49f6 Mon Sep 17 00:00:00 2001 From: Tetsuaki Hamano Date: Fri, 16 Dec 2022 23:36:24 +0900 Subject: [PATCH] Table Block: Support rowspan attribute in table HTML, including when pasting --- packages/block-library/src/table/block.json | 15 ++ packages/block-library/src/table/edit.js | 13 +- packages/block-library/src/table/save.js | 21 +- .../block-library/src/table/transforms.js | 4 +- packages/block-library/src/table/utils.js | 10 + .../api/raw-handling/test/paste-handler.js | 181 ++++++++++++------ 6 files changed, 183 insertions(+), 61 deletions(-) create mode 100644 packages/block-library/src/table/utils.js diff --git a/packages/block-library/src/table/block.json b/packages/block-library/src/table/block.json index 5e21c1f07f8ba2..adac1e9c2130e0 100644 --- a/packages/block-library/src/table/block.json +++ b/packages/block-library/src/table/block.json @@ -52,6 +52,11 @@ "type": "string", "source": "attribute", "attribute": "colspan" + }, + "rowspan": { + "type": "string", + "source": "attribute", + "attribute": "rowspan" } } } @@ -92,6 +97,11 @@ "type": "string", "source": "attribute", "attribute": "colspan" + }, + "rowspan": { + "type": "string", + "source": "attribute", + "attribute": "rowspan" } } } @@ -132,6 +142,11 @@ "type": "string", "source": "attribute", "attribute": "colspan" + }, + "rowspan": { + "type": "string", + "source": "attribute", + "attribute": "rowspan" } } } diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index 2ca9cb6c42a984..2edaaf49dbea87 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -57,6 +57,7 @@ import { toggleSection, isEmptyTableSection, } from './state'; +import { normalizeRowColSpan } from './utils'; const ALIGNMENT_CONTROLS = [ { @@ -404,7 +405,14 @@ function TableEdit( { { cells.map( ( - { content, tag: CellTag, scope, align, colspan }, + { + content, + tag: CellTag, + scope, + align, + colspan, + rowspan, + }, columnIndex ) => ( { diff --git a/packages/block-library/src/table/save.js b/packages/block-library/src/table/save.js index d3393267b754df..5766887895a977 100644 --- a/packages/block-library/src/table/save.js +++ b/packages/block-library/src/table/save.js @@ -14,6 +14,11 @@ import { __experimentalGetElementClassName, } from '@wordpress/block-editor'; +/** + * Internal dependencies + */ +import { normalizeRowColSpan } from './utils'; + export default function save( { attributes } ) { const { hasFixedLayout, head, body, foot, caption } = attributes; const isEmpty = ! head.length && ! body.length && ! foot.length; @@ -44,7 +49,14 @@ export default function save( { attributes } ) { { cells.map( ( - { content, tag, scope, align, colspan }, + { + content, + tag, + scope, + align, + colspan, + rowspan, + }, cellIndex ) => { const cellClasses = classnames( { @@ -65,7 +77,12 @@ export default function save( { attributes } ) { scope={ tag === 'th' ? scope : undefined } - colSpan={ colspan } + colSpan={ normalizeRowColSpan( + colspan + ) } + rowSpan={ normalizeRowColSpan( + rowspan + ) } /> ); } diff --git a/packages/block-library/src/table/transforms.js b/packages/block-library/src/table/transforms.js index 0651c3bc64c415..906af299e2557e 100644 --- a/packages/block-library/src/table/transforms.js +++ b/packages/block-library/src/table/transforms.js @@ -5,12 +5,12 @@ const tableContentPasteSchema = ( { phrasingContentSchema } ) => ( { th: { allowEmpty: true, children: phrasingContentSchema, - attributes: [ 'scope', 'colspan' ], + attributes: [ 'scope', 'colspan', 'rowspan' ], }, td: { allowEmpty: true, children: phrasingContentSchema, - attributes: [ 'colspan' ], + attributes: [ 'colspan', 'rowspan' ], }, }, }, diff --git a/packages/block-library/src/table/utils.js b/packages/block-library/src/table/utils.js new file mode 100644 index 00000000000000..240cf4b361e2d3 --- /dev/null +++ b/packages/block-library/src/table/utils.js @@ -0,0 +1,10 @@ +/** + * Normalize the row or column span value. + * + * @param {number|undefined} rowColSpan normalized value. + */ +export function normalizeRowColSpan( rowColSpan ) { + return parseInt( rowColSpan, 10 ) && parseInt( rowColSpan, 10 ) !== 1 + ? parseInt( rowColSpan, 10 ) + : undefined; +} diff --git a/packages/blocks/src/api/raw-handling/test/paste-handler.js b/packages/blocks/src/api/raw-handling/test/paste-handler.js index 378189b10e0ea0..a601f308137362 100644 --- a/packages/blocks/src/api/raw-handling/test/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/test/paste-handler.js @@ -7,48 +7,42 @@ import { pasteHandler } from '@wordpress/blocks'; */ import { init as initAndRegisterTableBlock } from '../../../../../block-library/src/table'; -const tableWithHeaderFooterAndBodyUsingColspan = ` +const tableWithHeaderFooterAndBodyUsingColspanAndRowspan = ` - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Colspan 2Header Cell
Footer CellFooter Cell
Colspan 2Cell Data
Header CellHeader CellRowspan 2
Colspan 2
Body CellBody CellRowspan 2
Colspan 2
Footer CellFooter CellRowspan 2
Colspan 2
`; -const googleDocsTableWithColspan = ` - -
- - - - - - - - - - -
-

Test colspan -

-
-

+const googleDocsTableWithColspanAndRowspan = ` +
Body CellBody Cell
Rowspan 2
Colspan 2
`; describe( 'pasteHandler', () => { @@ -56,9 +50,9 @@ describe( 'pasteHandler', () => { initAndRegisterTableBlock(); } ); - it( 'can handle a table with thead, tbody and tfoot using colspan', () => { + it( 'can handle a table with thead, tbody and tfoot using colspan and rowspan', () => { const [ result ] = pasteHandler( { - HTML: tableWithHeaderFooterAndBodyUsingColspan, + HTML: tableWithHeaderFooterAndBodyUsingColspanAndRowspan, tagName: 'p', preserveWhiteSpace: false, } ); @@ -71,24 +65,84 @@ describe( 'pasteHandler', () => { head: [ { cells: [ - { content: 'Colspan 2', tag: 'th', colspan: '2' }, - { content: 'Header Cell', tag: 'th' }, + { + content: 'Header Cell', + tag: 'th', + }, + { + content: 'Header Cell', + tag: 'th', + }, + { + content: 'Rowspan 2', + tag: 'th', + rowspan: '2', + }, + ], + }, + { + cells: [ + { + content: 'Colspan 2', + tag: 'th', + colspan: '2', + }, ], }, ], body: [ { cells: [ - { content: 'Colspan 2', tag: 'td', colspan: '2' }, - { content: 'Cell Data', tag: 'td' }, + { + content: 'Body Cell', + tag: 'td', + }, + { + content: 'Body Cell', + tag: 'td', + }, + { + content: 'Rowspan 2', + tag: 'td', + rowspan: '2', + }, + ], + }, + { + cells: [ + { + content: 'Colspan 2', + tag: 'td', + colspan: '2', + }, ], }, ], foot: [ { cells: [ - { content: 'Footer Cell', tag: 'th', colspan: '2' }, - { content: 'Footer Cell', tag: 'th' }, + { + content: 'Footer Cell', + tag: 'td', + }, + { + content: 'Footer Cell', + tag: 'td', + }, + { + content: 'Rowspan 2', + tag: 'td', + rowspan: '2', + }, + ], + }, + { + cells: [ + { + content: 'Colspan 2', + tag: 'td', + colspan: '2', + }, ], }, ], @@ -97,9 +151,9 @@ describe( 'pasteHandler', () => { expect( result.isValid ).toBeTruthy(); } ); - it( 'can handle a google docs table with colspan', () => { + it( 'can handle a google docs table with colspan and rowspan', () => { const [ result ] = pasteHandler( { - HTML: googleDocsTableWithColspan, + HTML: googleDocsTableWithColspanAndRowspan, tagName: 'p', preserveWhiteSpace: false, } ); @@ -107,23 +161,40 @@ describe( 'pasteHandler', () => { expect( console ).toHaveLogged(); expect( result.attributes ).toEqual( { + hasFixedLayout: false, + caption: '', body: [ { cells: [ { - align: undefined, - colspan: '2', - content: 'Test colspan', - scope: undefined, + content: 'Body Cell', + tag: 'td', + }, + { + content: 'Body Cell', + tag: 'td', + }, + { + content: 'Rowspan 2', tag: 'td', + colspan: '1', + rowspan: '2', + }, + ], + }, + { + cells: [ + { + content: 'Colspan 2', + tag: 'td', + colspan: '2', + rowspan: '1', }, ], }, ], - caption: '', - foot: [], - hasFixedLayout: false, head: [], + foot: [], } ); expect( result.name ).toEqual( 'core/table' ); expect( result.isValid ).toBeTruthy();