diff --git a/cypress/component/richtext.cy.ts b/cypress/component/richtext.cy.ts index 519068d7b9..867c992b39 100644 --- a/cypress/component/richtext.cy.ts +++ b/cypress/component/richtext.cy.ts @@ -6,6 +6,19 @@ import NcRichText from '../../src/components/NcRichText/NcRichText.vue' describe('NcRichText', () => { describe('renders with markdown', () => { + describe('normal text', () => { + it('XML-like text (escaped and unescaped)', () => { + mount(NcRichText, { + propsData: { + text: 'text</span>', + useMarkdown: true, + }, + }) + + cy.get('p').should('have.text', 'text') + }) + }) + describe('headings', () => { it('heading (with hash (#) syntax divided with space from text)', () => { const testCases = [ @@ -274,6 +287,17 @@ describe('NcRichText', () => { cy.get('code').should('have.text', 'inline code') }) + + it('inline code (with ignored bold, italic, XML-like syntax))', () => { + mount(NcRichText, { + propsData: { + text: '`inline code **bold text** _italic text_ text</span>`', + useMarkdown: true, + }, + }) + + cy.get('code').should('have.text', 'inline code **bold text** _italic text_ text') + }) }) describe('multiline code', () => { @@ -333,20 +357,20 @@ describe('NcRichText', () => { cy.get('code').should('have.text', 'line 1\nline 2\nline 3\n') }) - it('multiline code (with ignored bold, italic, inline code syntax)', () => { + it('multiline code (with ignored bold, italic, inline code, XML-like syntax)', () => { mount(NcRichText, { propsData: { - text: '```\n**bold text**\n_italic text_\n`inline code`\n```', + text: '```\n**bold text**\n_italic text_\n`inline code`\ntext</span>\n```', useMarkdown: true, }, }) - cy.get('pre').should('have.text', '**bold text**\n_italic text_\n`inline code`\n') + cy.get('pre').should('have.text', '**bold text**\n_italic text_\n`inline code`\ntext\n') }) }) describe('blockquote', () => { - it('blockquote (with greater then (gt >) syntax)', () => { + it('blockquote (with greater then (>) syntax - normal)', () => { mount(NcRichText, { propsData: { text: '> blockquote', @@ -357,6 +381,17 @@ describe('NcRichText', () => { cy.get('blockquote').should('have.text', '\nblockquote\n') }) + it('blockquote (with greater then (>) syntax - escaped)', () => { + mount(NcRichText, { + propsData: { + text: '> blockquote', + useMarkdown: true, + }, + }) + + cy.get('blockquote').should('have.text', '\nblockquote\n') + }) + it('blockquote (with bold, italic text, inline code)', () => { mount(NcRichText, { propsData: { diff --git a/src/components/NcRichText/NcRichText.vue b/src/components/NcRichText/NcRichText.vue index 79f4e0db40..3ae7224f8f 100644 --- a/src/components/NcRichText/NcRichText.vue +++ b/src/components/NcRichText/NcRichText.vue @@ -192,6 +192,12 @@ export default { }) .use(rehype2react, { createElement: (tag, attrs, children) => { + // unescape special symbol "<" for simple text nodes + children = children?.map(child => typeof child === 'string' + ? child.replace(/</gmi, '<') + : child, + ) + if (!tag.startsWith('#')) { return h(tag, attrs, children) } @@ -217,15 +223,15 @@ export default { }, prefix: false, }) - .processSync(this.useMarkdown - // In order to correctly show newlines in Markdown, - // each newline contains a non-breaking space - ? this.text.slice() - .replace(/\n>\n/g, '\n>\u00A0\n') - .replace(/\n{2,}/g, (match) => { - return '\n' + '\n\u00A0\n'.repeat(match.length - 1) - }) - : this.text) + .processSync(this.text.slice() + // escape special symbol "<" to not treat text as HTML + .replace(/" to parse blockquotes + .replace(/>/gmi, '>') + // add a non-breaking space to each newline to show them + .replace(/\n>\n/g, '\n>\u00A0\n') + .replace(/\n{2,}/g, (match) => '\n' + '\n\u00A0\n'.repeat(match.length - 1)), + ) .result return h('div', { class: 'rich-text--wrapper' }, [ @@ -239,11 +245,9 @@ export default { }, }, render(h) { - if (!this.useMarkdown) { - return this.renderPlaintext(h) - } - - return this.renderMarkdown(h) + return this.useMarkdown + ? this.renderMarkdown(h) + : this.renderPlaintext(h) }, }