Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development #54

Merged
merged 14 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/json-rte-serializer",
"version": "2.0.8",
"version": "2.0.9",
"description": "This Package converts Html Document to Json and vice-versa.",
"main": "lib/index.js",
"module": "lib/index.mjs",
Expand Down
47 changes: 38 additions & 9 deletions src/fromRedactor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const traverseChildAndModifyChild = (element: any, attrsForChild: any) => {
Array.from(element.children || []).map((el) => traverseChildAndModifyChild(el, attrsForChild)).flat()
return
}
const traverseChildAndWarpChild = (children: Array<Object>) => {
const traverseChildAndWarpChild = (children: Array<Object>, allowNonStandardTags: boolean = false) => {
let inlineElementIndex: Array<number> = []
let hasBlockElement = false
let childrenCopy = cloneDeep(children)
Expand All @@ -164,6 +164,9 @@ const traverseChildAndWarpChild = (children: Array<Object>) => {
} else {
inlineElementIndex.push(index)
}
}
else if (allowNonStandardTags && child?.attrs?.inline) {
inlineElementIndex.push(index)
} else {
hasBlockElement = true
}
Expand Down Expand Up @@ -191,6 +194,8 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
}
if (el.parentNode.nodeName === 'SPAN') {
let attrs = { style: {} }
const metadata = {}

if (el.parentNode.style?.color) {
attrs = {
...attrs,
Expand Down Expand Up @@ -218,7 +223,20 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
}
}
}
return jsx('text', { attrs: attrs }, el.textContent)
if(el.parentNode.getAttribute("id")){
metadata['id'] = el.parentNode.getAttribute("id")
el.parentNode.removeAttribute("id")
}
if(el.parentNode.getAttribute("class")){
metadata['classname'] = el.parentNode.getAttribute("class")
el.parentNode.removeAttribute("class")
}

if(!isEmpty(attrs.style)){
metadata['attrs'] = attrs
}

return jsx('text', metadata, el.textContent)
}
return el.textContent
} else if (el.nodeType !== 1) {
Expand Down Expand Up @@ -264,7 +282,7 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
}
let children: any = flatten(Array.from(parent.childNodes).map((child) => fromRedactor(child, options)))
children = children.filter((child: any) => child !== null)
children = traverseChildAndWarpChild(children)
children = traverseChildAndWarpChild(children, options?.allowNonStandardTags)
if (children.length === 0) {
children = [{ text: '' }]
}
Expand Down Expand Up @@ -482,6 +500,9 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
]
)
}
if(el.parentNode?.nodeName === 'FIGURE'){
return children
}
}

if (ELEMENT_TAGS[nodeName]) {
Expand Down Expand Up @@ -804,7 +825,7 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
}
let noOfInlineElement = 0
Array.from(el.parentNode?.childNodes || []).forEach((child: any) => {
if (child.nodeType === 3 || child.nodeName === 'SPAN' || child.nodeName === 'A') {
if (child.nodeType === 3 || child.nodeName === 'SPAN' || child.nodeName === 'A' || (options?.allowNonStandardTags && child.getAttribute('inline'))) {
noOfInlineElement += 1
}
})
Expand All @@ -817,6 +838,9 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
uid: generateId()
}
}
if (noOfInlineElement === el.parentNode?.childNodes.length && Array.from(el.attributes).length === 0) {
return children
}
}

if (children.length === 0) {
Expand Down Expand Up @@ -856,7 +880,7 @@ const getImageAttributes = (elementAttrs: any, childAttrs: any, extraAttrs: any)
...extraAttrs
},
"asset-caption": extraAttrs["asset-caption"],
"link": extraAttrs.link
"link": extraAttrs.link ?? extraAttrs.anchorLink
}
}
if (elementAttrs?.attrs?.["redactor-attributes"]?.link) {
Expand All @@ -873,11 +897,16 @@ const getImageAttributes = (elementAttrs: any, childAttrs: any, extraAttrs: any)

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

let { style } = elementAttrs.attrs;

extraAttrs['asset-caption'] = extraAttrs['caption'];
if(newChildren[0].attrs.width){
delete sizeAttrs.width
}
const style = {}
if (elementAttrs?.attrs?.style?.['text-align']) {
style['text-align'] = elementAttrs?.attrs?.style?.['text-align']
}

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

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

referenceAttrs.type = "reference";

delete referenceAttrs?.attrs?.['redactor-attributes']?.['anchorlink'];
return referenceAttrs
}

Expand Down
13 changes: 9 additions & 4 deletions src/toRedactor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const ELEMENT_TYPES: IJsonToHtmlElementTags = {
}

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

