diff --git a/.talismanrc b/.talismanrc
index a1a6958..a156b3a 100644
--- a/.talismanrc
+++ b/.talismanrc
@@ -3,7 +3,7 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
- checksum: 88174adc8b9dcedecf549defe09988aa4ca95940801e923d829123ba2f3ef6f4
+ checksum: 46eef8560b6a5f38e7a3b55f74525cf47036763b239e33b2458e435651ef6ac0
- filename: src/entry-editable.ts
checksum: 3ba7af9ed1c1adef2e2bd5610099716562bebb8ba750d4b41ddda99fc9eaf115
- filename: .husky/pre-commit
diff --git a/__test__/json-to-html.test.ts b/__test__/json-to-html.test.ts
index de20842..0bd5494 100644
--- a/__test__/json-to-html.test.ts
+++ b/__test__/json-to-html.test.ts
@@ -34,7 +34,11 @@ import {
testJsonRte,
testJsonAsset,
embeddedAssetAsLinkJsonEntry,
- escapeJsonHtml } from './mock/json-element-mock'
+ escapeJsonHtml,
+ breakTestEntry,
+ newlineBreakTestEntry,
+ multipleNewlinesBreakTestEntry,
+ plainNewlineTestEntry } from './mock/json-element-mock'
import {
blockquoteHtml,
codeHtml,
@@ -50,19 +54,23 @@ import {
orderListHtml,
paragraphHtml,
paragraphHtmlWithNewLine,
- plainTextHtml,
- styleinPHtml,
- tableHtml,
+ plainTextHtml,
+ styleinPHtml,
+ tableHtml,
unorderListHtml,
plainTextHtmlWithClass,
plainTextHtmlWithId,
htmlTextIdInAttrs,
classAndIdAttrsHtml,
- styleObjHtml,
+ styleObjHtml,
referenceObjHtml,
referenceObjHtmlBlock,
imagetags,
- escapeHtml } from './mock/json-element-mock-result'
+ escapeHtml,
+ breakTestHtml,
+ newlineBreakTestHtml,
+ multipleNewlinesBreakTestHtml,
+ plainNewlineTestHtml } from './mock/json-element-mock-result'
describe('Node parser paragraph content', () => {
it('Should accept proper values', done => {
const entry = { uid: 'uid'}
@@ -682,4 +690,59 @@ describe('Node parse json_rte Content', () => {
expect(entry.json_rte).toEqual(imagetags)
done()
})
+})
+
+describe('Break and Newline handling tests', () => {
+ it('Should handle break flag in text nodes correctly', done => {
+ const entry = {...breakTestEntry}
+ const paths = ['rich_text_editor']
+
+ jsonToHTML({ entry, paths })
+
+ expect(entry.rich_text_editor).toEqual(breakTestHtml)
+ done()
+ })
+
+ it('Should handle newline with break flag without duplication', done => {
+ const entry = {...newlineBreakTestEntry}
+ const paths = ['rich_text_editor']
+
+ jsonToHTML({ entry, paths })
+
+ expect(entry.rich_text_editor).toEqual(newlineBreakTestHtml)
+ done()
+ })
+
+ it('Should handle multiple newlines with break flag correctly', done => {
+ const entry = {...multipleNewlinesBreakTestEntry}
+ const paths = ['rich_text_editor']
+
+ jsonToHTML({ entry, paths })
+
+ expect(entry.rich_text_editor).toEqual(multipleNewlinesBreakTestHtml)
+ done()
+ })
+
+ it('Should handle plain newlines without break flag', done => {
+ const entry = {...plainNewlineTestEntry}
+ const paths = ['rich_text_editor']
+
+ jsonToHTML({ entry, paths })
+
+ expect(entry.rich_text_editor).toEqual(plainNewlineTestHtml)
+ done()
+ })
+
+ it('Should handle break flag in arrays', done => {
+ const entry = {
+ ...breakTestEntry,
+ rich_text_editor: [breakTestEntry.rich_text_editor]
+ }
+ const paths = ['rich_text_editor']
+
+ jsonToHTML({ entry, paths })
+
+ expect(entry.rich_text_editor).toEqual([breakTestHtml])
+ done()
+ })
})
\ No newline at end of file
diff --git a/__test__/mock/json-element-mock-result.ts b/__test__/mock/json-element-mock-result.ts
index b8aec13..635c955 100644
--- a/__test__/mock/json-element-mock-result.ts
+++ b/__test__/mock/json-element-mock-result.ts
@@ -25,6 +25,10 @@ const referenceObjHtml = "
Embed entry as a link
Embed entry as a link open in new tab
"
const imagetags = "The Batman "
const escapeHtml = "<p>Welcome to Contentstack! <script>console.log(/"Hello from Contentstack!/");</script> Explore our platform to create, manage, and publish content seamlessly.</p>
"
+const breakTestHtml = "Normal text with break tag after break.
"
+const newlineBreakTestHtml = "Text before newline break Text after newline break
"
+const multipleNewlinesBreakTestHtml = "Text before Text after
"
+const plainNewlineTestHtml = "Line 1 Line 2 Line 3
"
export {
h1Html,
@@ -53,5 +57,9 @@ export {
referenceObjHtmlBlock,
imagetags,
paragraphHtmlWithNewLine,
- escapeHtml
+ escapeHtml,
+ breakTestHtml,
+ newlineBreakTestHtml,
+ multipleNewlinesBreakTestHtml,
+ plainNewlineTestHtml
}
\ No newline at end of file
diff --git a/__test__/mock/json-element-mock.ts b/__test__/mock/json-element-mock.ts
index 9a0511a..beb00b6 100644
--- a/__test__/mock/json-element-mock.ts
+++ b/__test__/mock/json-element-mock.ts
@@ -2421,6 +2421,139 @@ const escapeJsonHtml = {
uid: 'asset_uid_10',
}
+const breakTestJson = {
+ uid: "break_test_uid",
+ _version: 1,
+ attrs: {},
+ children: [
+ {
+ type: "p",
+ attrs: {},
+ uid: "break_paragraph_uid",
+ children: [
+ {
+ text: "Normal text with ",
+ },
+ {
+ text: "break tag",
+ break: true
+ },
+ {
+ text: " after break."
+ }
+ ]
+ }
+ ],
+ type: "doc"
+}
+
+const newlineBreakTestJson = {
+ uid: "newline_break_test_uid",
+ _version: 1,
+ attrs: {},
+ children: [
+ {
+ type: "p",
+ attrs: {},
+ uid: "newline_break_paragraph_uid",
+ children: [
+ {
+ text: "Text before newline break",
+ },
+ {
+ text: "\n",
+ break: true
+ },
+ {
+ text: "Text after newline break"
+ }
+ ]
+ }
+ ],
+ type: "doc"
+}
+
+const multipleNewlinesBreakTestJson = {
+ uid: "multiple_newlines_break_test_uid",
+ _version: 1,
+ attrs: {},
+ children: [
+ {
+ type: "p",
+ attrs: {},
+ uid: "multiple_newlines_paragraph_uid",
+ children: [
+ {
+ text: "Text before",
+ },
+ {
+ text: "\n\n\n",
+ break: true
+ },
+ {
+ text: "Text after"
+ }
+ ]
+ }
+ ],
+ type: "doc"
+}
+
+const plainNewlineTestJson = {
+ uid: "plain_newline_test_uid",
+ _version: 1,
+ attrs: {},
+ children: [
+ {
+ type: "p",
+ attrs: {},
+ uid: "plain_newline_paragraph_uid",
+ children: [
+ {
+ text: "Line 1\nLine 2\nLine 3"
+ }
+ ]
+ }
+ ],
+ type: "doc"
+}
+
+const breakTestEntry = {
+ title: 'Break Test Entry',
+ url: '/break-test-entry',
+ rich_text_editor: {...breakTestJson},
+ locale: 'en-us',
+ _in_progress: false,
+ uid: 'break_test_entry_uid',
+}
+
+const newlineBreakTestEntry = {
+ title: 'Newline Break Test Entry',
+ url: '/newline-break-test-entry',
+ rich_text_editor: {...newlineBreakTestJson},
+ locale: 'en-us',
+ _in_progress: false,
+ uid: 'newline_break_test_entry_uid',
+}
+
+const multipleNewlinesBreakTestEntry = {
+ title: 'Multiple Newlines Break Test Entry',
+ url: '/multiple-newlines-break-test-entry',
+ rich_text_editor: {...multipleNewlinesBreakTestJson},
+ locale: 'en-us',
+ _in_progress: false,
+ uid: 'multiple_newlines_break_test_entry_uid',
+}
+
+const plainNewlineTestEntry = {
+ title: 'Plain Newline Test Entry',
+ url: '/plain-newline-test-entry',
+ rich_text_editor: {...plainNewlineTestJson},
+ locale: 'en-us',
+ _in_progress: false,
+ uid: 'plain_newline_test_entry_uid',
+}
+
export {
h1Json,
h2Json,
@@ -2460,5 +2593,13 @@ export {
testJsonRte,
testJsonAsset,
paragraphEntryWithNewline,
- escapeJsonHtml
+ escapeJsonHtml,
+ breakTestJson,
+ newlineBreakTestJson,
+ multipleNewlinesBreakTestJson,
+ plainNewlineTestJson,
+ breakTestEntry,
+ newlineBreakTestEntry,
+ multipleNewlinesBreakTestEntry,
+ plainNewlineTestEntry
}
\ No newline at end of file
diff --git a/__test__/text-node-to-html.test.ts b/__test__/text-node-to-html.test.ts
index e7ac35d..876df5a 100644
--- a/__test__/text-node-to-html.test.ts
+++ b/__test__/text-node-to-html.test.ts
@@ -122,4 +122,94 @@ describe('Text Node To HTML', () => {
expect(resultHtml).toEqual(`${textNode.text} `)
done()
})
+
+ it('Should return Break string text', done => {
+ const node = {
+ ...textNode,
+ break: true
+ }
+
+ const resultHtml = textNodeToHTML(node, {
+ ...defaultNodeOption
+ })
+
+ expect(resultHtml).toEqual(` ${textNode.text}`)
+ done()
+ })
+
+ it('Should handle newline character without break flag', done => {
+ const node = {
+ ...textNode,
+ text: "line1\nline2"
+ }
+
+ const resultHtml = textNodeToHTML(node, {
+ ...defaultNodeOption
+ })
+
+ expect(resultHtml).toEqual('line1 line2')
+ done()
+ })
+
+ it('Should handle single newline with break flag without duplication', done => {
+ const node = {
+ ...textNode,
+ text: "\n",
+ break: true
+ }
+
+ const resultHtml = textNodeToHTML(node, {
+ ...defaultNodeOption
+ })
+
+ expect(resultHtml).toEqual(' ')
+ done()
+ })
+
+ it('Should handle multiple newlines with break flag without duplication', done => {
+ const node = {
+ ...textNode,
+ text: "\n\n\n",
+ break: true
+ }
+
+ const resultHtml = textNodeToHTML(node, {
+ ...defaultNodeOption
+ })
+
+ expect(resultHtml).toEqual(' ')
+ done()
+ })
+
+ it('Should handle text with newline and break flag properly', done => {
+ const node = {
+ ...textNode,
+ text: "text with\nnewline",
+ break: true
+ }
+
+ const resultHtml = textNodeToHTML(node, {
+ ...defaultNodeOption
+ })
+
+ expect(resultHtml).toEqual(' text with newline')
+ done()
+ })
+
+ it('Should handle break with other marks', done => {
+ const node = {
+ ...textNode,
+ text: "break text",
+ break: true,
+ bold: true,
+ italic: true
+ }
+
+ const resultHtml = textNodeToHTML(node, {
+ ...defaultNodeOption
+ })
+
+ expect(resultHtml).toEqual(` ${node.text} `)
+ done()
+ })
})
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 3200810..ac565c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@contentstack/utils",
- "version": "1.4.3",
+ "version": "1.4.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@contentstack/utils",
- "version": "1.4.3",
+ "version": "1.4.4",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.26.0",
@@ -3937,7 +3937,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5392,7 +5394,9 @@
}
},
"node_modules/filelist/node_modules/brace-expansion": {
- "version": "2.0.1",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5499,13 +5503,16 @@
"license": "ISC"
},
"node_modules/form-data": {
- "version": "4.0.2",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
diff --git a/package.json b/package.json
index b54cbae..d9874da 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@contentstack/utils",
- "version": "1.4.3",
+ "version": "1.4.4",
"description": "Contentstack utilities for Javascript",
"main": "dist/index.es.js",
"types": "dist/types/index.d.ts",
diff --git a/src/helper/enumerate-entries.ts b/src/helper/enumerate-entries.ts
index ba688f9..122b135 100644
--- a/src/helper/enumerate-entries.ts
+++ b/src/helper/enumerate-entries.ts
@@ -43,33 +43,53 @@ export function enumerateContents(
export function textNodeToHTML(node: TextNode, renderOption: RenderOption): string {
let text = replaceHtmlEntities(node.text);
+
+ // Convert newlines to tags if there are no other marks
+ // This ensures newlines are always handled consistently
+ let hasMarks = false;
+
if (node.classname || node.id) {
text = (renderOption[MarkType.CLASSNAME_OR_ID] as RenderMark)(text, node.classname, node.id);
+ hasMarks = true;
}
if (node.break) {
text = (renderOption[MarkType.BREAK] as RenderMark)(text);
+ hasMarks = true;
}
if (node.superscript) {
text = (renderOption[MarkType.SUPERSCRIPT] as RenderMark)(text);
+ hasMarks = true;
}
if (node.subscript) {
text = (renderOption[MarkType.SUBSCRIPT] as RenderMark)(text);
+ hasMarks = true;
}
if (node.inlineCode) {
text = (renderOption[MarkType.INLINE_CODE] as RenderMark)(text);
+ hasMarks = true;
}
if (node.strikethrough) {
text = (renderOption[MarkType.STRIKE_THROUGH] as RenderMark)(text);
+ hasMarks = true;
}
if (node.underline) {
text = (renderOption[MarkType.UNDERLINE] as RenderMark)(text);
+ hasMarks = true;
}
if (node.italic) {
text = (renderOption[MarkType.ITALIC] as RenderMark)(text);
+ hasMarks = true;
}
if (node.bold) {
text = (renderOption[MarkType.BOLD] as RenderMark)(text);
+ hasMarks = true;
}
+
+ // If no marks were applied, but text contains newlines, convert them to
+ if (!hasMarks && text.includes('\n')) {
+ text = text.replace(/\n/g, ' ');
+ }
+
return text;
}
export function referenceToHTML(
diff --git a/src/options/default-node-options.ts b/src/options/default-node-options.ts
index 27afbe5..3f19f37 100644
--- a/src/options/default-node-options.ts
+++ b/src/options/default-node-options.ts
@@ -179,6 +179,12 @@ export const defaultNodeOption: RenderOption = {
return `${sanitizeHTML(text)} `
},
[MarkType.BREAK]:(text: string) => {
+ // Check if text is only newlines (which will be converted to by sanitizeHTML)
+ // If so, don't add an extra to avoid duplication
+ const onlyNewlines = /^\n+$/.test(text);
+ if (onlyNewlines) {
+ return sanitizeHTML(text);
+ }
return ` ${sanitizeHTML(text)}`
},
[MarkType.CLASSNAME_OR_ID]:(text: string, classname: string, id:string) => {