Skip to content

Commit dc66fc8

Browse files
authored
Merge pull request #54 from contentstack/development
Development
2 parents 66df6d0 + 8c8aa7d commit dc66fc8

File tree

6 files changed

+130
-52
lines changed

6 files changed

+130
-52
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/json-rte-serializer",
3-
"version": "2.0.8",
3+
"version": "2.0.9",
44
"description": "This Package converts Html Document to Json and vice-versa.",
55
"main": "lib/index.js",
66
"module": "lib/index.mjs",

src/fromRedactor.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ const traverseChildAndModifyChild = (element: any, attrsForChild: any) => {
144144
Array.from(element.children || []).map((el) => traverseChildAndModifyChild(el, attrsForChild)).flat()
145145
return
146146
}
147-
const traverseChildAndWarpChild = (children: Array<Object>) => {
147+
const traverseChildAndWarpChild = (children: Array<Object>, allowNonStandardTags: boolean = false) => {
148148
let inlineElementIndex: Array<number> = []
149149
let hasBlockElement = false
150150
let childrenCopy = cloneDeep(children)
@@ -164,6 +164,9 @@ const traverseChildAndWarpChild = (children: Array<Object>) => {
164164
} else {
165165
inlineElementIndex.push(index)
166166
}
167+
}
168+
else if (allowNonStandardTags && child?.attrs?.inline) {
169+
inlineElementIndex.push(index)
167170
} else {
168171
hasBlockElement = true
169172
}
@@ -191,6 +194,8 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
191194
}
192195
if (el.parentNode.nodeName === 'SPAN') {
193196
let attrs = { style: {} }
197+
const metadata = {}
198+
194199
if (el.parentNode.style?.color) {
195200
attrs = {
196201
...attrs,
@@ -218,7 +223,20 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
218223
}
219224
}
220225
}
221-
return jsx('text', { attrs: attrs }, el.textContent)
226+
if(el.parentNode.getAttribute("id")){
227+
metadata['id'] = el.parentNode.getAttribute("id")
228+
el.parentNode.removeAttribute("id")
229+
}
230+
if(el.parentNode.getAttribute("class")){
231+
metadata['classname'] = el.parentNode.getAttribute("class")
232+
el.parentNode.removeAttribute("class")
233+
}
234+
235+
if(!isEmpty(attrs.style)){
236+
metadata['attrs'] = attrs
237+
}
238+
239+
return jsx('text', metadata, el.textContent)
222240
}
223241
return el.textContent
224242
} else if (el.nodeType !== 1) {
@@ -264,7 +282,7 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
264282
}
265283
let children: any = flatten(Array.from(parent.childNodes).map((child) => fromRedactor(child, options)))
266284
children = children.filter((child: any) => child !== null)
267-
children = traverseChildAndWarpChild(children)
285+
children = traverseChildAndWarpChild(children, options?.allowNonStandardTags)
268286
if (children.length === 0) {
269287
children = [{ text: '' }]
270288
}
@@ -482,6 +500,9 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
482500
]
483501
)
484502
}
503+
if(el.parentNode?.nodeName === 'FIGURE'){
504+
return children
505+
}
485506
}
486507

487508
if (ELEMENT_TAGS[nodeName]) {
@@ -804,7 +825,7 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
804825
}
805826
let noOfInlineElement = 0
806827
Array.from(el.parentNode?.childNodes || []).forEach((child: any) => {
807-
if (child.nodeType === 3 || child.nodeName === 'SPAN' || child.nodeName === 'A') {
828+
if (child.nodeType === 3 || child.nodeName === 'SPAN' || child.nodeName === 'A' || (options?.allowNonStandardTags && child.getAttribute('inline'))) {
808829
noOfInlineElement += 1
809830
}
810831
})
@@ -817,6 +838,9 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
817838
uid: generateId()
818839
}
819840
}
841+
if (noOfInlineElement === el.parentNode?.childNodes.length && Array.from(el.attributes).length === 0) {
842+
return children
843+
}
820844
}
821845

822846
if (children.length === 0) {
@@ -856,7 +880,7 @@ const getImageAttributes = (elementAttrs: any, childAttrs: any, extraAttrs: any)
856880
...extraAttrs
857881
},
858882
"asset-caption": extraAttrs["asset-caption"],
859-
"link": extraAttrs.link
883+
"link": extraAttrs.link ?? extraAttrs.anchorLink
860884
}
861885
}
862886
if (elementAttrs?.attrs?.["redactor-attributes"]?.link) {
@@ -873,11 +897,16 @@ const getImageAttributes = (elementAttrs: any, childAttrs: any, extraAttrs: any)
873897

874898
const getReferenceAttributes = ({elementAttrs, newChildren, extraAttrs, sizeAttrs} : any) => {
875899

876-
let { style } = elementAttrs.attrs;
877-
878900
extraAttrs['asset-caption'] = extraAttrs['caption'];
901+
if(newChildren[0].attrs.width){
902+
delete sizeAttrs.width
903+
}
904+
const style = {}
905+
if (elementAttrs?.attrs?.style?.['text-align']) {
906+
style['text-align'] = elementAttrs?.attrs?.style?.['text-align']
907+
}
879908

880-
const childAttrs = { ...newChildren[0].attrs, ...sizeAttrs, style: { 'text-align': style['text-align'] }, position: extraAttrs.position }
909+
const childAttrs = { ...newChildren[0].attrs, ...sizeAttrs, style , position: extraAttrs.position }
881910
extraAttrs = { ...extraAttrs, ...sizeAttrs }
882911

883912
if (!childAttrs.position) {
@@ -887,7 +916,7 @@ const getReferenceAttributes = ({elementAttrs, newChildren, extraAttrs, sizeAttr
887916
const referenceAttrs = getImageAttributes(elementAttrs, childAttrs, extraAttrs);
888917

889918
referenceAttrs.type = "reference";
890-
919+
delete referenceAttrs?.attrs?.['redactor-attributes']?.['anchorlink'];
891920
return referenceAttrs
892921
}
893922

src/toRedactor.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ const ELEMENT_TYPES: IJsonToHtmlElementTags = {
125125
}
126126

127127
else if (extraAttrs?.displayType === "display") {
128-
const anchor = jsonBlock?.["attrs"]?.["link"];
128+
const anchor = jsonBlock?.["attrs"]?.["link"] ?? jsonBlock?.["attrs"]?.["anchorLink"];
129129

130130
const caption = jsonBlock?.["attrs"]?.["asset-caption"];
131131
const position = jsonBlock?.["attrs"]?.["position"];
@@ -134,7 +134,9 @@ const ELEMENT_TYPES: IJsonToHtmlElementTags = {
134134
const figureStyles = {
135135
margin: "0",
136136
};
137-
attrs = ` src="${jsonBlock?.["attrs"]?.["asset-link"]}"` + attrs;
137+
if(!attrs.includes(`src="${jsonBlock?.["attrs"]?.["asset-link"]}`)){
138+
attrs = ` src="${jsonBlock?.["attrs"]?.["asset-link"]}"` + attrs;
139+
}
138140
let img = `<img${attrs}/>`;
139141

140142
if (anchor) {
@@ -384,9 +386,9 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
384386
let width = String(allattrs['width'])
385387

386388
if (width.slice(width.length - 1) === '%') {
387-
allattrs['width'] = String(allattrs['width'])
388-
} else {
389389
allattrs['width'] = allattrs['width'] + '%'
390+
} else {
391+
allattrs['width'] = String(allattrs['width'])
390392
}
391393
// style = `width: ${allattrs['width']}; height: auto;`
392394
}
@@ -478,6 +480,9 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
478480
if (jsonValue['type'] === "style") {
479481
delete attrsJson['style-text']
480482
}
483+
if(jsonValue['type'] === 'img'){
484+
attrsJson['src'] = allattrs['url']
485+
}
481486
if(!(options?.customElementTypes && !isEmpty(options.customElementTypes) && options.customElementTypes[jsonValue['type']])) {
482487
delete attrsJson['url']
483488
}

test/expectedJson.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,15 +1693,15 @@ export default {
16931693
<figure style="margin: auto; text-align: center;width: 204px;"><a href="https://app.contentstack.com/"><img asset_uid="bltfea8157ddfb8e776" src="https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg" /></a>
16941694
<figcaption style="text-align: center;" style="text-align: center;">Landscape</figcaption>
16951695
</figure>`,
1696-
"json": {"type":"doc","uid":"d712f7746e984559bbf591d689ef69f6","attrs":{},"children":[{"type":"p","attrs":{},"uid":"79d6defd99624ecc8302db0479330d72","children":[{"text":""}]},{"type":"reference","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"asset_uid":"bltfea8157ddfb8e776","src":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","position":"center","captionAttrs":{"style":"text-align: center;"},"caption":"Landscape","anchorLink":"https://app.contentstack.com/","asset-caption":"Landscape","width":204},"asset-name":"landscape-3.jpg","content-type-uid":"sys_assets","asset-link":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","asset-type":"image/jpg","display-type":"display","type":"asset","asset-uid":"bltfea8157ddfb8e776","width":204,"position":"center","asset-caption":"Landscape"},"uid":"abc8e6a7f2974ad2a876356a6f4ae0fb","children":[{"text":""}]}]}
1696+
"json": {"type":"doc","uid":"d712f7746e984559bbf591d689ef69f6","attrs":{},"children":[{"type":"p","attrs":{},"uid":"79d6defd99624ecc8302db0479330d72","children":[{"text":""}]},{"type":"reference","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"asset_uid":"bltfea8157ddfb8e776","src":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","position":"center","captionAttrs":{"style":"text-align: center;"},"caption":"Landscape","anchorLink":"https://app.contentstack.com/","asset-caption":"Landscape","width":204},"link": "https://app.contentstack.com/","asset-name":"landscape-3.jpg","content-type-uid":"sys_assets","asset-link":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","asset-type":"image/jpg","display-type":"display","type":"asset","asset-uid":"bltfea8157ddfb8e776","width":204,"position":"center","asset-caption":"Landscape"},"uid":"abc8e6a7f2974ad2a876356a6f4ae0fb","children":[{"text":""}]}]}
16971697
},
16981698
"anchor-reference-width-position-caption": {
16991699
"html":
17001700
`<p></p>
17011701
<figure style="margin: auto; text-align: center;width: 204px;"><a href="https://app.contentstack.com/"><img src="https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg" /></a>
17021702
<figcaption style="text-align: center;" style="text-align: center;">Landscape</figcaption>
17031703
</figure>`,
1704-
"json": {"type":"doc","uid":"d6cd7b938dcc41a8a75fb8bad29aa2e9","attrs":{},"children":[{"type":"p","attrs":{},"uid":"c17f2b982464422aaa58499b9525b437","children":[{"text":""}]},{"type":"img","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"src":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","position":"center","captionAttrs":{"style":"text-align: center;"},"caption":"Landscape","anchorLink":"https://app.contentstack.com/","width":204},"url":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","width":204,"caption":"Landscape"},"uid":"929929b627704d38ae53ba6792ec8f69","children":[{"text":""}]}]}
1704+
"json": {"type":"doc","uid":"d6cd7b938dcc41a8a75fb8bad29aa2e9","attrs":{},"children":[{"type":"p","attrs":{},"uid":"c17f2b982464422aaa58499b9525b437","children":[{"text":""}]},{"type":"img","attrs":{"style":{"text-align":"center"},"redactor-attributes":{"src":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","position":"center","captionAttrs":{"style":"text-align: center;"},"caption":"Landscape","anchorLink":"https://app.contentstack.com/","width":204},"url":"https://images.contentstack.io/v3/assets/***REMOVED***/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","width":204,"caption":"Landscape","link":"https://app.contentstack.com/"},"children":[{"text":""}]}]}
17051705
},
17061706
"'\n' to <br>": {
17071707
"html": '<p>This is test for break element<br/>This is text on the next line.</p>',

test/fromRedactor.test.ts

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { JSDOM } from "jsdom"
44
import isEqual from "lodash.isequal"
55
import omitdeep from "omit-deep-lodash"
66
import expectedValue from "./expectedJson"
7+
import { IHtmlToJsonOptions } from "../src/types"
78

89
const docWrapper = (children: any) => {
910
return {
@@ -241,7 +242,7 @@ describe("Testing html to json conversion", () => {
241242

242243
test("should not convert stringify attrs when `allowNonStandardTags` is not true", () => {
243244
const html = `<p><span from="Paul, Addy" to="[object Object]">Hi There!</span></p>`;
244-
const json = {"attrs": {}, "children": [{"attrs": {}, "children": [{"attrs": {"redactor-attributes": {"from": "Paul, Addy", "to": "[object Object]"}, "style": {}}, "children": [{"attrs": {"style": {}}, "text": "Hi There!"}], "type": "span", "uid": "uid"}], "type": "p", "uid": "uid"}], "type": "doc", "uid": "uid"};
245+
const json = {"attrs": {}, "children": [{"attrs": {}, "children": [{"attrs": {"redactor-attributes": {"from": "Paul, Addy", "to": "[object Object]"}, "style": {}}, "children": [{"text": "Hi There!"}], "type": "span", "uid": "uid"}], "type": "p", "uid": "uid"}], "type": "doc", "uid": "uid"};
245246

246247
const dom = new JSDOM(html);
247248
let htmlDoc = dom.window.document.querySelector("body");
@@ -250,6 +251,50 @@ describe("Testing html to json conversion", () => {
250251
});
251252
})
252253

254+
describe("SPAN", () => {
255+
256+
test("should properly convert inline properties id and class to json", () => {
257+
let html =`<p dir="ltr">Hello <span class="class" id="id">World</span></p>`
258+
const json = htmlToJson(html)
259+
expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{"style":{},"redactor-attributes":{"dir":"ltr"}},"uid":"uid","children":[{"text":"Hello "},{"text":"World","id":"id","classname":"class"}]}]})
260+
})
261+
262+
test("should skip span if other element are inline and it does not have any attributes", () => {
263+
let html =`<p dir="ltr">Hello <span>World</span></p>`
264+
const json = htmlToJson(html)
265+
expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{"style":{},"redactor-attributes":{"dir":"ltr"}},"uid":"uid","children":[{"text":"Hello "},{"text":"World"}]}]})
266+
})
267+
268+
test("should not skip span if other element are inline and it does have any attribute", () => {
269+
let html =`<p dir="ltr">Hello <span data-test="test">World</span></p>`
270+
const json = htmlToJson(html)
271+
expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{"style":{},"redactor-attributes":{"dir":"ltr"}},"uid":"uid","children":[{"text":"Hello "},{"type":"span","attrs":{"style":{},"redactor-attributes":{"data-test":"test"}},"uid":"uid","children":[{"text":"World"}]}]}]})
272+
})
273+
274+
test("should consider the non standard elements as inline if it has attribute of inline with the span tag", () => {
275+
let html = `<p><unknown inline="true"></unknown>Being an absolute <span>tropical</span> stunner</p>`
276+
let jsonValue = htmlToJson(html, { allowNonStandardTags: true })
277+
expect(jsonValue).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"type":"unknown","attrs":{"inline":"true"},"children":[{"text":""}]},{"text":"Being an absolute "},{"text":"tropical"},{"text":" stunner"}]}] })
278+
})
279+
})
280+
281+
test("should consider the non standard elements as inline if it has attribute of inline", () => {
282+
let html = `<p><unknown inline="true"></unknown>Being an absolute <a href="https://chess.com">tropical</a> stunner</p>`
283+
let jsonValue = htmlToJson(html, { allowNonStandardTags: true })
284+
expect(jsonValue).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"type":"unknown","attrs":{"inline":"true"},"children":[{"text":""}]},{"text":"Being an absolute "},{"type":"a","attrs":{"url":"https://chess.com","style":{},"redactor-attributes":{"href":"https://chess.com"}},"uid":"uid","children":[{"text":"tropical"}]},{"text":" stunner"}]}] })
285+
})
286+
287+
288+
test("should convert asset to reference", () => {
289+
const html = `<figure style="margin: 0; text-align: right">
290+
<div style="display: inline-block"><a href="ss.com" target="_blank"><img src="***REMOVED***200" height="141" alt="image_(9).png" caption="ss" anchorLink="ss.com" class="embedded-asset" content-type-uid="sys_assets" type="asset" asset-alt="image_(9).png" width="148" max-height="141" max-width="148" style="max-height: 141px; height: 141px; text-align: right; max-width: 148px; width: auto" data-sys-asset-filelink="***REMOVED***200" data-sys-asset-uid="blt137d845621ef8168" data-sys-asset-filename="image_(9).png" data-sys-asset-contenttype="image/png" data-sys-asset-caption="ss" data-sys-asset-alt="image_(9).png" data-sys-asset-link="ss.com" data-sys-asset-position="right" data-sys-asset-isnewtab="true" sys-style-type="display" /></a>
291+
<figcaption style="text-align:center">ss</figcaption>
292+
</div>
293+
</figure>
294+
<p></p>`
295+
const json = htmlToJson(html)
296+
expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"reference","attrs":{"style":{"text-align":"right"},"redactor-attributes":{"src":"***REMOVED***200","height":"141","alt":"image_(9).png","caption":"ss","type":"asset","asset-alt":"image_(9).png","max-height":"141","max-width":"148","sys-style-type":"display","position":"right","captionAttrs":{"style":"text-align:center"},"anchorLink":"ss.com","target":true,"asset-caption":"ss"},"class-name":"embedded-asset","width":148,"type":"asset","asset-caption":"ss","link":"ss.com","asset-alt":"image_(9).png","target":"_blank","position":"right","asset-link":"***REMOVED***200","asset-uid":"blt137d845621ef8168","display-type":"display","asset-name":"image_(9).png","asset-type":"image/png","content-type-uid":"sys_assets"},"uid":"uid","children":[{"text":""}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]}] })
297+
})
253298
})
254299

255300

@@ -327,9 +372,14 @@ describe("CS-41001", () =>{
327372
})
328373
})
329374

330-
function htmlToJson (html, options) {
375+
376+
377+
378+
379+
function htmlToJson (html: string, options: IHtmlToJsonOptions) {
331380
const dom = new JSDOM(html);
332381
let htmlDoc = dom.window.document.querySelector("body");
333382
return fromRedactor(htmlDoc, options);
334383

335-
}
384+
}
385+

0 commit comments

Comments
 (0)