Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pasting table with lists content #46512

Closed
2 changes: 1 addition & 1 deletion packages/block-library/src/list/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { create, split, toHTMLString } from '@wordpress/rich-text';
*/
import { createListBlockFromDOMElement } from './utils';

function getListContentSchema( { phrasingContentSchema } ) {
export function getListContentSchema( { phrasingContentSchema } ) {
const listContentSchema = {
...phrasingContentSchema,
ul: {},
Expand Down
99 changes: 97 additions & 2 deletions packages/block-library/src/table/transforms.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
/**
* WordPress dependencies
*/
import { createBlock, getBlockAttributes } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { name } from './block.json';
import { getListContentSchema } from '../list/transforms';

const tableContentPasteSchema = ( { phrasingContentSchema } ) => ( {
tr: {
allowEmpty: true,
children: {
th: {
allowEmpty: true,
children: phrasingContentSchema,
children: getListContentSchema( { phrasingContentSchema } ),
attributes: [ 'scope', 'colspan' ],
},
td: {
allowEmpty: true,
children: phrasingContentSchema,
children: getListContentSchema( { phrasingContentSchema } ),
attributes: [ 'colspan' ],
},
},
Expand All @@ -35,12 +46,96 @@ const tablePasteSchema = ( args ) => ( {
},
} );

const indent = ( depth ) => {
return Array.from( { length: depth * 4 } )
.map( () => ' ' )
.join( '' );
};

const isList = ( node ) => {
return node.tagName === 'UL' || node.tagName === 'OL';
};

const transformContent = ( cell ) => {
// not available by default in node (jest) env so eslint complains.
// eslint-disable-next-line no-undef
const parser = new DOMParser();
const document = parser.parseFromString( cell.content, 'text/html' );
const result = [];
const processDOMTree = ( node, listDepth = 0 ) => {
if ( isList( node ) ) {
if ( listDepth === 0 ) {
result.push( '<br/>' );
}
Array.from( node.childNodes ).forEach(
( listItem, listItemIndex ) => {
const children = Array.from( listItem.childNodes );
let simple = [];
children.forEach( ( child ) => {
if ( isList( child ) ) {
const bullet =
node.tagName === 'UL'
? '-'
: `${ listItemIndex + 1 }.`;
if ( simple.length ) {
result.push(
`${ indent(
listDepth
) } ${ bullet } ${ simple.join( '' ) }<br/>`
);
simple = [];
}
processDOMTree( child, listDepth + 1 );
} else {
simple.push( child.outerHTML || child.textContent );
}
} );
if ( simple.length ) {
const bullet =
node.tagName === 'UL'
? '-'
: `${ listItemIndex + 1 }.`;

result.push(
`${ indent(
listDepth
) } ${ bullet } ${ simple.join( '' ) }<br/>`
);
simple = [];
}
}
);
} else if ( node.nodeName === '#text' ) {
result.push( node.textContent );
} else {
result.push( node.outerHTML );
}
};

Array.from( document.body.childNodes ).forEach( ( node ) =>
processDOMTree( node )
);

cell.content = result.join( '' );
};

const transformTableNode = ( node ) => {
const attributes = getBlockAttributes( name, node.outerHTML );
attributes.body.forEach( ( row ) => {
row.cells.forEach( ( cell ) => {
transformContent( cell );
} );
} );
return createBlock( name, attributes );
};

const transforms = {
from: [
{
type: 'raw',
selector: 'table',
schema: tablePasteSchema,
transform: transformTableNode,
},
],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ exports[`Blocks raw handling pasteHandler google-docs-table 1`] = `"<br><br><br>

exports[`Blocks raw handling pasteHandler google-docs-table-with-comments 1`] = `"<br><br><br><br>One<br>Two<br>Three<br>1<br>2<br>3<br>I<br>II<br>III"`;

exports[`Blocks raw handling pasteHandler google-docs-table-with-lists 1`] = `"<br><br><strong>1. Owner</strong><br>Daniel<br><strong>Notes:</strong><br>“Issues” are topics and <strong>agenda</strong> items <strong><em>anyone</em></strong> can bring to the meeting.<br>Nested<br>IDS stands for “Identify, Discuss, Solve”.<br>Testing new line<br>This is nested<br>This is even <strong>more</strong> nested!<br>More <em>stuff</em><br><br><strong>Other ideas</strong>:<br>A new list<br>Nested <strong>again</strong><br>"`;

exports[`Blocks raw handling pasteHandler google-docs-with-comments 1`] = `"This is a <strong>title</strong><br><br>This is a <em>heading</em><br><br>Formatting test: <strong>bold</strong>, <em>italic</em>, <a href=\\"https://w.org/\\">link</a>, <s>strikethrough</s>, <sup>superscript</sup>, <sub>subscript</sub>, <strong><em>nested</em></strong>.<br><br>A<br>Bulleted<br>Indented<br>List<br><br>One<br>Two<br>Three<br><br><br><br><br>One<br>Two<br>Three<br>1<br>2<br>3<br>I<br>II<br>III<br><br><br><br><br><br>An image:<br><br><img src=\\"https://lh4.googleusercontent.com/ID\\" width=\\"544\\" height=\\"184\\"><br><br>"`;

exports[`Blocks raw handling pasteHandler gutenberg 1`] = `"Test"`;
Expand Down
6 changes: 6 additions & 0 deletions test/integration/blocks-raw-handling.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// eslint-disable-next-line jsdoc/check-tag-names
/**
* @jest-environment jsdom
*/

/**
* External dependencies
*/
Expand Down Expand Up @@ -379,6 +384,7 @@ describe( 'Blocks raw handling', () => {
'google-docs-list-only',
'google-docs-table',
'google-docs-table-with-comments',
'google-docs-table-with-lists',
'google-docs-with-comments',
'ms-word',
'ms-word-styled',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<meta charset="utf-8"><b style="font-weight:normal;" id="docs-internal-guid-ff31a234-7fff-c928-ad0b-e49c17a28874"><div dir="ltr" style="margin-left:0pt;" align="left"><table style="border:none;border-collapse:collapse;"><colgroup><col width="185" /><col width="439" /></colgroup><tbody><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1. Owner</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Daniel</span></p></td></tr><tr style="height:112.44140625pt"><td colspan="2" style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Notes:</span></p><ol style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:decimal;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">&ldquo;Issues&rdquo; are topics and </span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">agenda</span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> items </span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">anyone</span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> can bring to the meeting.</span></p></li><ul style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:circle;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="2"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Nested</span></p></li></ul><li dir="ltr" style="list-style-type:decimal;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">IDS stands for &ldquo;Identify, Discuss, Solve&rdquo;.</span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"><br /></span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Testing new line</span></p></li><ul style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:circle;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="2"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">This is nested</span></p></li><ol style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:lower-roman;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="3"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">This is even </span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">more</span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> nested!</span></p></li></ol></ul></ol><p dir="ltr" style="line-height:1.2;margin-left: 36pt;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">More </span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:italic;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">stuff</span></p><br /><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Other ideas</span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">:</span></p><ol style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:decimal;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="1"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">A new list</span></p></li><ol style="margin-top:0;margin-bottom:0;padding-inline-start:48px;"><li dir="ltr" style="list-style-type:lower-alpha;font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;" aria-level="2"><p dir="ltr" style="line-height:1.2;margin-top:0pt;margin-bottom:0pt;" role="presentation"><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Nested </span><span style="font-size:11pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">again</span></p></li></ol></ol></td></tr></tbody></table></div><br /></b>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- wp:table -->
<figure class="wp-block-table"><table><tbody><tr><td><strong>1. Owner</strong></td><td>Daniel</td></tr><tr><td colspan="2"><strong>Notes:</strong><br/> 1. “Issues” are topics and <strong>agenda</strong> items <strong><em>anyone</em></strong> can bring to the meeting.<br/> - Nested<br/> 2. IDS stands for “Identify, Discuss, Solve”.<br>Testing new line<br/> - This is nested<br/> 1. This is even <strong>more</strong> nested!<br/>More <em>stuff</em><br><strong>Other ideas</strong>:<br/> 1. A new list<br/> 1. Nested <strong>again</strong><br/></td></tr></tbody></table></figure>
<!-- /wp:table -->