Commit 78a8c6e8 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '232567-templater' into 'master'

Resolve "Custom renderers cleanup - erb"

Closes #232567

See merge request gitlab-org/gitlab!38694
parents 2353e28f 08a78828
......@@ -8,6 +8,7 @@ import { EDITOR_TYPES } from '~/vue_shared/components/rich_content_editor/consta
import { DEFAULT_IMAGE_UPLOAD_PATH } from '../constants';
import imageRepository from '../image_repository';
import formatter from '../services/formatter';
import templater from '../services/templater';
export default {
components: {
......@@ -44,7 +45,7 @@ export default {
data() {
return {
saveable: false,
parsedSource: parseSourceFile(this.content),
parsedSource: parseSourceFile(this.preProcess(true, this.content)),
editorMode: EDITOR_TYPES.wysiwyg,
isModified: false,
};
......@@ -59,22 +60,30 @@ export default {
},
},
methods: {
preProcess(isWrap, value) {
const formattedContent = formatter(value);
const templatedContent = isWrap
? templater.wrap(formattedContent)
: templater.unwrap(formattedContent);
return templatedContent;
},
onInputChange(newVal) {
this.parsedSource.sync(newVal, this.isWysiwygMode);
this.isModified = this.parsedSource.isModified();
},
onModeChange(mode) {
this.editorMode = mode;
const formattedContent = formatter(this.editableContent);
this.$refs.editor.resetInitialValue(formattedContent);
const preProcessedContent = this.preProcess(this.isWysiwygMode, this.editableContent);
this.$refs.editor.resetInitialValue(preProcessedContent);
},
onUploadImage({ file, imageUrl }) {
this.$options.imageRepository.add(file, imageUrl);
},
onSubmit() {
const formattedContent = formatter(this.parsedSource.content());
const preProcessedContent = this.preProcess(false, this.parsedSource.content());
this.$emit('submit', {
content: formattedContent,
content: preProcessedContent,
images: this.$options.imageRepository.getAll(),
});
},
......
const marker = 'sse';
const ticks = '```';
const prefix = `${ticks} ${marker}\n`; // Space intentional due to https://github.com/nhn/tui.editor/blob/6bcec75c69028570d93d973aa7533090257eaae0/libs/to-mark/src/renderer.gfm.js#L26
const postfix = `\n${ticks}`;
const code = '.| |\\t|\\n(?!\\n)';
const templatedRegex = new RegExp(`(^${prefix}(${code})+${postfix}$)`, 'gm');
const embeddedRubyRegex = new RegExp(`(^<%(${code})+%>$)`, 'gm');
const unwrap = source => {
let text = source;
const matches = text.match(templatedRegex);
if (matches) {
matches.forEach(match => {
const initial = match.replace(prefix, '').replace(postfix, '');
text = text.replace(match, initial);
});
}
return text;
};
const wrap = source => {
let text = unwrap(source);
const matches = text.match(embeddedRubyRegex);
if (matches) {
matches.forEach(match => {
text = text.replace(match, `${prefix}${match}${postfix}`);
});
}
return text;
};
export default { wrap, unwrap };
......@@ -3,7 +3,6 @@ import renderKramdownList from './renderers/render_kramdown_list';
import renderKramdownText from './renderers/render_kramdown_text';
import renderIdentifierInstanceText from './renderers/render_identifier_instance_text';
import renderIdentifierParagraph from './renderers/render_identifier_paragraph';
import renderEmbeddedRubyText from './renderers/render_embedded_ruby_text';
import renderFontAwesomeHtmlInline from './renderers/render_font_awesome_html_inline';
import renderSoftbreak from './renderers/render_softbreak';
......@@ -11,7 +10,7 @@ const htmlInlineRenderers = [renderFontAwesomeHtmlInline];
const htmlBlockRenderers = [renderBlockHtml];
const listRenderers = [renderKramdownList];
const paragraphRenderers = [renderIdentifierParagraph];
const textRenderers = [renderKramdownText, renderEmbeddedRubyText, renderIdentifierInstanceText];
const textRenderers = [renderKramdownText, renderIdentifierInstanceText];
const softbreakRenderers = [renderSoftbreak];
const executeRenderer = (renderers, node, context) => {
......
---
title: Added pre-processing step to the Static Site Editor so code templates (ERB) are interpreted as code not content
merge_request: 38694
author:
type: added
......@@ -15,11 +15,11 @@ import {
returnUrl,
} from '../mock_data';
jest.mock('~/static_site_editor/services/formatter', () => jest.fn(str => `${str} formatted`));
jest.mock('~/static_site_editor/services/formatter', () => jest.fn(str => `${str} format-pass`));
describe('~/static_site_editor/components/edit_area.vue', () => {
let wrapper;
const formattedContent = `${content} formatted`;
const formattedBody = `${body} format-pass`;
const savingChanges = true;
const newBody = `new ${body}`;
......@@ -53,9 +53,9 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
expect(findEditHeader().props('title')).toBe(title);
});
it('renders rich content editor', () => {
it('renders rich content editor with a format pass', () => {
expect(findRichContentEditor().exists()).toBe(true);
expect(findRichContentEditor().props('content')).toBe(body);
expect(findRichContentEditor().props('content')).toBe(formattedBody);
});
it('renders publish toolbar', () => {
......@@ -97,7 +97,7 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
});
it('sets publish toolbar as not saveable when content changes are rollback', () => {
findRichContentEditor().vm.$emit('input', body);
findRichContentEditor().vm.$emit('input', formattedBody);
return wrapper.vm.$nextTick().then(() => {
expect(findPublishToolbar().props('saveable')).toBe(false);
......@@ -124,8 +124,8 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
it.each`
initialMode | targetMode | resetValue
${EDITOR_TYPES.wysiwyg} | ${EDITOR_TYPES.markdown} | ${formattedContent}
${EDITOR_TYPES.markdown} | ${EDITOR_TYPES.wysiwyg} | ${`${body} formatted`}
${EDITOR_TYPES.wysiwyg} | ${EDITOR_TYPES.markdown} | ${`${content} format-pass format-pass`}
${EDITOR_TYPES.markdown} | ${EDITOR_TYPES.wysiwyg} | ${`${body} format-pass format-pass`}
`(
'sets editorMode from $initialMode to $targetMode',
({ initialMode, targetMode, resetValue }) => {
......@@ -144,7 +144,7 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
findRichContentEditor().vm.$emit('modeChange', EDITOR_TYPES.markdown);
expect(resetInitialValue).toHaveBeenCalledWith(formattedContent);
expect(resetInitialValue).toHaveBeenCalledWith(`${content} format-pass format-pass`);
});
});
......@@ -152,7 +152,7 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
it('should format the content', () => {
findPublishToolbar().vm.$emit('submit', content);
expect(wrapper.emitted('submit')[0][0].content).toBe(formattedContent);
expect(wrapper.emitted('submit')[0][0].content).toBe(`${content} format-pass format-pass`);
});
});
});
/* eslint-disable no-useless-escape */
import templater from '~/static_site_editor/services/templater';
describe('templater', () => {
const source = `Some text
<% some erb code %>
Some more text
<% if apptype.maturity && (apptype.maturity != "planned") %>
<% maturity = "This application type is at the \"#{apptype.maturity}\" level of maturity." %>
<% end %>
With even text with indented code above.
`;
const sourceTemplated = `Some text
\`\`\` sse
<% some erb code %>
\`\`\`
Some more text
\`\`\` sse
<% if apptype.maturity && (apptype.maturity != "planned") %>
<% maturity = "This application type is at the \"#{apptype.maturity}\" level of maturity." %>
<% end %>
\`\`\`
With even text with indented code above.
`;
it.each`
fn | initial | target
${'wrap'} | ${source} | ${sourceTemplated}
${'wrap'} | ${sourceTemplated} | ${sourceTemplated}
${'unwrap'} | ${sourceTemplated} | ${source}
${'unwrap'} | ${source} | ${source}
`(
'wraps $initial in a templated sse codeblock if $fn is wrap, unwraps otherwise',
({ fn, initial, target }) => {
expect(templater[fn](initial)).toMatch(target);
},
);
});
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