diff --git a/.changeset/quick-lamps-check.md b/.changeset/quick-lamps-check.md new file mode 100644 index 0000000..562ce27 --- /dev/null +++ b/.changeset/quick-lamps-check.md @@ -0,0 +1,7 @@ +--- +"@graphcms/html-to-slate-ast": minor +--- + +- Add IFrame support +- Add bold text support to table cells +- Add nested tags support to table cells diff --git a/packages/html-to-slate-ast/src/index.ts b/packages/html-to-slate-ast/src/index.ts index 41c6b74..1b1abdd 100644 --- a/packages/html-to-slate-ast/src/index.ts +++ b/packages/html-to-slate-ast/src/index.ts @@ -62,6 +62,25 @@ const ELEMENT_TAGS: Record< }; }, PRE: () => ({ type: 'code-block' }), + IFRAME: el => { + const src = el.getAttribute('src'); + if (!src) return {}; + const height = el.getAttribute('height'); + const width = el.getAttribute('width'); + return { + type: 'iframe', + url: '//www.youtube.com/embed/ljiWOrULppk?rel=0', + // default iframe height is 150 + height: Number(height || 150), + // default iframe width is 300 + width: Number(width || 300), + children: [ + { + text: '', + }, + ], + }; + }, }; const TEXT_TAGS: Record< @@ -215,19 +234,17 @@ function deserialize< } else if (nodeName === 'TD' || nodeName === 'TH') { // if TD or TH is empty, insert a paragraph to ensure selection can be placed inside const childNodes = Array.from((el as HTMLTableCellElement).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); + if (childNodes.length === 0) { + return jsx('element', attrs, [ + { + type: 'paragraph', + children: [{ text: '' }], + }, + ]); + } else { + const children = childNodes.map(c => deserialize(c, global)).flat(); + return jsx('element', attrs, children); + } } else if (nodeName === 'IMG') { return jsx('element', attrs, [attrs.href]); } diff --git a/packages/html-to-slate-ast/test/html_input_iframe.html b/packages/html-to-slate-ast/test/html_input_iframe.html new file mode 100644 index 0000000..d4e64db --- /dev/null +++ b/packages/html-to-slate-ast/test/html_input_iframe.html @@ -0,0 +1,34 @@ +
+

+ SANTA CLARA, Calif., (June 20, 2017) – experience for fans and newcomers alike. +

+
+ +
+

 

+

+ .hack is a multimedia franchise created and developed by famed + Japanese developer CyberConnect2. Comprising of video games, anime, novels, + and manga, the world of .hack focuses on the mysterious events + surrounding a wildly popular in-universe massively multiplayer role-playing + game called The World. .hack//G.U. begins after the events of the + original .hack series with players assuming the role of Haseo as he + tracks down a powerful Player Killer named Tri-Edge who killed his friend’s + in-game avatar Shino, and put her into a coma in real life. +

+
diff --git a/packages/html-to-slate-ast/test/html_input_table.html b/packages/html-to-slate-ast/test/html_input_table.html new file mode 100644 index 0000000..bcfa0ac --- /dev/null +++ b/packages/html-to-slate-ast/test/html_input_table.html @@ -0,0 +1,28 @@ +
+ + + + + + + + + + + + + +
+

R1C1 - BOLD TEXT

+
+
R1C2
+

R1C3

+

R2C1

+
+ R2C2 - ITALIC TEXT + +

+ R2C3 - BOLD TEXT +

+
+
diff --git a/packages/html-to-slate-ast/test/index.test.ts b/packages/html-to-slate-ast/test/index.test.ts index fca9c22..1de8614 100644 --- a/packages/html-to-slate-ast/test/index.test.ts +++ b/packages/html-to-slate-ast/test/index.test.ts @@ -491,11 +491,205 @@ test('Transforms inner spans wrapped in a div into paragraph', () => { ); }); +test('Transforms iframe correctly', async () => { + const input = fs + .readFileSync(__dirname + '/html_input_iframe.html') + .toString(); + + const ast = await htmlToSlateAST(input); + expect(ast).toStrictEqual([ + { + type: 'paragraph', + children: [ + { + text: 'SANTA CLARA, Calif., (June 20, 2017) –', + bold: true, + }, + { + text: ' experience for fans and newcomers alike.\n  ', + }, + ], + }, + { + type: 'paragraph', + children: [ + { + url: '//www.youtube.com/embed/ljiWOrULppk?rel=0', + type: 'iframe', + width: 640, + height: 360, + children: [ + { + text: '', + }, + ], + }, + ], + }, + { + type: 'paragraph', + children: [ + { + text: ' ', + }, + ], + }, + { + type: 'paragraph', + children: [ + { + text: '.hack', + italic: true, + }, + { + text: + ' is a multimedia franchise created and developed by famed\n Japanese developer CyberConnect2. Comprising of video games, anime, novels,\n and manga, the world of ', + }, + { + text: '.hack', + italic: true, + }, + { + text: + ' focuses on the mysterious events\n surrounding a wildly popular in-universe massively multiplayer role-playing\n game called The World. ', + }, + { + text: '.hack//G.U. ', + italic: true, + }, + { + text: 'begins after the events of the\n original ', + }, + { + text: '.hack ', + italic: true, + }, + { + text: + 'series with players assuming the role of Haseo as he\n tracks down a powerful Player Killer named Tri-Edge who killed his friend’s\n in-game avatar Shino, and put her into a coma in real life.\n  ', + }, + ], + }, + { + text: '\n', + }, + ]); +}); + +test('Transforms tables with nested html tags as cells', async () => { + const input = fs + .readFileSync(__dirname + '/html_input_table.html') + .toString(); + + const ast = await htmlToSlateAST(input); + expect(ast).toStrictEqual([ + { + type: 'table', + children: [ + { + type: 'table_body', + children: [ + { + type: 'table_row', + children: [ + { + type: 'table_cell', + children: [ + { + type: 'paragraph', + children: [ + { + bold: true, + text: 'R1C1 - BOLD TEXT', + }, + ], + }, + ], + }, + { + type: 'table_cell', + children: [ + { + type: 'paragraph', + children: [ + { + text: 'R1C2', + }, + ], + }, + ], + }, + { + type: 'table_cell', + children: [ + { + type: 'paragraph', + children: [ + { + text: 'R1C3', + }, + ], + }, + ], + }, + ], + }, + { + type: 'table_row', + children: [ + { + type: 'table_cell', + children: [ + { + type: 'paragraph', + children: [ + { + text: 'R2C1', + }, + ], + }, + ], + }, + { + type: 'table_cell', + children: [ + { + italic: true, + text: 'R2C2 - ITALIC TEXT', + }, + ], + }, + { + type: 'table_cell', + children: [ + { + type: 'paragraph', + children: [ + { + bold: true, + text: 'R2C3 - BOLD TEXT', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + text: '\n', + }, + ]); +}); + test('Transforms Google Docs input', () => { const input = fs .readFileSync(__dirname + '/google-docs_input.html') .toString(); - return htmlToSlateAST(input).then(ast => + return htmlToSlateAST(input).then(ast => { expect(ast).toEqual([ { type: 'heading-one', @@ -733,12 +927,7 @@ test('Transforms Google Docs input', () => { type: 'table_cell', children: [ { - type: 'paragraph', - children: [ - { - text: '', - }, - ], + text: '\n', }, ], }, @@ -746,12 +935,7 @@ test('Transforms Google Docs input', () => { type: 'table_cell', children: [ { - type: 'paragraph', - children: [ - { - text: '', - }, - ], + text: '\n', }, ], }, @@ -787,8 +971,8 @@ test('Transforms Google Docs input', () => { }, ], }, - ]) - ); + ]); + }); }); test('Converts word documents', () => {