diff --git a/__tests__/serializers/slateToHtml/index.spec.ts b/__tests__/serializers/slateToHtml/index.spec.ts index f51df3b..4509c63 100644 --- a/__tests__/serializers/slateToHtml/index.spec.ts +++ b/__tests__/serializers/slateToHtml/index.spec.ts @@ -1,4 +1,4 @@ -import { ChildNode, Element } from 'domhandler' +import { Element } from 'domhandler' import { slateToHtml, slateToDomConfig } from '../../../src' describe('slateToHtml expected behaviour', () => { @@ -17,6 +17,69 @@ describe('slateToHtml expected behaviour', () => { expect(slateToHtml(slate)).toEqual(html) }) + /** + * @see https://www.w3.org/International/questions/qa-escapes#use + */ + it('encodes `breaking` HTML entities', () => { + const html = `

2 > 1 but is < 3 & it can break HTML

` + const slate = [ + { + children: [ + { + text: "2 > 1 but is < 3 & it can break HTML", + }, + ], + type: 'p', + }, + ] + expect(slateToHtml(slate)).toEqual(html) + }) + + it('encodes `non breaking` HTML entities', () => { + const html = `

The company’s priority is 'inside sales' and changing the spelling of cafe to café.

` + const slate = [ + { + children: [ + { + text: "The company’s priority is 'inside sales' and changing the spelling of cafe to café.", + }, + ], + type: 'p', + }, + ] + expect(slateToHtml(slate)).toEqual(html) + }) + + it('encodes `breaking` HTML entities only if option is active', () => { + const html = `

2 > 1 but is < 3 & it can break HTML

The company’s priority is 'inside sales' and changing the spelling of cafe to café.

` + const slate = [ + { + children: [ + { + text: "2 > 1 but is < 3 & it can break HTML", + }, + ], + type: 'p', + }, + { + children: [ + { + text: "The company’s priority is 'inside sales' and changing the spelling of cafe to café.", + }, + ], + type: 'p', + }, + ] + expect(slateToHtml( + slate, + { + ...slateToDomConfig, + encodeEntities: false, + alwaysEncodeBreakingEntities: true, + } + )).toEqual(html) + }) + it('does not encode HTML entities with the appropriate option', () => { const html = `

What's Heading 1

` const slate = [ diff --git a/src/config/slateToDom/default.ts b/src/config/slateToDom/default.ts index 845f4e3..892dd2b 100644 --- a/src/config/slateToDom/default.ts +++ b/src/config/slateToDom/default.ts @@ -51,6 +51,7 @@ export const config: Config = { }, }, encodeEntities: true, + alwaysEncodeBreakingEntities: false, alwaysEncodeCodeEntities: false, convertLineBreakToBr: false, } diff --git a/src/config/slateToDom/types.ts b/src/config/slateToDom/types.ts index 9185c9c..1b010c5 100644 --- a/src/config/slateToDom/types.ts +++ b/src/config/slateToDom/types.ts @@ -21,6 +21,7 @@ export interface Config { elementTransforms: ElementTagTransform defaultTag?: string encodeEntities?: boolean + alwaysEncodeBreakingEntities?: boolean alwaysEncodeCodeEntities?: boolean convertLineBreakToBr?: boolean } diff --git a/src/serializers/slateToHtml/index.ts b/src/serializers/slateToHtml/index.ts index d169031..aad9d64 100644 --- a/src/serializers/slateToHtml/index.ts +++ b/src/serializers/slateToHtml/index.ts @@ -6,7 +6,7 @@ import { Text as SlateText } from 'slate' import { config as defaultConfig } from '../../config/slateToDom/default' import { nestedMarkElements } from '../../utilities/domhandler' -import { getNested, isEmptyObject, styleToString } from '../../utilities' +import { getNested, isEmptyObject, styleToString, encodeBreakingEntities } from '../../utilities' import { SlateToDomConfig } from '../..' import { isBlock } from '../blocks' @@ -30,7 +30,7 @@ export const slateToDom: SlateToDom = (node: any[], config = defaultConfig) => { const slateNodeToHtml = (node: any, config = defaultConfig, isLastNodeInDocument = false) => { if (SlateText.isText(node)) { - const str = node.text + const str = (config.alwaysEncodeBreakingEntities && config.encodeEntities === false) ? encodeBreakingEntities(node.text) : node.text // convert line breaks to br tags const strLines = config.convertLineBreakToBr ? str.split('\n') : [str] diff --git a/src/utilities/index.ts b/src/utilities/index.ts index 5b3cad5..cb3c5b0 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -62,3 +62,23 @@ export const removeEmpty = (obj: {}): {} => { export const getNested = (obj: any, ...args: string[]) => { return args.reduce((o, level) => o && o[level], obj) } + +export const encodeBreakingEntities = (str: string) => { + const swapChar = (charToSwap: string) => { // that swaps characters to HTML entities + switch(charToSwap){ + case "&": + return "&" + case "<": + return "<" + case ">": + return ">" + default: + return charToSwap + } + } + str = str.replace(/[&<>]/g, function(match){ + return swapChar(match) + }) + + return str +} \ No newline at end of file