Commit 44b31ef5 authored by Himanshu Kapoor's avatar Himanshu Kapoor Committed by Michael Lunøe

Add support for div, figure, figcaption in content editor

parent 331e9287
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'; ...@@ -8,9 +8,12 @@ import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list'; import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code'; import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight'; import CodeBlockHighlight from '../extensions/code_block_highlight';
import Division from '../extensions/division';
import Document from '../extensions/document'; import Document from '../extensions/document';
import Dropcursor from '../extensions/dropcursor'; import Dropcursor from '../extensions/dropcursor';
import Emoji from '../extensions/emoji'; import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
import FigureCaption from '../extensions/figure_caption';
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';
...@@ -71,8 +74,11 @@ export const createContentEditor = ({ ...@@ -71,8 +74,11 @@ export const createContentEditor = ({
Code, Code,
CodeBlockHighlight, CodeBlockHighlight,
Document, Document,
Division,
Dropcursor, Dropcursor,
Emoji, Emoji,
Figure,
FigureCaption,
Gapcursor, Gapcursor,
HardBreak, HardBreak,
Heading, Heading,
......
...@@ -9,7 +9,10 @@ import Bold from '../extensions/bold'; ...@@ -9,7 +9,10 @@ import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list'; import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code'; import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight'; import CodeBlockHighlight from '../extensions/code_block_highlight';
import Division from '../extensions/division';
import Emoji from '../extensions/emoji'; import Emoji from '../extensions/emoji';
import Figure from '../extensions/figure';
import FigureCaption from '../extensions/figure_caption';
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';
...@@ -43,6 +46,7 @@ import { ...@@ -43,6 +46,7 @@ import {
renderOrderedList, renderOrderedList,
renderImage, renderImage,
renderPlayable, renderPlayable,
renderHTMLNode,
} from './serialization_helpers'; } from './serialization_helpers';
const defaultSerializerConfig = { const defaultSerializerConfig = {
...@@ -116,11 +120,14 @@ const defaultSerializerConfig = { ...@@ -116,11 +120,14 @@ const defaultSerializerConfig = {
state.write('```'); state.write('```');
state.closeBlock(node); state.closeBlock(node);
}, },
[Division.name]: renderHTMLNode('div'),
[Emoji.name]: (state, node) => { [Emoji.name]: (state, node) => {
const { name } = node.attrs; const { name } = node.attrs;
state.write(`:${name}:`); state.write(`:${name}:`);
}, },
[Figure.name]: renderHTMLNode('figure'),
[FigureCaption.name]: renderHTMLNode('figcaption'),
[HardBreak.name]: renderHardBreak, [HardBreak.name]: renderHardBreak,
[Heading.name]: defaultMarkdownSerializer.nodes.heading, [Heading.name]: defaultMarkdownSerializer.nodes.heading,
[HorizontalRule.name]: defaultMarkdownSerializer.nodes.horizontal_rule, [HorizontalRule.name]: defaultMarkdownSerializer.nodes.horizontal_rule,
......
...@@ -24,12 +24,20 @@ export function isPlainURL(link, parent, index, side) { ...@@ -24,12 +24,20 @@ export function isPlainURL(link, parent, index, side) {
return !link.isInSet(next.marks); return !link.isInSet(next.marks);
} }
function shouldRenderCellInline(cell) { 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) { if (cell.childCount === 1) {
const parent = cell.child(0); const child = cell.child(0);
if (parent.type.name === 'paragraph' && parent.childCount === 1) { if (child.type.name === 'paragraph') {
const child = parent.child(0); return containsOnlyText(child);
return child.isText && child.marks.length === 0;
} }
} }
...@@ -208,7 +216,7 @@ function renderTableRowAsHTML(state, node) { ...@@ -208,7 +216,7 @@ function renderTableRowAsHTML(state, node) {
renderTagOpen(state, tag, cell.attrs); renderTagOpen(state, tag, cell.attrs);
if (!shouldRenderCellInline(cell)) { if (!containsParagraphWithOnlyText(cell)) {
state.closeBlock(node); state.closeBlock(node);
state.flushClose(); state.flushClose();
} }
...@@ -222,6 +230,38 @@ function renderTableRowAsHTML(state, node) { ...@@ -222,6 +230,38 @@ function renderTableRowAsHTML(state, node) {
renderTagClose(state, 'tr'); 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) { export function renderOrderedList(state, node) {
const { parens } = node.attrs; const { parens } = node.attrs;
const start = node.attrs.start || 1; const start = node.attrs.start || 1;
...@@ -241,7 +281,7 @@ export function renderTableCell(state, node) { ...@@ -241,7 +281,7 @@ export function renderTableCell(state, node) {
return; return;
} }
if (!isInBlockTable(node) || shouldRenderCellInline(node)) { if (!isInBlockTable(node) || containsParagraphWithOnlyText(node)) {
state.renderInline(node.child(0)); state.renderInline(node.child(0));
} else { } else {
state.renderContent(node); state.renderContent(node);
......
...@@ -3,7 +3,10 @@ import Bold from '~/content_editor/extensions/bold'; ...@@ -3,7 +3,10 @@ import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list'; import BulletList from '~/content_editor/extensions/bullet_list';
import Code from '~/content_editor/extensions/code'; import Code from '~/content_editor/extensions/code';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight'; import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
import Division from '~/content_editor/extensions/division';
import Emoji from '~/content_editor/extensions/emoji'; 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 HardBreak from '~/content_editor/extensions/hard_break';
import Heading from '~/content_editor/extensions/heading'; import Heading from '~/content_editor/extensions/heading';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule'; import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
...@@ -38,7 +41,10 @@ const tiptapEditor = createTestEditor({ ...@@ -38,7 +41,10 @@ const tiptapEditor = createTestEditor({
BulletList, BulletList,
Code, Code,
CodeBlockHighlight, CodeBlockHighlight,
Division,
Emoji, Emoji,
Figure,
FigureCaption,
HardBreak, HardBreak,
Heading, Heading,
HorizontalRule, HorizontalRule,
...@@ -68,7 +74,10 @@ const { ...@@ -68,7 +74,10 @@ const {
bulletList, bulletList,
code, code,
codeBlock, codeBlock,
division,
emoji, emoji,
figure,
figureCaption,
heading, heading,
hardBreak, hardBreak,
horizontalRule, horizontalRule,
...@@ -95,7 +104,10 @@ const { ...@@ -95,7 +104,10 @@ const {
bulletList: { nodeType: BulletList.name }, bulletList: { nodeType: BulletList.name },
code: { markType: Code.name }, code: { markType: Code.name },
codeBlock: { nodeType: CodeBlockHighlight.name }, codeBlock: { nodeType: CodeBlockHighlight.name },
division: { nodeType: Division.name },
emoji: { markType: Emoji.name }, emoji: { markType: Emoji.name },
figure: { nodeType: Figure.name },
figureCaption: { nodeType: FigureCaption.name },
hardBreak: { nodeType: HardBreak.name }, hardBreak: { nodeType: HardBreak.name },
heading: { nodeType: Heading.name }, heading: { nodeType: Heading.name },
horizontalRule: { nodeType: HorizontalRule.name }, horizontalRule: { nodeType: HorizontalRule.name },
...@@ -533,6 +545,61 @@ this is not really json but just trying out whether this case works or not ...@@ -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', () => { it('correctly serializes a table with inline content', () => {
expect( expect(
serialize( serialize(
......
...@@ -33,6 +33,32 @@ ...@@ -33,6 +33,32 @@
* <ruby>漢<rt>ㄏㄢˋ</rt></ruby> * <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 * 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> * 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 - name: link
markdown: '[GitLab](https://gitlab.com)' markdown: '[GitLab](https://gitlab.com)'
- name: attachment_link - 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