Skip to content
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
5 changes: 5 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ fileignoreconfig:
checksum: cccb3cd93c499acc87593eca5cc032e256c11cf530d4de67ece09e57fc430215
- filename: test/expectedJson.ts
checksum: a1966b0b3993c8e3a0e9e45de49204e7788ba74ba0089a8a6b6eba0729f990bd
- filename: package-lock.json
checksum: 96da2dcdb517a744b09062fad7fbe38f49e4efe5535a3e9e65a94805e7e4808c
- filename: src/toRedactor.tsx
checksum: c6792b5b19cf89024ab33333a77d22af0375d4976c2fc64dae1d2f5971493397
version: "1.0"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024-2025 Contentstack
Copyright (c) 2021-2026 Contentstack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
15 changes: 8 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
},
"dependencies": {
"array-flat-polyfill": "^1.0.1",
"lodash": "^4.17.21",
"lodash": "^4.17.23",
"lodash.clonedeep": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isempty": "^4.4.0",
Expand Down
15 changes: 15 additions & 0 deletions src/fromRedactor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,13 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
}
if (el.style) {
let allStyleAttrs: { [key: string]: any } = {}
const hasIndentLevel = redactor['data-indent-level']
Array.from({ length: el.style.length }).forEach((child, index) => {
let property = el.style.item(index)
// If data-indent-level is present, skip margin-left as indent-level is source of truth
if (hasIndentLevel && kebabCase(property) === 'margin-left') {
return
}
allStyleAttrs[kebabCase(property)] = el.style.getPropertyValue(property)
})
elementAttrs = {
Expand All @@ -476,6 +481,16 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject
attrs: { ...elementAttrs['attrs'], 'data-sys-asset-uid': redactor['data-sys-asset-uid'] }
}
}
// If data-indent-level is present, keep it as a proper attribute (not in redactor-attributes)
// Also delete style from redactor to prevent margin-left from appearing in redactor-attributes
if (redactor['data-indent-level']) {
elementAttrs = {
...elementAttrs,
attrs: { ...elementAttrs['attrs'], 'data-indent-level': redactor['data-indent-level'] }
}
delete redactor['data-indent-level']
delete redactor['style']
}

elementAttrs = { ...elementAttrs, attrs: { ...elementAttrs['attrs'], "redactor-attributes": redactor } }
}
Expand Down
10 changes: 10 additions & 0 deletions src/toRedactor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,20 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
}
if (allattrs['style'] && jsonValue['type'] !== 'img') {
Object.keys(allattrs['style']).forEach((key) => {
// If data-indent-level is present, skip margin-left from style as indent-level is source of truth
if (allattrs['data-indent-level'] && kebbab(key) === 'margin-left') {
return
}
style += `${kebbab(key)}: ${allattrs.style[key]};`
})
delete allattrs['style']
}
if (allattrs['data-indent-level']) {
const indentLevel = Number(allattrs['data-indent-level'])
if (!isNaN(indentLevel) && indentLevel > 0) {
style += `margin-left: ${indentLevel * 30}px;`
}
}
if (allattrs['rows'] && allattrs['cols'] && allattrs['colWidths']) {
delete allattrs['rows']
delete allattrs['cols']
Expand Down
50 changes: 49 additions & 1 deletion test/fromRedactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,58 @@ describe("ELEMENT_TAGS", () => {
})
})

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

}

describe("data-indent-level handling", () => {
test("should keep data-indent-level as a proper attribute, not in redactor-attributes", () => {
const html = `<p data-indent-level="2" style="margin-left: 60px;">Indented paragraph</p>`
const json = htmlToJson(html)

expect(json.children[0].attrs['data-indent-level']).toBe('2')
expect(json.children[0].attrs['redactor-attributes']['data-indent-level']).toBeUndefined()
})

test("should not include margin-left in style when data-indent-level is present", () => {
const html = `<p data-indent-level="2" style="margin-left: 60px; color: red;">Indented paragraph</p>`
const json = htmlToJson(html)

expect(json.children[0].attrs.style['margin-left']).toBeUndefined()
expect(json.children[0].attrs.style['color']).toBe('red')
expect(json.children[0].attrs['data-indent-level']).toBe('2')
})

test("should include margin-left in style when data-indent-level is NOT present", () => {
const html = `<p style="margin-left: 60px; color: blue;">Non-indented paragraph</p>`
const json = htmlToJson(html)

expect(json.children[0].attrs.style['margin-left']).toBe('60px')
expect(json.children[0].attrs.style['color']).toBe('blue')
expect(json.children[0].attrs['data-indent-level']).toBeUndefined()
})

test("should not include style in redactor-attributes when data-indent-level is present", () => {
const html = `<p data-indent-level="3" style="margin-left: 90px;">Deeply indented</p>`
const json = htmlToJson(html)

expect(json.children[0].attrs['redactor-attributes']['style']).toBeUndefined()
expect(json.children[0].attrs['data-indent-level']).toBe('3')
})

test("should handle data-indent-level with multiple style properties correctly", () => {
const html = `<h2 data-indent-level="1" style="margin-left: 30px; text-align: center; font-size: 24px;">Indented heading</h2>`
const json = htmlToJson(html)

expect(json.children[0].attrs['data-indent-level']).toBe('1')
expect(json.children[0].attrs.style['margin-left']).toBeUndefined()
expect(json.children[0].attrs.style['text-align']).toBe('center')
expect(json.children[0].attrs.style['font-size']).toBe('24px')
expect(json.children[0].attrs['redactor-attributes']['data-indent-level']).toBeUndefined()
expect(json.children[0].attrs['redactor-attributes']['style']).toBeUndefined()
})
})

82 changes: 82 additions & 0 deletions test/toRedactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,85 @@ test("should convert codeblock to proper html, where \n should not be replaced w
const html = toRedactor(json);
expect(html).toBe(`<pre>Hi\nHello</pre>`);
})

describe("data-indent-level handling", () => {
test("should generate margin-left based on data-indent-level value", () => {
const json = {
"type": "doc",
"attrs": {},
"children": [{
"type": "p",
"attrs": { "data-indent-level": "2" },
"children": [{ "text": "Indented paragraph" }]
}]
}
const html = toRedactor(json)
expect(html).toBe('<p data-indent-level="2" style="margin-left: 60px;">Indented paragraph</p>')
})

test("should use data-indent-level instead of margin-left from style when both are present", () => {
const json = {
"type": "doc",
"attrs": {},
"children": [{
"type": "p",
"attrs": {
"data-indent-level": "3",
"style": { "margin-left": "100px", "color": "red" }
},
"children": [{ "text": "Indented paragraph" }]
}]
}
const html = toRedactor(json)
// margin-left should be 90px (3 * 30px) not 100px, and color should be preserved
expect(html).toBe('<p data-indent-level="3" style="color: red;margin-left: 90px;">Indented paragraph</p>')
})

test("should preserve margin-left from style when data-indent-level is NOT present", () => {
const json = {
"type": "doc",
"attrs": {},
"children": [{
"type": "p",
"attrs": {
"style": { "margin-left": "100px" }
},
"children": [{ "text": "Non-indented paragraph" }]
}]
}
const html = toRedactor(json)
expect(html).toBe('<p style="margin-left: 100px;">Non-indented paragraph</p>')
})

test("should not add margin-left when data-indent-level is 0", () => {
const json = {
"type": "doc",
"attrs": {},
"children": [{
"type": "p",
"attrs": { "data-indent-level": "0" },
"children": [{ "text": "No indent" }]
}]
}
const html = toRedactor(json)
expect(html).toBe('<p data-indent-level="0">No indent</p>')
})

test("should handle data-indent-level with multiple style properties correctly", () => {
const json = {
"type": "doc",
"attrs": {},
"children": [{
"type": "h2",
"attrs": {
"data-indent-level": "1",
"style": { "margin-left": "50px", "text-align": "center", "font-size": "24px" }
},
"children": [{ "text": "Indented heading" }]
}]
}
const html = toRedactor(json)
// margin-left should be 30px (1 * 30px), other styles should be preserved
expect(html).toBe('<h2 data-indent-level="1" style="text-align: center;font-size: 24px;margin-left: 30px;">Indented heading</h2>')
})
})
Loading