const caption = jsonBlock?.["attrs"]?.["asset-caption"];
const position = jsonBlock?.["attrs"]?.["position"];
Expand All @@ -134,7 +134,9 @@ const ELEMENT_TYPES: IJsonToHtmlElementTags = {
const figureStyles = {
margin: "0",
};
attrs = ` src="${jsonBlock?.["attrs"]?.["asset-link"]}"` + attrs;
if(!attrs.includes(`src="${jsonBlock?.["attrs"]?.["asset-link"]}`)){
attrs = ` src="${jsonBlock?.["attrs"]?.["asset-link"]}"` + attrs;
}
let img = `<img${attrs}/>`;

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

if (width.slice(width.length - 1) === '%') {
allattrs['width'] = String(allattrs['width'])
} else {
allattrs['width'] = allattrs['width'] + '%'
} else {
allattrs['width'] = String(allattrs['width'])
}
// style = `width: ${allattrs['width']}; height: auto;`
}
Expand Down Expand Up @@ -478,6 +480,9 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
if (jsonValue['type'] === "style") {
delete attrsJson['style-text']
}
if(jsonValue['type'] === 'img'){
attrsJson['src'] = allattrs['url']
}
if(!(options?.customElementTypes && !isEmpty(options.customElementTypes) && options.customElementTypes[jsonValue['type']])) {
delete attrsJson['url']
}
Expand Down
4 changes: 2 additions & 2 deletions test/expectedJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1693,15 +1693,15 @@ export default {
<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/blt858e12437ac2679e/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg" /></a>
<figcaption style="text-align: center;" style="text-align: center;">Landscape</figcaption>
</figure>`,
"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/blt858e12437ac2679e/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/blt858e12437ac2679e/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":""}]}]}
"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/blt858e12437ac2679e/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/blt858e12437ac2679e/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":""}]}]}
},
"anchor-reference-width-position-caption": {
"html":
`<p></p>
<figure style="margin: auto; text-align: center;width: 204px;"><a href="https://app.contentstack.com/"><img src="https://images.contentstack.io/v3/assets/blt858e12437ac2679e/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg" /></a>
<figcaption style="text-align: center;" style="text-align: center;">Landscape</figcaption>
</figure>`,
"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/blt858e12437ac2679e/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/blt858e12437ac2679e/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","width":204,"caption":"Landscape"},"uid":"929929b627704d38ae53ba6792ec8f69","children":[{"text":""}]}]}
"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/blt858e12437ac2679e/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/blt858e12437ac2679e/bltfea8157ddfb8e776/6329f1106a7f7364973c028c/landscape-3.jpg","width":204,"caption":"Landscape","link":"https://app.contentstack.com/"},"children":[{"text":""}]}]}
},
"'\n' to <br>": {
"html": '<p>This is test for break element<br/>This is text on the next line.</p>',
Expand Down
56 changes: 53 additions & 3 deletions test/fromRedactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { JSDOM } from "jsdom"
import isEqual from "lodash.isequal"
import omitdeep from "omit-deep-lodash"
import expectedValue from "./expectedJson"
import { IHtmlToJsonOptions } from "../src/types"

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

test("should not convert stringify attrs when `allowNonStandardTags` is not true", () => {
const html = `<p><span from="Paul, Addy" to="[object Object]">Hi There!</span></p>`;
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"};
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"};

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

describe("SPAN", () => {

test("should properly convert inline properties id and class to json", () => {
let html =`<p dir="ltr">Hello <span class="class" id="id">World</span></p>`
const json = htmlToJson(html)
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"}]}]})
})

test("should skip span if other element are inline and it does not have any attributes", () => {
let html =`<p dir="ltr">Hello <span>World</span></p>`
const json = htmlToJson(html)
expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{"style":{},"redactor-attributes":{"dir":"ltr"}},"uid":"uid","children":[{"text":"Hello "},{"text":"World"}]}]})
})

test("should not skip span if other element are inline and it does have any attribute", () => {
let html =`<p dir="ltr">Hello <span data-test="test">World</span></p>`
const json = htmlToJson(html)
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"}]}]}]})
})

test("should consider the non standard elements as inline if it has attribute of inline with the span tag", () => {
let html = `<p><unknown inline="true"></unknown>Being an absolute <span>tropical</span> stunner</p>`
let jsonValue = htmlToJson(html, { allowNonStandardTags: true })
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"}]}] })
})
})

test("should consider the non standard elements as inline if it has attribute of inline", () => {
let html = `<p><unknown inline="true"></unknown>Being an absolute <a href="https://chess.com">tropical</a> stunner</p>`
let jsonValue = htmlToJson(html, { allowNonStandardTags: true })
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"}]}] })
})


test("should convert asset to reference", () => {
const html = `<figure style="margin: 0; text-align: right">
<div style="display: inline-block"><a href="ss.com" target="_blank"><img src="https://picsum.photos/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="https://picsum.photos/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>
<figcaption style="text-align:center">ss</figcaption>
</div>
</figure>
<p></p>`
const json = htmlToJson(html)
expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"reference","attrs":{"style":{"text-align":"right"},"redactor-attributes":{"src":"https://picsum.photos/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":"https://picsum.photos/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":""}]}] })
})
})


Expand Down Expand Up @@ -327,9 +372,14 @@ describe("CS-41001", () =>{
})
})

function htmlToJson (html, options) {




function htmlToJson (html: string, options: IHtmlToJsonOptions) {
const dom = new JSDOM(html);
let htmlDoc = dom.window.document.querySelector("body");
return fromRedactor(htmlDoc, options);

}
}

Loading
Loading