diff --git a/.changeset/lazy-walls-run.md b/.changeset/lazy-walls-run.md
new file mode 100644
index 0000000..427bf52
--- /dev/null
+++ b/.changeset/lazy-walls-run.md
@@ -0,0 +1,5 @@
+---
+'@graphcms/html-to-slate-ast': minor
+---
+
+Populate empty children array with text node
diff --git a/packages/html-to-slate-ast/src/index.ts b/packages/html-to-slate-ast/src/index.ts
index df16617..05e925e 100644
--- a/packages/html-to-slate-ast/src/index.ts
+++ b/packages/html-to-slate-ast/src/index.ts
@@ -86,10 +86,15 @@ function deserialize(el: Node) {
) {
parent = el.childNodes[0];
}
- const children = Array.from(parent.childNodes)
+ let children = Array.from(parent.childNodes)
.map(deserialize)
.flat() as ChildNode[];
+ if (children.length === 0) {
+ if (!['COLGROUP', 'COL', 'CAPTION', 'TFOOT'].includes(nodeName))
+ // @ts-expect-error
+ children = [{ text: '' }];
+ }
if (el.nodeName === 'BODY') {
return jsx('fragment', {}, children);
}
@@ -137,7 +142,9 @@ function deserialize(el: Node) {
return jsx('element', attrs, listItemChildren);
} else if (
nodeName === 'TABLE' &&
- !children.find((node: ChildNode) => node.nodeName === 'THEAD')
+ !Array.from((el as HTMLTableElement).childNodes).find(
+ (node: ChildNode) => node.nodeName === 'THEAD'
+ )
) {
// tables must have thead, otherwise field crashes
const thead = {
@@ -145,6 +152,41 @@ function deserialize(el: Node) {
children: [],
};
return jsx('element', attrs, [thead, ...children]);
+ } else if (nodeName === 'TR') {
+ // if TR is empty, insert a cell with a paragraph to ensure selection can be placed inside
+ const modifiedChildren =
+ (el as HTMLTableRowElement).cells.length === 0
+ ? [
+ {
+ type: 'table_cell',
+ children: [
+ {
+ type: 'paragraph',
+ children: [{ text: el.textContent ? el.textContent : '' }],
+ },
+ ],
+ },
+ ]
+ : children;
+ return jsx('element', attrs, modifiedChildren);
+ } else if (nodeName === 'TD') {
+ // if TD is empty, insert a a paragraph to ensure selection can be placed inside
+ const childNodes = Array.from(
+ (el as HTMLTableDataCellElement).childNodes
+ );
+ const modifiedChildren =
+ childNodes.length === 0
+ ? [
+ {
+ type: 'paragraph',
+ children: [{ text: '' }],
+ },
+ ]
+ : childNodes.map((child) => ({
+ type: 'paragraph',
+ children: [{ text: child.textContent ? child.textContent : '' }],
+ }));
+ return jsx('element', attrs, modifiedChildren);
} else if (nodeName === 'IMG') {
return jsx('element', attrs, [attrs.href]);
}
@@ -153,12 +195,10 @@ function deserialize(el: Node) {
if (nodeName === 'DIV') {
const childNodes = Array.from(el.childNodes);
- const isParagraph =
- childNodes.length &&
- childNodes.every(
- (child) =>
- (isElementNode(child) && isInlineElement(child)) || isTextNode(child)
- );
+ const isParagraph = childNodes.every(
+ (child) =>
+ (isElementNode(child) && isInlineElement(child)) || isTextNode(child)
+ );
if (isParagraph) {
return jsx('element', { type: 'paragraph' }, children);
}
diff --git a/packages/html-to-slate-ast/test/google-docs_input.html b/packages/html-to-slate-ast/test/google-docs_input.html
new file mode 100644
index 0000000..b8022cc
--- /dev/null
+++ b/packages/html-to-slate-ast/test/google-docs_input.html
@@ -0,0 +1 @@
+Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Unordered list:
One
Two
Three
Ordered list:
One
Two
Table:
Title
Subtitle
Unordered list:
One
Two
Three
Ordered list:
One
Two
Table: