Skip to content

Commit

Permalink
Update nested lists handling (#48)
Browse files Browse the repository at this point in the history
* refactor: wrap each list item child in separate LIC nodes if nested list present

* chore: lint

* chore: changeset
  • Loading branch information
oddtinker authored Nov 24, 2021
1 parent 9eda915 commit 28455b3
Show file tree
Hide file tree
Showing 6 changed files with 454 additions and 39 deletions.
5 changes: 5 additions & 0 deletions .changeset/strange-paws-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphcms/html-to-slate-ast': minor
---

Refactor nested lists handling
40 changes: 26 additions & 14 deletions packages/html-to-slate-ast/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ELEMENT_TAGS: Record<
OL: () => ({ type: 'numbered-list' }),
UL: () => ({ type: 'bulleted-list' }),
P: () => ({ type: 'paragraph' }),
A: el => {
A: (el) => {
const href = el.getAttribute('href');
if (href === null) return {};
return {
Expand Down Expand Up @@ -44,7 +44,7 @@ const ELEMENT_TAGS: Record<
TR: () => ({ type: 'table_row' }),
TD: () => ({ type: 'table_cell' }),
TH: () => ({ type: 'table_cell' }),
IMG: el => {
IMG: (el) => {
const href = el.getAttribute('src');
const title = Boolean(el.getAttribute('alt'))
? el.getAttribute('alt')
Expand Down Expand Up @@ -108,7 +108,7 @@ function deserialize<
parent = el.childNodes[0];
}
let children = Array.from(parent.childNodes)
.map(c => deserialize(c, global))
.map((c) => deserialize(c, global))
.flat();

if (children.length === 0) {
Expand All @@ -124,7 +124,7 @@ function deserialize<
if (
isElementNode(el) &&
Array.from(el.attributes).find(
attr => attr.name === 'role' && attr.value === 'heading'
(attr) => attr.name === 'role' && attr.value === 'heading'
)
) {
const level = el.attributes.getNamedItem('aria-level')?.value;
Expand Down Expand Up @@ -157,6 +157,20 @@ function deserialize<
const attrs = ELEMENT_TAGS[nodeName](el as HTMLElement);
// li children must be rendered in spans, like in list plugin
if (nodeName === 'LI') {
const hasNestedListChild = children.find(
(item) =>
SlateElement.isElement(item) &&
// if element has a nested list as a child, all children must be wrapped in individual list-item-child nodes
// TODO: sync with GCMS types for Slate elements
// @ts-expect-error
(item.type === 'numbered-list' || item.type === 'bulleted-list')
);
if (hasNestedListChild) {
const wrappedChildren = children.map((item) =>
jsx('element', { type: 'list-item-child' }, item)
);
return jsx('element', attrs, wrappedChildren);
}
// in any case we add a single list-item-child containing the children
const child = jsx('element', { type: 'list-item-child' }, children);
return jsx('element', attrs, [child]);
Expand All @@ -179,9 +193,7 @@ function deserialize<
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 childNodes = Array.from((el as HTMLTableCellElement).childNodes);
const modifiedChildren =
childNodes.length === 0
? [
Expand All @@ -190,7 +202,7 @@ function deserialize<
children: [{ text: '' }],
},
]
: childNodes.map(child => ({
: childNodes.map((child) => ({
type: 'paragraph',
children: [{ text: child.textContent ? child.textContent : '' }],
}));
Expand All @@ -204,7 +216,7 @@ function deserialize<
if (nodeName === 'DIV') {
const childNodes = Array.from(el.childNodes);
const isParagraph = childNodes.every(
child =>
(child) =>
(isElementNode(child) && isInlineElement(child)) || isTextNode(child)
);
if (isParagraph) {
Expand Down Expand Up @@ -242,15 +254,15 @@ function deserialize<
const attrs = tagNames.reduce((acc, current) => {
return { ...acc, ...TEXT_TAGS[current]() };
}, {});
return children.map(child => {
return children.map((child) => {
if (typeof child === 'string') {
return jsx('text', attrs, child);
}

if (isChildNode(child, global)) return child;

if (SlateElement.isElement(child) && !SlateText.isText(child)) {
child.children = child.children.map(c => ({ ...c, ...attrs }));
child.children = child.children.map((c) => ({ ...c, ...attrs }));
return child;
}

Expand All @@ -261,15 +273,15 @@ function deserialize<

if (TEXT_TAGS[nodeName]) {
const attrs = TEXT_TAGS[nodeName](el as HTMLElement);
return children.map(child => {
return children.map((child) => {
if (typeof child === 'string') {
return jsx('text', attrs, child);
}

if (isChildNode(child, global)) return child;

if (SlateElement.isElement(child) && !SlateText.isText(child)) {
child.children = child.children.map(c => ({ ...c, ...attrs }));
child.children = child.children.map((c) => ({ ...c, ...attrs }));
return child;
}

Expand Down Expand Up @@ -441,7 +453,7 @@ export async function htmlToSlateAST(html: string) {
const domDocument = await parseDomDocument(normalizedHTML);
const global = await (async () => {
if (typeof window !== 'undefined') return window;
return await import('jsdom').then(jsdom => new jsdom.JSDOM().window);
return await import('jsdom').then((jsdom) => new jsdom.JSDOM().window);
})();
return deserialize(domDocument.body, global);
}
Expand Down
Loading

0 comments on commit 28455b3

Please sign in to comment.