Commit f6d0fd0e authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '338273-render-frontmatter-content-editor' into 'master'

Render frontmatter in the Content Editor

See merge request gitlab-org/gitlab!70854
parents 5abda9c9 d84bcddf
<script>
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2';
import { __ } from '~/locale';
export default {
name: 'FrontMatter',
components: {
NodeViewWrapper,
NodeViewContent,
},
props: {
node: {
type: Object,
required: true,
},
},
i18n: {
frontmatter: __('frontmatter'),
},
};
</script>
<template>
<node-view-wrapper class="gl-relative code highlight" as="pre">
<span
data-testid="frontmatter-label"
class="gl-absolute gl-top-0 gl-right-3"
contenteditable="false"
>{{ $options.i18n.frontmatter }}:{{ node.attrs.language }}</span
>
<node-view-content as="code" />
</node-view-wrapper>
</template>
...@@ -11,7 +11,8 @@ export default CodeBlockLowlight.extend({ ...@@ -11,7 +11,8 @@ export default CodeBlockLowlight.extend({
parseHTML: (element) => extractLanguage(element), parseHTML: (element) => extractLanguage(element),
}, },
class: { class: {
default: 'code highlight js-syntax-highlight', // eslint-disable-next-line @gitlab/require-i18n-strings
default: 'code highlight',
}, },
}; };
}, },
......
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
import FrontmatterWrapper from '../components/wrappers/frontmatter.vue';
import CodeBlockHighlight from './code_block_highlight';
export default CodeBlockHighlight.extend({
name: 'frontmatter',
parseHTML() {
return [
{
tag: 'pre[data-lang-params="frontmatter"]',
preserveWhitespace: 'full',
priority: PARSE_HTML_PRIORITY_HIGHEST,
},
];
},
addNodeView() {
return new VueNodeViewRenderer(FrontmatterWrapper);
},
});
...@@ -16,6 +16,7 @@ import Dropcursor from '../extensions/dropcursor'; ...@@ -16,6 +16,7 @@ import Dropcursor from '../extensions/dropcursor';
import Emoji from '../extensions/emoji'; import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure'; import Figure from '../extensions/figure';
import FigureCaption from '../extensions/figure_caption'; import FigureCaption from '../extensions/figure_caption';
import Frontmatter from '../extensions/frontmatter';
import Gapcursor from '../extensions/gapcursor'; import Gapcursor from '../extensions/gapcursor';
import HardBreak from '../extensions/hard_break'; import HardBreak from '../extensions/hard_break';
import Heading from '../extensions/heading'; import Heading from '../extensions/heading';
...@@ -86,6 +87,7 @@ export const createContentEditor = ({ ...@@ -86,6 +87,7 @@ export const createContentEditor = ({
Emoji, Emoji,
Figure, Figure,
FigureCaption, FigureCaption,
Frontmatter,
Gapcursor, Gapcursor,
HardBreak, HardBreak,
Heading, Heading,
......
...@@ -15,6 +15,7 @@ import Division from '../extensions/division'; ...@@ -15,6 +15,7 @@ import Division from '../extensions/division';
import Emoji from '../extensions/emoji'; import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure'; import Figure from '../extensions/figure';
import FigureCaption from '../extensions/figure_caption'; import FigureCaption from '../extensions/figure_caption';
import Frontmatter from '../extensions/frontmatter';
import HardBreak from '../extensions/hard_break'; import HardBreak from '../extensions/hard_break';
import Heading from '../extensions/heading'; import Heading from '../extensions/heading';
import HorizontalRule from '../extensions/horizontal_rule'; import HorizontalRule from '../extensions/horizontal_rule';
...@@ -137,6 +138,20 @@ const defaultSerializerConfig = { ...@@ -137,6 +138,20 @@ const defaultSerializerConfig = {
state.write(`:${name}:`); state.write(`:${name}:`);
}, },
[Frontmatter.name]: (state, node) => {
const { language } = node.attrs;
const syntax = {
toml: '+++',
json: ';;;',
yaml: '---',
}[language];
state.write(`${syntax}\n`);
state.text(node.textContent, false);
state.ensureNewLine();
state.write(syntax);
state.closeBlock(node);
},
[Figure.name]: renderHTMLNode('figure'), [Figure.name]: renderHTMLNode('figure'),
[FigureCaption.name]: renderHTMLNode('figcaption'), [FigureCaption.name]: renderHTMLNode('figcaption'),
[HardBreak.name]: renderHardBreak, [HardBreak.name]: renderHardBreak,
......
...@@ -39995,6 +39995,9 @@ msgid_plural "from %d jobs" ...@@ -39995,6 +39995,9 @@ msgid_plural "from %d jobs"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "frontmatter"
msgstr ""
msgid "group" msgid "group"
msgstr "" msgstr ""
......
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2';
import { shallowMount } from '@vue/test-utils';
import FrontmatterWrapper from '~/content_editor/components/wrappers/frontmatter.vue';
describe('content/components/wrappers/frontmatter', () => {
let wrapper;
const createWrapper = async (nodeAttrs = { language: 'yaml' }) => {
wrapper = shallowMount(FrontmatterWrapper, {
propsData: {
node: {
attrs: nodeAttrs,
},
},
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders a node-view-wrapper as a pre element', () => {
createWrapper();
expect(wrapper.findComponent(NodeViewWrapper).props().as).toBe('pre');
expect(wrapper.findComponent(NodeViewWrapper).classes()).toContain('gl-relative');
});
it('renders a node-view-content as a code element', () => {
createWrapper();
expect(wrapper.findComponent(NodeViewContent).props().as).toBe('code');
});
it('renders label indicating that code block is frontmatter', () => {
createWrapper();
const label = wrapper.find('[data-testid="frontmatter-label"]');
expect(label.text()).toEqual('frontmatter:yaml');
expect(label.classes()).toEqual(['gl-absolute', 'gl-top-0', 'gl-right-3']);
});
});
...@@ -221,3 +221,20 @@ ...@@ -221,3 +221,20 @@
### I don't know ### I don't know
- name: word_break - name: word_break
markdown: Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz markdown: Fernstraßen<wbr>bau<wbr>privat<wbr>finanzierungs<wbr>gesetz
- name: frontmatter_yaml
markdown: |-
---
title: Page title
---
- name: frontmatter_toml
markdown: |-
+++
title = "Page title"
+++
- name: frontmatter_json
markdown: |-
;;;
{
"title": "Page title"
}
;;;
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