diff --git a/.talismanrc b/.talismanrc index d04c06a..baebc93 100644 --- a/.talismanrc +++ b/.talismanrc @@ -24,3 +24,7 @@ fileignoreconfig: checksum: 3d014702628ad538065c970d988a695af003c61663596a8f6b9267b4e57ef6ea - filename: test/expectedJson.json checksum: 9979f84be3e5aa27f24381a0c49e0e6696388d19615c4f3b09082780968236ee +- filename: README.md + checksum: cccb3cd93c499acc87593eca5cc032e256c11cf530d4de67ece09e57fc430215 +- filename: test/expectedJson.ts + checksum: a1966b0b3993c8e3a0e9e45de49204e7788ba74ba0089a8a6b6eba0729f990bd diff --git a/README.md b/README.md index 55df54b..49bdad2 100644 --- a/README.md +++ b/README.md @@ -161,12 +161,27 @@ On the other hand, the `customTextWrapper` parser function provides the followin - `child`: The HTML string that specifies the child element - `value`: The value passed against the child element +___ -You can pass an object to `allowedEmptyAttributes` to retain empty attribute values for specific element types during HTML conversion. + `allowedEmptyAttributes` -**Note:** -By default, if nothing is passed to `allowedEmptyAttributes`, we retain the `alt` attribute for `` and `reference` (asset) element types, even when its value is empty, during HTML conversion. +- Type: `object` +- Default: `{ img: ['alt'], reference: ['alt'] }` +Specifies which empty attributes should be retained for specific HTML elements during the jsonToHtml conversion. +By default, the converter preserves the alt attribute for and reference (asset) elements, even when their values are empty. +This is particularly useful for ensuring semantic correctness and accessibility. + +Use this option when you want to retain specific attributes with empty values during the conversion process. + +___ + + `addNbspForEmptyBlocks` + +- Type: `boolean` +- Default:`false` + +When set to true, this option adds a non-breaking space (nbsp;) to empty blocks during the jsonToHtml conversion. This helps maintain the visual structure of the HTML output—especially useful for preserving spacing in editable content or content editors. You can use the following customized JSON RTE Serializer code to convert your JSON RTE field data into HTML format. @@ -196,6 +211,16 @@ const jsonValue = { }, ], }, + { + "type": "p", + "uid": "28c837c127504d3c85b9cb6d7099cb0b", + "attrs": {}, + "children": [ + { + "text": "" + } + ] + }, { type: "p", attrs: {}, @@ -225,7 +250,8 @@ const htmlValue = jsonToHtml( allowedEmptyAttributes : { "p": ["dir"], "img" : ["width"] - } + }, + addNbspForEmptyBlocks : true } ); diff --git a/package.json b/package.json index e353a44..2314fe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/json-rte-serializer", - "version": "3.0.1", + "version": "3.0.2", "description": "This Package converts Html Document to Json and vice-versa.", "main": "lib/index.js", "module": "lib/index.mjs", diff --git a/src/fromRedactor.tsx b/src/fromRedactor.tsx index da4ef56..07021ff 100644 --- a/src/fromRedactor.tsx +++ b/src/fromRedactor.tsx @@ -20,12 +20,16 @@ export const ELEMENT_TAGS: IHtmlToJsonElementTags = { const attrs: Record = {} const target = el.getAttribute('target'); const href = el.getAttribute('href'); + const title = el.getAttribute('title'); attrs.url = href ? href : '#'; if(target && target !== '') { attrs.target = target; } + if(title && title !== '') { + attrs.title = title; + } return { type: "a", @@ -206,6 +210,9 @@ export const fromRedactor = (el: any, options?:IHtmlToJsonOptions) : IAnyObject if (el.textContent === '\n') { return null } + if (options?.addNbspForEmptyBlocks && el.textContent.trim() === '') { + return { text: '' } + } if (el.parentNode.nodeName === 'SPAN') { let attrs = { style: {} } const metadata = {} @@ -297,6 +304,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, options?.allowNonStandardTags) + if (children.length === 0) { children = [{ text: '' }] } diff --git a/src/jsonToMarkdown.tsx b/src/jsonToMarkdown.tsx index 1a7b84b..3ec4f03 100644 --- a/src/jsonToMarkdown.tsx +++ b/src/jsonToMarkdown.tsx @@ -14,32 +14,32 @@ const ELEMENT_TYPES: IJsonToMarkdownElementTags = { 'h1': (attrs: any, child: string) => { return ` -#${child}#` +# ${child} #` }, 'h2': (attrs: any, child: any) => { return ` -##${child}##` +## ${child} ##` }, 'h3': (attrs: any, child: any) => { return ` -###${child}###` +### ${child} ###` }, 'h4': (attrs: any, child: any) => { return ` -####${child}####` +#### ${child} ####` }, 'h5': (attrs: any, child: any) => { return ` -#####${child}#####` +##### ${child} #####` }, 'h6': (attrs: any, child: any) => { return ` -######${child}######` +###### ${child} ######` }, img: (attrsJson: any, child: any) => { if(attrsJson) { diff --git a/src/toRedactor.tsx b/src/toRedactor.tsx index 0cdfc24..4d39395 100644 --- a/src/toRedactor.tsx +++ b/src/toRedactor.tsx @@ -218,8 +218,13 @@ const ALLOWED_EMPTY_ATTRIBUTES: IJsonToHtmlAllowedEmptyAttributes = { reference: ['alt'] } +let ADD_NBSP_FOR_EMPTY_BLOCKS : boolean = false + export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string => { //TODO: optimize assign once per function call + if(options?.addNbspForEmptyBlocks){ + ADD_NBSP_FOR_EMPTY_BLOCKS = options?.addNbspForEmptyBlocks + } if(options?.customTextWrapper && !isEmpty(options.customTextWrapper)){ Object.assign(TEXT_WRAPPERS,options.customTextWrapper) } @@ -590,7 +595,12 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string attrs = (attrs.trim() ? ' ' : '') + attrs.trim() - return ELEMENT_TYPES[orgType || jsonValue['type']](attrs, children,jsonValue, figureStyles) + return ELEMENT_TYPES[orgType || jsonValue['type']]( + attrs, + ADD_NBSP_FOR_EMPTY_BLOCKS && !children ? ' ' : children, + jsonValue, + figureStyles + ) } return children diff --git a/src/types.ts b/src/types.ts index e531ecf..11404b4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,7 +2,8 @@ export interface IAnyObject {[key:string]:any} export interface IHtmlToJsonOptions { allowNonStandardTags?: boolean, customElementTags?: IHtmlToJsonElementTags, - customTextTags?: IHtmlToJsonTextTags + customTextTags?: IHtmlToJsonTextTags, + addNbspForEmptyBlocks?: boolean } export interface IHtmlToJsonElementTagsAttributes { type:string, @@ -22,4 +23,5 @@ export interface IJsonToHtmlOptions { customTextWrapper?: IJsonToHtmlTextTags, allowNonStandardTypes?: boolean, allowedEmptyAttributes?: IJsonToHtmlAllowedEmptyAttributes, + addNbspForEmptyBlocks?: boolean } diff --git a/test/expectedJson.ts b/test/expectedJson.ts index 57f2a8a..796a240 100644 --- a/test/expectedJson.ts +++ b/test/expectedJson.ts @@ -2416,6 +2416,77 @@ export default { "_version": 2 } ] + }, + "RT-501":{ + "html" : [ + `

ABC

` + ], + "json" : [ + { + "type": "doc", + "attrs": {}, + "uid": "bd63f151aa8d402cae046c8dae440134", + "children": [ + { + "type": "p", + "uid": "d2949ce0e0974ce783543edd37410c71", + "attrs": {}, + "children": [ + { + "uid": "7a2fd904668447ca8720428cbd2b0acc", + "type": "a", + "attrs": { + "url": "google.in", + "target": "_blank", + "title": "google" + }, + "children": [ + { + "text": "ABC" + } + ] + } + ] + } + ], + } + ], + "jsonWithRedactorAttributes": [ + { + "type": "doc", + "attrs": {}, + "uid": "bd63f151aa8d402cae046c8dae440134", + "children": [ + { + "type": "p", + "uid": "d2949ce0e0974ce783543edd37410c71", + "attrs": {}, + "children": [ + { + "uid": "7a2fd904668447ca8720428cbd2b0acc", + "type": "a", + "attrs": { + "url": "google.in", + "target": "_blank", + "title": "google", + "style": {}, + "redactor-attributes": { + "href": "google.in", + "target": "_blank", + "title": "google" + } + }, + "children": [ + { + "text": "ABC" + } + ] + } + ] + } + ] + } + ] } } \ No newline at end of file diff --git a/test/expectedMarkdown.ts b/test/expectedMarkdown.ts index 4baf5cb..ab701d5 100644 --- a/test/expectedMarkdown.ts +++ b/test/expectedMarkdown.ts @@ -163,17 +163,17 @@ This is a paragraph with \`inline code\`.` }], "markdown": ` -#Heading 1# +# Heading 1 # -##Heading 2## +## Heading 2 ## -###Heading 3### +### Heading 3 ### -####Heading 4#### +#### Heading 4 #### -#####Heading 5##### +##### Heading 5 ##### -######Heading 6######` +###### Heading 6 ######` }, { "title": "Block Quote Conversion", diff --git a/test/fromRedactor.test.ts b/test/fromRedactor.test.ts index 5c1fa63..a145ad7 100644 --- a/test/fromRedactor.test.ts +++ b/test/fromRedactor.test.ts @@ -328,6 +328,13 @@ describe("Testing html to json conversion", () => { expect(json).toStrictEqual({"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"text":"Hello","attrs":{"style":{}},"bold":true},{"text":" Hii"}]}]}) }) + test("should add title attr to anchor tag", () => { + const html = expectedValue["RT-501"].html[0]; + const json =expectedValue["RT-501"].jsonWithRedactorAttributes[0]; + + let jsonValue = htmlToJson(html) + expect(omitdeep(jsonValue, "uid")).toStrictEqual(omitdeep(json, "uid")) + }) }) diff --git a/test/toRedactor.test.ts b/test/toRedactor.test.ts index c1a10ae..854a6db 100644 --- a/test/toRedactor.test.ts +++ b/test/toRedactor.test.ts @@ -329,5 +329,22 @@ describe("Testing json to html conversion", () => { }) }) + describe("RT-501",()=>{ + it("should add title attr to anchor tag",()=>{ + const html = expectedValue["RT-501"].html[0]; + const json =expectedValue["RT-501"].json[0]; + + let htmlValue = toRedactor(json) + expect(htmlValue).toBe(html); + }) + + }) + }) +test("should add nbsp for empty blocks", () => { + const json = {"type":"doc","uid":"uid","attrs":{},"children":[{"type":"p","attrs":{},"uid":"uid","children":[{"text":"Hi"}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":""}]},{"type":"p","attrs":{},"uid":"uid","children":[{"text":"Hello"}]}]}; + const html = toRedactor(json, {addNbspForEmptyBlocks: true}); + expect(html).toBe(`

Hi

 

 

Hello

`); +}); +