diff --git a/package.json b/package.json
index 2746f10..f95fd8d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@contentstack/json-rte-serializer",
- "version": "2.0.4",
+ "version": "2.0.5",
"description": "This Package converts Html Document to Json and vice-versa.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
diff --git a/src/fromRedactor.tsx b/src/fromRedactor.tsx
index 2609d54..27242cd 100644
--- a/src/fromRedactor.tsx
+++ b/src/fromRedactor.tsx
@@ -51,8 +51,8 @@ const ELEMENT_TAGS: IHtmlToJsonElementTags = {
THEAD: (el: HTMLElement) => ({ type: 'thead', attrs: {} }),
TBODY: (el: HTMLElement) => ({ type: 'tbody', attrs: {} }),
TR: (el: HTMLElement) => ({ type: 'tr', attrs: {} }),
- TD: (el: HTMLElement) => ({ type: 'td', attrs: {} }),
- TH: (el: HTMLElement) => ({ type: 'th', attrs: {} }),
+ TD: (el: HTMLElement) => ({ type: 'td', attrs: { ...spanningAttrs(el) } }),
+ TH: (el: HTMLElement) => ({ type: 'th', attrs: { ...spanningAttrs(el) } }),
// FIGURE: (el: HTMLElement) => ({ type: 'reference', attrs: { default: true, "display-type": "display", "type": "asset" } }),
FIGURE: (el: HTMLElement) => {
@@ -70,6 +70,18 @@ const ELEMENT_TAGS: IHtmlToJsonElementTags = {
DIV: (el: HTMLElement) => {
return { type: 'div', attrs: {} }
},
+ VIDEO: (el: HTMLElement) => {
+ const srcArray = Array.from(el.querySelectorAll("source")).map((source) =>
+ source.getAttribute("src")
+ );
+
+ return {
+ type: 'embed',
+ attrs: {
+ src: srcArray.length > 0 ? srcArray[0] : null,
+ },
+ }
+ },
STYLE: (el: HTMLElement) => {
return { type: 'style', attrs: { "style-text": el.textContent } }
},
@@ -208,6 +220,28 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
} else if (el.nodeName === 'COLGROUP') {
return null
}
+ else if (el.nodeName === "TABLE") {
+ const tbody = el.querySelector('tbody')
+ const thead = el.querySelector('thead')
+
+ if (!tbody && !thead) {
+ el.innerHTML += "
"
+ }
+ }
+ else if (['TBODY', 'THEAD'].includes(el.nodeName)) {
+ const row = el.querySelector('tr')
+ if (!row) {
+ el.innerHTML += "
"
+ }
+ }
+ else if (el.nodeName === 'TR') {
+ const cell = el.querySelector('th, td')
+ if (!cell) {
+ const cellType = el.parentElement.nodeName === 'THEAD' ? 'th' : 'td'
+ el.innerHTML += `<${cellType}>${cellType}>`
+
+ }
+ }
const { nodeName } = el
let parent = el
if(el.nodeName === "BODY"){
@@ -381,6 +415,9 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
if (nodeName === 'FIGCAPTION') {
return null
}
+ if (nodeName === 'SOURCE') {
+ return null;
+ }
if (nodeName === 'DIV') {
const dataType = el.attributes['data-type']?.value
if (dataType === 'row') {
@@ -588,7 +625,7 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
return jsx('element', elementAttrs, [{ text: '' }])
}
}
- if (nodeName === 'IMG' || nodeName === 'IFRAME') {
+ if (nodeName === 'IMG' || nodeName === 'IFRAME' || nodeName === 'VIDEO') {
if (elementAttrs?.attrs?.["redactor-attributes"]?.width) {
let width = elementAttrs.attrs["redactor-attributes"].width
if (width.slice(width.length - 1) === '%') {
@@ -613,46 +650,111 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
})
}
if (nodeName === 'TABLE') {
- let row = 0
+ const row = el.querySelectorAll('TR').length
let table_child = ['THEAD', 'TBODY']
let cell_type = ['TH', 'TD']
let col = 0
- Array.from(el.childNodes).forEach((child: any) => {
- if (table_child.includes(child.nodeName)) {
- row += child.childNodes.length
- }
- })
- let rowElement = el.getElementsByTagName('TR')[0]
- if (rowElement)
- Array.from(rowElement.childNodes).forEach((child: any) => {
- if (cell_type.includes(child.nodeName)) {
- col += 1
- }
- })
+
+ const colElementLength = el.getElementsByTagName('COLGROUP')[0]?.children?.length ?? 0
+ col = Math.max(...Array.from(el.getElementsByTagName('TR')).map((row: any) => row.children.length), colElementLength)
let colWidths: Array = Array.from({ length: col }).fill(250)
- if (el?.childNodes?.[0]?.nodeName === 'COLGROUP') {
- let colGroupWidth: Array = []
- let totalWidth = parseFloat(el.childNodes[0].getAttribute('data-width')) || col * 250
- Array.from(el.childNodes[0].childNodes).forEach((child: any) => {
- let width = child?.style?.width || '250px'
- if (width.slice(width.length - 1) === '%') {
- colGroupWidth.push((parseFloat(width.slice(0, width.length - 1)) * totalWidth) / 100)
- } else if (width.slice(width.length - 2) === 'px') {
- colGroupWidth.push(parseFloat(width.slice(0, width.length - 2)))
+
+ Array.from(el.childNodes).forEach((child: any) => {
+ if (child?.nodeName === 'COLGROUP') {
+ let colGroupWidth = Array(col).fill(250)
+ let totalWidth = parseFloat(child.getAttribute('data-width')) || col * 250
+ Array.from(child.children).forEach((child: any, index) => {
+ if (child?.nodeName === 'COL') {
+ let width = child?.style?.width ?? '250px'
+ if (width.substr(-1) === '%') {
+ colGroupWidth[index] = (parseFloat(width.slice(0, width.length - 1)) * totalWidth) / 100
+ } else if (width.substr(-2) === 'px') {
+ colGroupWidth[index] = parseFloat(width.slice(0, width.length - 2))
+ }
}
})
colWidths = colGroupWidth
}
+ })
+ let tableHead : any
+ let tableBody: any
+
+ children.forEach((tableChild: any) => {
+ if (tableChild?.type === 'thead') {
+ tableHead = tableChild
+ return
+ }
+ if (tableChild?.type === 'tbody') {
+ tableBody = tableChild
+ return
+ }
+ });
+
+ let disabledCols = [...tableHead?.attrs?.disabledCols ?? [], ...tableBody?.attrs?.disabledCols ?? []]
+ delete tableHead?.attrs?.disabledCols
+ delete tableBody?.attrs?.disabledCols
+
+ const tableAttrs = {
+ ...elementAttrs.attrs,
+ rows: row,
+ cols: col,
+ colWidths: colWidths,
+ }
+
+ if(!isEmpty(disabledCols)){
+ tableAttrs['disabledCols'] = Array.from(new Set(disabledCols))
+ }
+
elementAttrs = {
...elementAttrs,
- attrs: {
- ...elementAttrs.attrs,
- rows: row,
- cols: col,
- colWidths: colWidths
- }
+ attrs: tableAttrs
}
}
+ if (["THEAD", "TBODY"].includes(nodeName)) {
+ const rows = children as any[]
+ const disabledCols = rows.flatMap(row => {
+ const { disabledCols } = row.attrs
+ delete row['attrs']['disabledCols']
+ return disabledCols ?? []
+ })
+ elementAttrs.attrs['disabledCols'] = disabledCols
+ }
+ if (nodeName === "TBODY") {
+
+ const rows = children
+
+ addVoidCellsAndApplyAttributes(rows)
+
+ children = getTbodyChildren(rows)
+ }
+
+ if (nodeName === "TR") {
+
+ const cells = children.filter((child:any) => ['th', 'td'].includes(child.type)) as any[]
+
+
+ const disabledCols = cells.flatMap((cell, cellIndex) => {
+ let { colSpan } = cell.attrs
+ if (!colSpan) return []
+ colSpan = parseInt(colSpan)
+ return Array(colSpan).fill(0).map((_, i) => cellIndex + i)
+ })
+
+ if (disabledCols?.length)
+ elementAttrs.attrs['disabledCols'] = disabledCols
+
+
+ }
+ if (['TD', 'TH'].includes(nodeName)) {
+ const { colSpan = 1, rowSpan } = elementAttrs?.['attrs']
+
+ return [
+ jsx('element', elementAttrs, children),
+ ...Array(colSpan - 1)
+ .fill(0)
+ .map((_) => emptyCell(nodeName.toLowerCase(), rowSpan ? { inducedRowSpan: rowSpan } : {}))
+ ]
+ }
if (nodeName === 'P') {
if (
elementAttrs?.attrs?.["redactor-attributes"]?.['data-checked'] &&
@@ -809,3 +911,84 @@ export const getNestedValueIfAvailable = (value: string) => {
return value;
}
};
+
+
+const spanningAttrs = (el: HTMLElement) => {
+ const attrs = {}
+ const rowSpan = parseInt(el.getAttribute('rowspan') ?? '1')
+ const colSpan = parseInt(el.getAttribute('colspan') ?? '1')
+ if (rowSpan > 1) attrs['rowSpan'] = rowSpan
+ if (colSpan > 1) attrs['colSpan'] = colSpan
+
+ return attrs
+}
+const emptyCell = (cellType: string, attrs = {}) => {
+ return jsx('element', { type: cellType, attrs: { void: true, ...attrs } }, [{ text: '' }])
+}
+
+const addVoidCellsAndApplyAttributes = (rows: any[]) => {
+ rows.forEach((row, currIndex) => {
+ const cells = row.children as any[]
+
+ cells.forEach((cell, cellIndex) => {
+ if (!cell || !cell.attrs) return
+
+ const { rowSpan, inducedRowSpan } = cell.attrs
+
+ let span = rowSpan ?? inducedRowSpan ?? 0
+
+ if (!span || span < 2) return
+ const nextRow = rows[currIndex + 1]
+ if (!nextRow) {
+ delete cell?.attrs?.inducedRowSpan
+ return
+ }
+
+ // set inducedRowSpan on cell in row at cellIndex
+ span--
+ nextRow?.children?.splice(cellIndex, 0,
+ emptyCell('td',
+ (span > 1) ? { inducedRowSpan: span } : {}
+ ))
+
+ // Include next row in trgrp
+ nextRow['attrs']['included'] = true
+
+ // Make a new trgrp
+ if (rowSpan) {
+ row['attrs']['origin'] = true
+ }
+
+ delete cell?.['attrs']?.['inducedRowSpan']
+
+ })
+ })
+}
+
+
+function getTbodyChildren (rows: any[]) {
+ const newTbodyChildren = rows.reduce((tBodyChildren, row, rowIndex) => {
+
+ const { included, origin } = row.attrs
+ const l = tBodyChildren.length
+
+ if (included || origin) {
+
+ if (origin && !included) {
+ tBodyChildren.push(jsx('element', { type: 'trgrp' }, row))
+ }
+ if (included) {
+ tBodyChildren[l - 1].children.push(row)
+
+ }
+ delete row['attrs']['included']
+ delete row['attrs']['origin']
+ return tBodyChildren
+ }
+
+ tBodyChildren.push(row)
+ return tBodyChildren
+
+ }, [])
+ return newTbodyChildren
+}
\ No newline at end of file
diff --git a/src/toRedactor.tsx b/src/toRedactor.tsx
index 829a7d8..0be5c64 100644
--- a/src/toRedactor.tsx
+++ b/src/toRedactor.tsx
@@ -80,6 +80,9 @@ const ELEMENT_TYPES: IJsonToHtmlElementTags = {
tr: (attrs: any, child: any) => {
return `${child}
`
},
+ trgrp: (attrs: any, child: any) => {
+ return child
+ },
td: (attrs: any, child: any) => {
return `${child} | `
},
@@ -399,6 +402,15 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
delete allattrs['cols']
delete allattrs['colWidths']
}
+ if (allattrs['disabledCols']) {
+ delete allattrs['disabledCols']
+ }
+ if (allattrs['colSpan']) {
+ delete allattrs['colSpan']
+ }
+ if (allattrs['rowSpan']) {
+ delete allattrs['rowSpan']
+ }
attrsJson = { ...attrsJson, ...allattrs, style: style }
if (jsonValue['type'] === 'reference') {
@@ -482,7 +494,10 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
if (!(setCol.size === 1 && jsonValue.attrs.cols * setCol.values().next().value === totalWidth)) {
let col = ''
Array.from(colWidths).forEach(
- (child, index) => (col += ``)
+ (colWidth, index) => {
+ const width = (colWidth as number / totalWidth) * 100
+ col += ``
+ }
)
let colgroup = `${col}`
children = colgroup + children
@@ -521,6 +536,11 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
return children
}
}
+
+ if(['td','th'].includes(jsonValue['type'])){
+ if(jsonValue?.['attrs']?.['void']) return ''
+ }
+
attrs = (attrs.trim() ? ' ' : '') + attrs.trim()
return ELEMENT_TYPES[orgType || jsonValue['type']](attrs, children,jsonValue, figureStyles)
diff --git a/test/expectedJson.ts b/test/expectedJson.ts
index d2e0986..f2584d0 100644
--- a/test/expectedJson.ts
+++ b/test/expectedJson.ts
@@ -1755,5 +1755,117 @@ export default {
json: {"type":"doc","uid":"uid","attrs":{},"children":[{"type":"hangout-module","attrs":{},"children":[{"type":"hangout-chat","attrs":{"from":"Paul, Addy","nested-json":{"to":"Hello World","more-nesting":{"from":"Beautiful World"}}},"children":[{"type":"hangout-discussion","attrs":{},"children":[{"type":"hangout-message","attrs":{"from":"Paul","profile":"profile.png","datetime":"2013-07-17T12:02"},"children":[{"type":"p","uid":"uid","attrs":{},"children":[{"text":"Feelin' this Web Components thing."}]},{"type":"p","uid":"uid","attrs":{},"children":[{"text":"Heard of it?"}]}]}]}]},{"type":"hangout-chat","attrs":{},"children":[{"text":"Hi There!"}]}]}]},
html : `Feelin' this Web Components thing.
Heard of it?
Hi There!`
},
- ]
+ ],
+ "video-tag": [
+ {
+ json: {"type":"doc","uid":"cfd06695ff85459c90f2d2d4da01af10","attrs":{},"children":[{"type":"embed","attrs":{"src":"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"},"uid":"2c144d07b3a14d8f98c78125b964edcb","children":[{"text":""}]}]},
+ html: ``},
+ {
+ json: {"type":"doc","uid":"00459467e3184fdcab0ba1819f0e3645","attrs":{},"children":[{"type":"embed","attrs":{"src":"https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm","style":{},"redactor-attributes":{"controls":"","width":"250"},"width":250},"uid":"83fb5d7ce52c4f78872d33478bb12f2f","children":[{"text":""}]}]},
+ html: ``,
+ }
+ ],
+ 'table-rowspan-colspan': {
+ html: `
+
+
+
+
+
+
+
+
+
+ h1 |
+
+
+
+
+ 1 |
+ 2 |
+
+
+ 3 |
+ 4 |
+
+
+ 5 |
+ 6 |
+ 7 |
+
+
+ 8 |
+
+
+ 9 |
+ 10 |
+
+
+ 11 |
+ 12 |
+ 13 |
+ 14 |
+
+
+
+ `,
+ expectedJson: {"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]},{"type":"table","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"style":"text-align: center;"},"rows":7,"cols":4,"colWidths":[116.99979767422,109.00003591007999,114.00005388759998,104.9999325281],"disabledCols":[0,1,2,3]},"uid":"uid","children":[{"type":"thead","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"style":"text-align: center;"}},"uid":"uid","children":[{"type":"tr","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"style":"text-align: center;"}},"uid":"uid","children":[{"type":"th","attrs":{"colSpan":4,"style":{"text-align":"center"},"redactor-attributes":{"colspan":"4","style":"text-align: center;"}},"uid":"uid","children":[{"text":"h1"}]},{"type":"th","attrs":{"void":true},"children":[{"text":""}]},{"type":"th","attrs":{"void":true},"children":[{"text":""}]},{"type":"th","attrs":{"void":true},"children":[{"text":""}]}]}]},{"type":"tbody","attrs":{},"uid":"uid","children":[{"type":"trgrp","children":[{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"rowSpan":2,"colSpan":2,"style":{},"redactor-attributes":{"rowspan":"2","colspan":"2"}},"uid":"uid","children":[{"text":"1"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"colSpan":2,"style":{},"redactor-attributes":{"colspan":"2"}},"uid":"uid","children":[{"text":"2"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"3"}]},{"type":"td","attrs":{"rowSpan":4,"style":{},"redactor-attributes":{"rowspan":"4"}},"uid":"uid","children":[{"text":"4"}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"rowSpan":3,"style":{},"redactor-attributes":{"rowspan":"3"}},"uid":"uid","children":[{"text":"5"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"6"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"7"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"colSpan":2,"style":{},"redactor-attributes":{"colspan":"2"}},"uid":"uid","children":[{"text":"8"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"9"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"10"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{},"uid":"uid","children":[{"text":"11"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"12"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"13"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"14"}]}]}]}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]}]}
+ },
+ 'table-rowspan-colspan-2': {
+ html: `
+
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+
+ `,
+ expectedJson: {"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]},{"type":"table","attrs":{"rows":3,"cols":2,"colWidths":[250,250]},"uid":"uid","children":[{"type":"tbody","attrs":{},"uid":"uid","children":[{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{},"uid":"uid","children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":""}]}]},{"type":"trgrp","children":[{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"rowSpan":4,"style":{},"redactor-attributes":{"rowspan":"4"}},"uid":"uid","children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":""}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":""}]}]}]}]}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]}]}
+ },
+ 'table-rowspan-colspan-3': {
+ html: `
+
+
+ Header 1 |
+ Header 2 |
+ Header 3 |
+
+
+
+
+ Row 1, Col 1 |
+ Row 1, Col 2 |
+ Row 1, Col 3 |
+ Row 1, Col 4 |
+
+
+ Row 2, Col 2 |
+ Row 2, Col 3 |
+ Row 2, Col 4 |
+
+
+ Row 3, Col 1 |
+ Row 3, Col 2 |
+ Row 3, Col 5 |
+
+
+ Row 4, Col 1 |
+ Row 4, Col 5 |
+
+
+
+ `,
+ expectedJson: {"type":"doc","uid":"uid","attrs":{},"children":[{"type":"table","attrs":{"style":{},"redactor-attributes":{"border":"1"},"rows":5,"cols":4,"colWidths":[250,250,250,250],"disabledCols":[1,2,3]},"uid":"uid","children":[{"type":"thead","attrs":{},"uid":"uid","children":[{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"th","attrs":{},"uid":"uid","children":[{"text":"Header 1"}]},{"type":"th","attrs":{"colSpan":3,"style":{},"redactor-attributes":{"colspan":"3"}},"uid":"uid","children":[{"text":"Header 2"}]},{"type":"th","attrs":{"void":true},"children":[{"text":""}]},{"type":"th","attrs":{"void":true},"children":[{"text":""}]},{"type":"th","attrs":{},"uid":"uid","children":[{"text":"Header 3"}]}]}]},{"type":"tbody","attrs":{},"uid":"uid","children":[{"type":"trgrp","children":[{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"rowSpan":2,"style":{},"redactor-attributes":{"rowspan":"2"}},"uid":"uid","children":[{"text":"Row 1, Col 1"}]},{"type":"td","attrs":{"colSpan":2,"style":{},"redactor-attributes":{"colspan":"2"}},"uid":"uid","children":[{"text":"Row 1, Col 2"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 1, Col 3"}]},{"type":"td","attrs":{"rowSpan":2,"style":{},"redactor-attributes":{"rowspan":"2"}},"uid":"uid","children":[{"text":"Row 1, Col 4"}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 2, Col 2"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 2, Col 3"}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 2, Col 4"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]}]}]},{"type":"trgrp","children":[{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 3, Col 1"}]},{"type":"td","attrs":{"rowSpan":2,"colSpan":3,"style":{},"redactor-attributes":{"colspan":"3","rowspan":"2"}},"uid":"uid","children":[{"text":"Row 3, Col 2"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 3, Col 5"}]}]},{"type":"tr","attrs":{},"uid":"uid","children":[{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 4, Col 1"}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{"void":true},"children":[{"text":""}]},{"type":"td","attrs":{},"uid":"uid","children":[{"text":"Row 4, Col 5"}]}]}]}]}]}]}
+ },
+
}
\ No newline at end of file
diff --git a/test/fromRedactor.test.ts b/test/fromRedactor.test.ts
index e35ffc1..7efee5c 100644
--- a/test/fromRedactor.test.ts
+++ b/test/fromRedactor.test.ts
@@ -303,5 +303,33 @@ describe("CS-41001", () =>{
const jsonValue = fromRedactor(body);
expect(jsonValue).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"text":"Hello"}]},{"type":"fragment","attrs":{},"uid":"uid","children":[{"text":" beautiful "}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":"World"}]}]})
})
+ test("should convert video tag into embed", () => {
+ expectedValue['video-tag'].forEach((testCase) => {
+ const dom = new JSDOM(testCase.html);
+ let htmlDoc = dom.window.document.querySelector("body");
+ const jsonValue = fromRedactor(htmlDoc);
+ expect(omitdeep(jsonValue, "uid")).toStrictEqual( omitdeep(testCase.json, "uid"))
+ })
+ })
+
+ test('table JSON should have proper structure with rowspan and colspan', () => {
+ const testCases = ['table-rowspan-colspan', 'table-rowspan-colspan-2', 'table-rowspan-colspan-3']
+ testCases.forEach(testCase => {
+ try {
+ const { html, expectedJson } = expectedValue[testCase]
+ const json = htmlToJson(html)
+ expect(json).toStrictEqual(expectedJson)
+ }
+ catch (e) {
+ throw new Error(`Test failed for ${testCase} - ${e}`)
+ }
+ })
+ })
})
+function htmlToJson (html, options) {
+ const dom = new JSDOM(html);
+ let htmlDoc = dom.window.document.querySelector("body");
+ return fromRedactor(htmlDoc, options);
+
+}
\ No newline at end of file
diff --git a/test/toRedactor.test.ts b/test/toRedactor.test.ts
index 38e84bb..df9b87c 100644
--- a/test/toRedactor.test.ts
+++ b/test/toRedactor.test.ts
@@ -223,4 +223,18 @@ describe("Testing json to html conversion", () => {
});
});
});
+
+ test("should convert rowspan and colspan tables to proper html", () => {
+
+ const { html: expectedHtml, expectedJson: json } = {
+ html: ``,
+ expectedJson: [{ "type": "p", "attrs": {}, "uid": "uid", "children": [{ "text": "" }] }, { "type": "table", "attrs": { "style": { "text-align": "center" }, "redactor-attributes": { "style": "text-align: center;" }, "rows": 7, "cols": 4, "colWidths": [116.99979767422, 109.00003591007999, 114.00005388759998, 104.9999325281], "disabledCols": [0, 1, 2, 3] }, "uid": "uid", "children": [{ "type": "thead", "attrs": { "style": { "text-align": "center" }, "redactor-attributes": { "style": "text-align: center;" } }, "uid": "uid", "children": [{ "type": "tr", "attrs": { "style": { "text-align": "center" }, "redactor-attributes": { "style": "text-align: center;" } }, "uid": "uid", "children": [{ "type": "th", "attrs": { "colSpan": 4, "style": { "text-align": "center" }, "redactor-attributes": { "colspan": "4", "style": "text-align: center;" } }, "uid": "uid", "children": [{ "text": "h1" }] }, { "type": "th", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "th", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "th", "attrs": { "void": true }, "children": [{ "text": "" }] }] }] }, { "type": "tbody", "attrs": {}, "uid": "uid", "children": [{ "type": "trgrp", "children": [{ "type": "tr", "attrs": {}, "uid": "uid", "children": [{ "type": "td", "attrs": { "rowSpan": 2, "colSpan": 2, "redactor-attributes": { "rowspan": "2", "colspan": "2" } }, "uid": "uid", "children": [{ "text": "1" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "td", "attrs": { "colSpan": 2, "redactor-attributes": { "colspan": "2" } }, "uid": "uid", "children": [{ "text": "2" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }] }, { "type": "tr", "attrs": {}, "uid": "uid", "children": [{ "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "3" }] }, { "type": "td", "attrs": { "rowSpan": 4, "redactor-attributes": { "rowspan": "4" } }, "uid": "uid", "children": [{ "text": "4" }] }] }, { "type": "tr", "attrs": {}, "uid": "uid", "children": [{ "type": "td", "attrs": { "rowSpan": 3, "redactor-attributes": { "rowspan": "3" } }, "uid": "uid", "children": [{ "text": "5" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "6" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "7" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }] }, { "type": "tr", "attrs": {}, "uid": "uid", "children": [{ "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "td", "attrs": { "colSpan": 2, "redactor-attributes": { "colspan": "2" } }, "uid": "uid", "children": [{ "text": "8" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }] }, { "type": "tr", "attrs": {}, "uid": "uid", "children": [{ "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "9" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "10" }] }, { "type": "td", "attrs": { "void": true }, "children": [{ "text": "" }] }] }] }, { "type": "tr", "attrs": {}, "uid": "uid", "children": [{ "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "11" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "12" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "13" }] }, { "type": "td", "attrs": {}, "uid": "uid", "children": [{ "text": "14" }] }] }] }] }, { "type": "p", "attrs": {}, "uid": "uid", "children": [{ "text": "" }] }]
+ }
+ const html = toRedactor({
+ type: 'docs',
+ children: json
+ })
+ expect(html).toStrictEqual(expectedHtml)
+ })
+
})
\ No newline at end of file