Commit 0523e401 authored by derek-knox's avatar derek-knox

V2 of custom html renderer

Update the custom html renderer to create a text
vs. html token. This allows us to properly prevent
WYSIWYG mode from rendering as HTML without borking
the source content.
parent c6eb657f
import renderHtml from './renderers/render_html';
import renderBlockHtml from './renderers/render_html_block';
import renderKramdownList from './renderers/render_kramdown_list';
import renderKramdownText from './renderers/render_kramdown_text';
import renderIdentifierParagraph from './renderers/render_identifier_paragraph';
......@@ -6,7 +6,7 @@ import renderEmbeddedRubyText from './renderers/render_embedded_ruby_text';
import renderFontAwesomeHtmlInline from './renderers/render_font_awesome_html_inline';
const htmlInlineRenderers = [renderFontAwesomeHtmlInline];
const htmlRenderers = [renderHtml];
const htmlBlockRenderers = [renderBlockHtml];
const listRenderers = [renderKramdownList];
const paragraphRenderers = [renderIdentifierParagraph];
const textRenderers = [renderKramdownText, renderEmbeddedRubyText];
......@@ -32,9 +32,9 @@ const buildCustomHTMLRenderer = (
) => {
const defaults = {
htmlBlock(node, context) {
const allHtmlRenderers = [...customRenderers.list, ...htmlRenderers];
const allHtmlBlockRenderers = [...customRenderers.htmlBlock, ...htmlBlockRenderers];
return executeRenderer(allHtmlRenderers, node, context);
return executeRenderer(allHtmlBlockRenderers, node, context);
},
htmlInline(node, context) {
const allHtmlInlineRenderers = [...customRenderers.htmlInline, ...htmlInlineRenderers];
......@@ -47,7 +47,7 @@ const buildCustomHTMLRenderer = (
return executeRenderer(allListRenderers, node, context);
},
paragraph(node, context) {
const allParagraphRenderers = [...customRenderers.list, ...paragraphRenderers];
const allParagraphRenderers = [...customRenderers.paragraph, ...paragraphRenderers];
return executeRenderer(allParagraphRenderers, node, context);
},
......
......@@ -7,24 +7,30 @@ const TAG_TYPES = {
inline: 'span',
};
// Open helpers (singular and multiple)
const buildUneditableOpenToken = (type = TAG_TYPES.block) =>
buildToken('openTag', type, {
attributes: { contenteditable: false },
classNames: [
'gl-px-4 gl-py-2 gl-opacity-5 gl-bg-gray-100 gl-user-select-none gl-cursor-not-allowed',
],
});
export const buildUneditableOpenTokens = (token, type = TAG_TYPES.block) => {
return [
buildToken('openTag', type, {
attributes: { contenteditable: false },
classNames: [
'gl-px-4 gl-py-2 gl-opacity-5 gl-bg-gray-100 gl-user-select-none gl-cursor-not-allowed',
],
}),
token,
];
return [buildUneditableOpenToken(type), token];
};
// Close helpers (singular and multiple)
export const buildUneditableCloseToken = (type = TAG_TYPES.block) => buildToken('closeTag', type);
export const buildUneditableCloseTokens = (token, type = TAG_TYPES.block) => {
return [token, buildUneditableCloseToken(type)];
};
// Complete helpers (open plus close)
export const buildUneditableInlineTokens = token => {
return [
...buildUneditableOpenTokens(token, TAG_TYPES.inline),
......@@ -35,3 +41,20 @@ export const buildUneditableInlineTokens = token => {
export const buildUneditableTokens = token => {
return [...buildUneditableOpenTokens(token), buildUneditableCloseToken()];
};
export const buildUneditableHtmlTokens = node => {
/*
Toast UI internally appends ' data-tomark-pass ' attribute flags so it can target certain
nested nodes for internal use during Markdown <=> WYSIWYG conversions. In our case, we want
to prevent HTML being rendered completely in WYSIWYG mode and thus we use a `text` vs. `html`
type when building the token. However, in doing so, we need to strip out the ` data-tomark-pass `
to prevent their persistence within the `text` content as the user did not intend these as edits.
https://github.com/nhn/tui.editor/blob/cc54ec224fc3a4b6e5a2b19a71650959f41adc0e/apps/editor/src/js/convertor.js#L72
*/
const regex = / data-tomark-pass /gm;
const content = node.literal.replace(regex, '');
const htmlAsTextToken = buildToken('text', null, { content });
return [buildUneditableOpenToken(), htmlAsTextToken, buildUneditableCloseToken()];
};
import { buildUneditableTokens } from './build_uneditable_token';
import { buildUneditableHtmlTokens } from './build_uneditable_token';
const canRender = ({ type }) => {
return type === 'htmlBlock';
};
const render = (_, { origin }) => buildUneditableTokens(origin());
const render = node => buildUneditableHtmlTokens(node);
export default { canRender, render };
---
title: Add a custom HTML renderer to the Static Site Editor for HTML block syntax
merge_request: 36330
author:
type: added
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_html';
import { buildUneditableTokens } from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
import renderer from '~/vue_shared/components/rich_content_editor/services/renderers/render_html_block';
import { buildUneditableHtmlTokens } from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
import { normalTextNode } from './mock_data';
const htmlLiteral = '<div><h1>Heading</h1><p>Paragraph.</p></div>';
const htmlBlockNode = {
firstChild: null,
literal: htmlLiteral,
literal: '<div><h1>Heading</h1><p>Paragraph.</p></div>',
type: 'htmlBlock',
};
......@@ -22,13 +21,18 @@ describe('Render HTML renderer', () => {
});
describe('render', () => {
it('should return uneditable tokens wrapping the origin token', () => {
const origin = jest.fn();
const context = { origin };
const htmlBlockNodeToMark = {
firstChild: null,
literal: '<div data-to-mark ></div>',
type: 'htmlBlock',
};
expect(renderer.render(htmlBlockNode, context)).toStrictEqual(
buildUneditableTokens(origin()),
);
it.each`
node
${htmlBlockNode}
${htmlBlockNodeToMark}
`('should return uneditable tokens wrapping the $node as a token', ({ node }) => {
expect(renderer.render(node)).toStrictEqual(buildUneditableHtmlTokens(node));
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment