Commit 964390f4 authored by Michael Lunøe's avatar Michael Lunøe

Merge branch '338380-html-tags' into 'master'

Add support for div, figure, figcaption in content editor

See merge request gitlab-org/gitlab!69235
parents 57cc0fda 44b31ef5
import { Node } from '@tiptap/core';
import { PARSE_HTML_PRIORITY_LOWEST } from '../constants';
export default Node.create({
name: 'division',
content: 'block*',
group: 'block',
defining: true,
parseHTML() {
return [{ tag: 'div', priority: PARSE_HTML_PRIORITY_LOWEST }];
},
renderHTML({ HTMLAttributes }) {
return ['div', HTMLAttributes, 0];
},
});
import { Node } from '@tiptap/core';
export default Node.create({
name: 'figure',
content: 'block+',
group: 'block',
defining: true,
parseHTML() {
return [{ tag: 'figure' }];
},
renderHTML({ HTMLAttributes }) {
return ['figure', HTMLAttributes, 0];
},
});
import { Node } from '@tiptap/core';
export default Node.create({
name: 'figureCaption',
content: 'inline*',
group: 'block',
defining: true,
parseHTML() {
return [{ tag: 'figcaption' }];
},
renderHTML({ HTMLAttributes }) {
return ['figcaption', HTMLAttributes, 0];
},
});
......@@ -8,9 +8,12 @@ import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import Division from '../extensions/division';
import Document from '../extensions/document';
import Dropcursor from '../extensions/dropcursor';
import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
import FigureCaption from '../extensions/figure_caption';
import Gapcursor from '../extensions/gapcursor';
import HardBreak from '../extensions/hard_break';
import Heading from '../extensions/heading';
......@@ -71,8 +74,11 @@ export const createContentEditor = ({
Code,
CodeBlockHighlight,
Document,
Division,
Dropcursor,
Emoji,
Figure,
FigureCaption,
Gapcursor,
HardBreak,
Heading,
......
......@@ -9,7 +9,10 @@ import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import Division from '../extensions/division';
import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
import FigureCaption from '../extensions/figure_caption';
import HardBreak from '../extensions/hard_break';
import Heading from '../extensions/heading';
import HorizontalRule from '../extensions/horizontal_rule';
......@@ -43,6 +46,7 @@ import {
renderOrderedList,
renderImage,
renderPlayable,
renderHTMLNode,
} from './serialization_helpers';
const defaultSerializerConfig = {
......@@ -116,11 +120,14 @@ const defaultSerializerConfig = {
state.write('```');
state.closeBlock(node);
},
[Division.name]: renderHTMLNode('div'),
[Emoji.name]: (state, node) => {
const { name } = node.attrs;
state.write(`:${name}:`);
},
[Figure.name]: renderHTMLNode('figure'),
[FigureCaption.name]: renderHTMLNode('figcaption'),
[HardBreak.name]: renderHardBreak,
[Heading.name]: defaultMarkdownSerializer.nodes.heading,
[HorizontalRule.name]: defaultMarkdownSerializer.nodes.horizontal_rule,
......
......@@ -24,13 +24,21 @@ export function isPlainURL(link, parent, index, side) {
return !link.isInSet(next.marks);
}
function shouldRenderCellInline(cell) {
if (cell.childCount === 1) {
const parent = cell.child(0);
if (parent.type.name === 'paragraph' && parent.childCount === 1) {
const child = parent.child(0);
function containsOnlyText(node) {
if (node.childCount === 1) {
const child = node.child(0);
return child.isText && child.marks.length === 0;
}
return false;
}
function containsParagraphWithOnlyText(cell) {
if (cell.childCount === 1) {
const child = cell.child(0);
if (child.type.name === 'paragraph') {
return containsOnlyText(child);
}
}
return false;
......@@ -208,7 +216,7 @@ function renderTableRowAsHTML(state, node) {
renderTagOpen(state, tag, cell.attrs);
if (!shouldRenderCellInline(cell)) {
if (!containsParagraphWithOnlyText(cell)) {
state.closeBlock(node);
state.flushClose();
}
......@@ -222,6 +230,38 @@ function renderTableRowAsHTML(state, node) {
renderTagClose(state, 'tr');
}
export function renderContent(state, node, forceRenderInline) {
if (node.type.inlineContent) {
if (containsOnlyText(node)) {
state.renderInline(node);
} else {
state.closeBlock(node);
state.flushClose();
state.renderInline(node);
state.closeBlock(node);
state.flushClose();
}
} else {
const renderInline = forceRenderInline || containsParagraphWithOnlyText(node);
if (!renderInline) {
state.closeBlock(node);
state.flushClose();
state.renderContent(node);
state.ensureNewLine();
} else {
state.renderInline(forceRenderInline ? node : node.child(0));
}
}
}
export function renderHTMLNode(tagName, forceRenderInline = false) {
return (state, node) => {
renderTagOpen(state, tagName, node.attrs);
renderContent(state, node, forceRenderInline);
renderTagClose(state, tagName, false);
};
}
export function renderOrderedList(state, node) {
const { parens } = node.attrs;
const start = node.attrs.start || 1;
......@@ -241,7 +281,7 @@ export function renderTableCell(state, node) {
return;
}
if (!isInBlockTable(node) || shouldRenderCellInline(node)) {
if (!isInBlockTable(node) || containsParagraphWithOnlyText(node)) {
state.renderInline(node.child(0));
} else {
state.renderContent(node);
......
......@@ -3,7 +3,10 @@ import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
import Code from '~/content_editor/extensions/code';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
import Division from '~/content_editor/extensions/division';
import Emoji from '~/content_editor/extensions/emoji';
import Figure from '~/content_editor/extensions/figure';
import FigureCaption from '~/content_editor/extensions/figure_caption';
import HardBreak from '~/content_editor/extensions/hard_break';
import Heading from '~/content_editor/extensions/heading';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
......@@ -38,7 +41,10 @@ const tiptapEditor = createTestEditor({
BulletList,
Code,
CodeBlockHighlight,
Division,
Emoji,
Figure,
FigureCaption,
HardBreak,
Heading,
HorizontalRule,
......@@ -68,7 +74,10 @@ const {
bulletList,
code,
codeBlock,
division,
emoji,
figure,
figureCaption,
heading,
hardBreak,
horizontalRule,
......@@ -95,7 +104,10 @@ const {
bulletList: { nodeType: BulletList.name },
code: { markType: Code.name },
codeBlock: { nodeType: CodeBlockHighlight.name },
division: { nodeType: Division.name },
emoji: { markType: Emoji.name },
figure: { nodeType: Figure.name },
figureCaption: { nodeType: FigureCaption.name },
hardBreak: { nodeType: HardBreak.name },
heading: { nodeType: Heading.name },
horizontalRule: { nodeType: HorizontalRule.name },
......@@ -533,6 +545,61 @@ this is not really json but just trying out whether this case works or not
);
});
it('correctly renders div', () => {
expect(
serialize(
division(paragraph('just a paragraph in a div')),
division(paragraph('just some ', bold('styled'), ' ', italic('content'), ' in a div')),
),
).toBe(
'<div>just a paragraph in a div</div>\n<div>\n\njust some **styled** _content_ in a div\n\n</div>',
);
});
it('correctly renders figure', () => {
expect(
serialize(
figure(
paragraph(image({ src: 'elephant.jpg', alt: 'An elephant at sunset' })),
figureCaption('An elephant at sunset'),
),
),
).toBe(
`
<figure>
![An elephant at sunset](elephant.jpg)
<figcaption>An elephant at sunset</figcaption>
</figure>
`.trim(),
);
});
it('correctly renders figure with styled caption', () => {
expect(
serialize(
figure(
paragraph(image({ src: 'elephant.jpg', alt: 'An elephant at sunset' })),
figureCaption(italic('An elephant at sunset')),
),
),
).toBe(
`
<figure>
![An elephant at sunset](elephant.jpg)
<figcaption>
_An elephant at sunset_
</figcaption>
</figure>
`.trim(),
);
});
it('correctly serializes a table with inline content', () => {
expect(
serialize(
......
......@@ -33,6 +33,32 @@
* <ruby>漢<rt>ㄏㄢˋ</rt></ruby>
* C<sub>7</sub>H<sub>16</sub> + O<sub>2</sub> → CO<sub>2</sub> + H<sub>2</sub>O
* The **Pythagorean theorem** is often expressed as <var>a<sup>2</sup></var> + <var>b<sup>2</sup></var> = <var>c<sup>2</sup></var>
- name: div
markdown: |-
<div>plain text</div>
<div>
just a plain ol' div, not much to _expect_!
</div>
- name: figure
markdown: |-
<figure>
![Elephant at sunset](elephant-sunset.jpg)
<figcaption>An elephant at sunset</figcaption>
</figure>
<figure>
![A crocodile wearing crocs](croc-crocs.jpg)
<figcaption>
A crocodile wearing _crocs_!
</figcaption>
</figure>
- name: link
markdown: '[GitLab](https://gitlab.com)'
- name: attachment_link
......
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