diff --git a/designer/client/src/views/preview-components/declarationfield.njk b/designer/client/src/views/preview-components/declarationfield.njk
index 2ad3349c9e..faf4a1217d 100644
--- a/designer/client/src/views/preview-components/declarationfield.njk
+++ b/designer/client/src/views/preview-components/declarationfield.njk
@@ -13,7 +13,7 @@
diff --git a/model/src/form/form-editor/preview/markdown.js b/model/src/form/form-editor/preview/markdown.js
index ed83d3b57a..e914b0c0d9 100644
--- a/model/src/form/form-editor/preview/markdown.js
+++ b/model/src/form/form-editor/preview/markdown.js
@@ -31,7 +31,7 @@ export class Markdown extends Content {
constructor(htmlElements, questionRenderer) {
super(htmlElements, questionRenderer)
const { content } = htmlElements.values
- this._content = markdownToHtml(content)
+ this._content = markdownToHtml(content, { startingHeaderLevel: 2 })
}
/**
@@ -39,7 +39,7 @@ export class Markdown extends Content {
* @protected
*/
_setContent(value) {
- super._setContent(markdownToHtml(value))
+ super._setContent(markdownToHtml(value, { startingHeaderLevel: 2 }))
}
}
diff --git a/model/src/form/form-editor/preview/markdown.test.js b/model/src/form/form-editor/preview/markdown.test.js
index a73385b3b7..4204f7c3f6 100644
--- a/model/src/form/form-editor/preview/markdown.test.js
+++ b/model/src/form/form-editor/preview/markdown.test.js
@@ -8,18 +8,20 @@ describe('markdown', () => {
const questionElements = new ContentElements(
buildMarkdownComponent({
title: 'Which quest would you like to pick?',
- content: '# This is a heading'
+ content: '# This is a heading demoted'
})
)
describe('Markdown', () => {
it('should create class', () => {
- expect(questionElements.values.content).toBe('# This is a heading')
+ expect(questionElements.values.content).toBe(
+ '# This is a heading demoted'
+ )
const res = new Markdown(questionElements, renderer)
expect(res.renderInput).toEqual({
id: 'markdown',
name: 'markdown',
classes: '',
- content: 'This is a heading
\n'
+ content: 'This is a heading demoted
\n'
})
expect(res.titleText).toBe('Which quest would you like to pick?')
expect(res.question).toBe('Which quest would you like to pick?')
@@ -40,7 +42,7 @@ describe('markdown', () => {
res.optional = true
expect(res.titleText).toBe('New question (optional)')
res.content = '## This is a subheading'
- expect(res.content).toBe('This is a subheading
\n')
+ expect(res.content).toBe('This is a subheading
\n')
})
})
})
diff --git a/model/src/utils/markdown.test.js b/model/src/utils/markdown.test.js
index 018a217b6c..e8649588fa 100644
--- a/model/src/utils/markdown.test.js
+++ b/model/src/utils/markdown.test.js
@@ -45,7 +45,7 @@ describe('Helpers', () => {
html: 'link
\n'
}
])("formats '$markdown' to '$html'", ({ markdown, html }) => {
- expect(markdownToHtml(markdown, exampleBaseUrl)).toBe(html)
+ expect(markdownToHtml(markdown, { baseUrl: exampleBaseUrl })).toBe(html)
})
})
@@ -63,4 +63,49 @@ describe('Helpers', () => {
expect(markdownToHtml(markdown)).toBe(html)
})
})
+
+ describe('markdown with demotion from speific levels', () => {
+ it.each([
+ {
+ level: 1,
+ markdown:
+ '# This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n',
+ html: 'This is H1
\nThis is H2
\nThis is H3
\nThis is H4
\nThis is H5
\nThis is H6
\n'
+ },
+ {
+ level: 2,
+ markdown:
+ '# This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n',
+ html: 'This is H1
\nThis is H2
\nThis is H3
\nThis is H4
\nThis is H5
\nThis is H6
\n'
+ },
+ {
+ level: 3,
+ markdown:
+ '# This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n',
+ html: 'This is H1
\nThis is H2
\nThis is H3
\nThis is H4
\nThis is H5
\nThis is H6
\n'
+ },
+ {
+ level: 4,
+ markdown:
+ '# This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n',
+ html: 'This is H1
\nThis is H2
\nThis is H3
\nThis is H4
\nThis is H5
\nThis is H6
\n'
+ },
+ {
+ level: 5,
+ markdown:
+ '# This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n',
+ html: 'This is H1
\nThis is H2
\nThis is H3
\nThis is H4
\nThis is H5
\nThis is H6
\n'
+ },
+ {
+ level: 6,
+ markdown:
+ '# This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n',
+ html: 'This is H1
\nThis is H2
\nThis is H3
\nThis is H4
\nThis is H5
\nThis is H6
\n'
+ }
+ ])("formats '$markdown' to '$html'", ({ markdown, html, level }) => {
+ expect(markdownToHtml(markdown, { startingHeaderLevel: level })).toBe(
+ html
+ )
+ })
+ })
})
diff --git a/model/src/utils/markdown.ts b/model/src/utils/markdown.ts
index f3962fb53e..febe4cb5d8 100644
--- a/model/src/utils/markdown.ts
+++ b/model/src/utils/markdown.ts
@@ -26,12 +26,26 @@ function renderLink(href: string, text: string, baseUrl?: string) {
return `${label}`
}
+function demoteHeading(
+ text: string,
+ depth: number,
+ startingHeaderLevel: number
+) {
+ // Max heading is h6 so don't demote further than that
+ depth = Math.min(depth + startingHeaderLevel - 1, 6)
+ return `${text}
+`
+}
+
/**
* Convert markdown to HTML, escaping any HTML tags first
*/
export function markdownToHtml(
markdown: string | null | undefined,
- baseUrl?: string // optional in some contexts, e.g. from the designer where it might not make sense
+ options?: {
+ baseUrl?: string // optional in some contexts, e.g. from the designer where it might not make sense,
+ startingHeaderLevel?: number
+ }
) {
if (markdown === undefined || markdown === null) {
return ''
@@ -46,8 +60,12 @@ export function markdownToHtml(
const renderer = new Renderer()
renderer.link = ({ href, text }: Tokens.Link): string => {
- return renderLink(href, text, baseUrl)
+ return renderLink(href, text, options?.baseUrl)
+ }
+ if (options?.startingHeaderLevel) {
+ renderer.heading = ({ text, depth }: Tokens.Heading): string => {
+ return demoteHeading(text, depth, options.startingHeaderLevel ?? 1)
+ }
}
-
return marked.parse(escaped, { async: false, renderer })
}