Commit 0653e298 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch 'refactor/put-all-serializers-in-one-place' into 'master'

Refactor: Declare all Content Editor Markdown serializers in a single module

See merge request gitlab-org/gitlab!66867
parents f344fa1d 30776c80
......@@ -9,10 +9,9 @@ import {
GlTooltipDirective as GlTooltip,
} from '@gitlab/ui';
import { Editor as TiptapEditor } from '@tiptap/vue-2';
import Link from '../extensions/link';
import { hasSelection } from '../services/utils';
export const linkContentType = 'link';
export default {
components: {
GlDropdown,
......@@ -38,12 +37,12 @@ export default {
},
computed: {
isActive() {
return this.tiptapEditor.isActive(linkContentType);
return this.tiptapEditor.isActive(Link.name);
},
},
mounted() {
this.tiptapEditor.on('selectionUpdate', ({ editor }) => {
const { canonicalSrc, href } = editor.getAttributes(linkContentType);
const { canonicalSrc, href } = editor.getAttributes(Link.name);
this.linkHref = canonicalSrc || href;
});
......@@ -60,20 +59,20 @@ export default {
})
.run();
this.$emit('execute', { contentType: linkContentType });
this.$emit('execute', { contentType: Link.name });
},
selectLink() {
const { tiptapEditor } = this;
// a selection has already been made by the user, so do nothing
if (!hasSelection(tiptapEditor)) {
tiptapEditor.chain().focus().extendMarkRange(linkContentType).run();
tiptapEditor.chain().focus().extendMarkRange(Link.name).run();
}
},
removeLink() {
this.tiptapEditor.chain().focus().unsetLink().run();
this.$emit('execute', { contentType: linkContentType });
this.$emit('execute', { contentType: Link.name });
},
},
};
......
import { Blockquote } from '@tiptap/extension-blockquote';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = Blockquote;
export const serializer = defaultMarkdownSerializer.nodes.blockquote;
export { Blockquote as default } from '@tiptap/extension-blockquote';
import { Bold } from '@tiptap/extension-bold';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = Bold;
export const serializer = defaultMarkdownSerializer.marks.strong;
export { Bold as default } from '@tiptap/extension-bold';
import { BulletList } from '@tiptap/extension-bullet-list';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = BulletList;
export const serializer = defaultMarkdownSerializer.nodes.bullet_list;
export { BulletList as default } from '@tiptap/extension-bullet-list';
import { Code } from '@tiptap/extension-code';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = Code;
export const serializer = defaultMarkdownSerializer.marks.code;
export { Code as default } from '@tiptap/extension-code';
import { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight';
import * as lowlight from 'lowlight';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
const extractLanguage = (element) => element.getAttribute('lang');
const ExtendedCodeBlockLowlight = CodeBlockLowlight.extend({
export default CodeBlockLowlight.extend({
addAttributes() {
return {
language: {
......@@ -38,6 +37,3 @@ const ExtendedCodeBlockLowlight = CodeBlockLowlight.extend({
}).configure({
lowlight,
});
export const tiptapExtension = ExtendedCodeBlockLowlight;
export const serializer = defaultMarkdownSerializer.nodes.code_block;
import Document from '@tiptap/extension-document';
export const tiptapExtension = Document;
export { Document as default } from '@tiptap/extension-document';
import Dropcursor from '@tiptap/extension-dropcursor';
export const tiptapExtension = Dropcursor;
export { Dropcursor as default } from '@tiptap/extension-dropcursor';
import Gapcursor from '@tiptap/extension-gapcursor';
export const tiptapExtension = Gapcursor;
export { Gapcursor as default } from '@tiptap/extension-gapcursor';
import { HardBreak } from '@tiptap/extension-hard-break';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
const ExtendedHardBreak = HardBreak.extend({
export default HardBreak.extend({
addKeyboardShortcuts() {
return {
'Shift-Enter': () => this.editor.commands.setHardBreak(),
};
},
});
export const tiptapExtension = ExtendedHardBreak;
export const serializer = defaultMarkdownSerializer.nodes.hard_break;
import { Heading } from '@tiptap/extension-heading';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = Heading;
export const serializer = defaultMarkdownSerializer.nodes.heading;
export { Heading as default } from '@tiptap/extension-heading';
import History from '@tiptap/extension-history';
export const tiptapExtension = History;
export { History as default } from '@tiptap/extension-history';
import { nodeInputRule } from '@tiptap/core';
import { HorizontalRule } from '@tiptap/extension-horizontal-rule';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const hrInputRuleRegExp = /^---$/;
export const tiptapExtension = HorizontalRule.extend({
export default HorizontalRule.extend({
addInputRules() {
return [nodeInputRule(hrInputRuleRegExp, this.type)];
},
});
export const serializer = defaultMarkdownSerializer.nodes.horizontal_rule;
......@@ -48,11 +48,12 @@ const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown }) => {
return false;
};
const ExtendedImage = Image.extend({
export default Image.extend({
defaultOptions: {
...Image.options,
uploadsPath: null,
renderMarkdown: null,
inline: true,
},
addAttributes() {
return {
......@@ -152,17 +153,3 @@ const ExtendedImage = Image.extend({
return VueNodeViewRenderer(ImageWrapper);
},
});
const serializer = (state, node) => {
const { alt, canonicalSrc, src, title } = node.attrs;
const quotedTitle = title ? ` ${state.quote(title)}` : '';
state.write(`![${state.esc(alt || '')}](${state.esc(canonicalSrc || src)}${quotedTitle})`);
};
export const configure = ({ renderMarkdown, uploadsPath }) => {
return {
tiptapExtension: ExtendedImage.configure({ inline: true, renderMarkdown, uploadsPath }),
serializer,
};
};
import { Italic } from '@tiptap/extension-italic';
export const tiptapExtension = Italic;
export const serializer = { open: '_', close: '_', mixable: true, expelEnclosingWhitespace: true };
export { Italic as default } from '@tiptap/extension-italic';
......@@ -20,7 +20,7 @@ export const extractHrefFromMarkdownLink = (match) => {
return extractHrefFromMatch(match);
};
export const tiptapExtension = Link.extend({
export default Link.extend({
addInputRules() {
return [
markInputRule(markdownLinkSyntaxInputRuleRegExp, this.type, extractHrefFromMarkdownLink),
......@@ -51,13 +51,3 @@ export const tiptapExtension = Link.extend({
}).configure({
openOnClick: false,
});
export const serializer = {
open() {
return '[';
},
close(state, mark) {
const href = mark.attrs.canonicalSrc || mark.attrs.href;
return `](${state.esc(href)}${mark.attrs.title ? ` ${state.quote(mark.attrs.title)}` : ''})`;
},
};
import { ListItem } from '@tiptap/extension-list-item';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = ListItem;
export const serializer = defaultMarkdownSerializer.nodes.list_item;
export { ListItem as default } from '@tiptap/extension-list-item';
import { OrderedList } from '@tiptap/extension-ordered-list';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = OrderedList;
export const serializer = defaultMarkdownSerializer.nodes.ordered_list;
export { OrderedList as default } from '@tiptap/extension-ordered-list';
import { Paragraph } from '@tiptap/extension-paragraph';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = Paragraph;
export const serializer = defaultMarkdownSerializer.nodes.paragraph;
export { Paragraph as default } from '@tiptap/extension-paragraph';
import { Strike } from '@tiptap/extension-strike';
export const tiptapExtension = Strike;
export const serializer = {
open: '~~',
close: '~~',
mixable: true,
expelEnclosingWhitespace: true,
};
export { Strike as default } from '@tiptap/extension-strike';
import { Table } from '@tiptap/extension-table';
export const tiptapExtension = Table;
export function serializer(state, node) {
state.renderContent(node);
}
export { Table as default } from '@tiptap/extension-table';
import { TableCell } from '@tiptap/extension-table-cell';
export const tiptapExtension = TableCell.extend({
export default TableCell.extend({
content: 'inline*',
});
export function serializer(state, node) {
state.renderInline(node);
}
import { TableHeader } from '@tiptap/extension-table-header';
export const tiptapExtension = TableHeader.extend({
export default TableHeader.extend({
content: 'inline*',
});
export function serializer(state, node) {
state.renderInline(node);
}
import { TableRow } from '@tiptap/extension-table-row';
export const tiptapExtension = TableRow.extend({
export default TableRow.extend({
allowGapCursor: false,
});
export function serializer(state, node) {
const isHeaderRow = node.child(0).type.name === 'tableHeader';
const renderRow = () => {
const cellWidths = [];
state.flushClose(1);
state.write('| ');
node.forEach((cell, _, i) => {
if (i) state.write(' | ');
const { length } = state.out;
state.render(cell, node, i);
cellWidths.push(state.out.length - length);
});
state.write(' |');
state.closeBlock(node);
return cellWidths;
};
const renderHeaderRow = (cellWidths) => {
state.flushClose(1);
state.write('|');
node.forEach((cell, _, i) => {
if (i) state.write('|');
state.write(cell.attrs.align === 'center' ? ':' : '-');
state.write(state.repeat('-', cellWidths[i]));
state.write(cell.attrs.align === 'center' || cell.attrs.align === 'right' ? ':' : '-');
});
state.write('|');
state.closeBlock(node);
};
if (isHeaderRow) {
renderHeaderRow(renderRow());
} else {
renderRow();
}
}
import { Text } from '@tiptap/extension-text';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
export const tiptapExtension = Text;
export const serializer = defaultMarkdownSerializer.nodes.text;
export { Text as default } from '@tiptap/extension-text';
const buildSerializerConfig = (extensions = []) =>
extensions
.filter(({ serializer }) => serializer)
.reduce(
(serializers, { serializer, tiptapExtension: { name, type } }) => {
const collection = `${type}s`;
return {
...serializers,
[collection]: {
...serializers[collection],
[name]: serializer,
},
};
},
{
nodes: {},
marks: {},
},
);
export default buildSerializerConfig;
import { Editor } from '@tiptap/vue-2';
import { isFunction } from 'lodash';
import { PROVIDE_SERIALIZER_OR_RENDERER_ERROR } from '../constants';
import * as Blockquote from '../extensions/blockquote';
import * as Bold from '../extensions/bold';
import * as BulletList from '../extensions/bullet_list';
import * as Code from '../extensions/code';
import * as CodeBlockHighlight from '../extensions/code_block_highlight';
import * as Document from '../extensions/document';
import * as Dropcursor from '../extensions/dropcursor';
import * as Gapcursor from '../extensions/gapcursor';
import * as HardBreak from '../extensions/hard_break';
import * as Heading from '../extensions/heading';
import * as History from '../extensions/history';
import * as HorizontalRule from '../extensions/horizontal_rule';
import * as Image from '../extensions/image';
import * as Italic from '../extensions/italic';
import * as Link from '../extensions/link';
import * as ListItem from '../extensions/list_item';
import * as OrderedList from '../extensions/ordered_list';
import * as Paragraph from '../extensions/paragraph';
import * as Strike from '../extensions/strike';
import * as Table from '../extensions/table';
import * as TableCell from '../extensions/table_cell';
import * as TableHeader from '../extensions/table_header';
import * as TableRow from '../extensions/table_row';
import * as Text from '../extensions/text';
import buildSerializerConfig from './build_serializer_config';
import Blockquote from '../extensions/blockquote';
import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import Document from '../extensions/document';
import Dropcursor from '../extensions/dropcursor';
import Gapcursor from '../extensions/gapcursor';
import HardBreak from '../extensions/hard_break';
import Heading from '../extensions/heading';
import History from '../extensions/history';
import HorizontalRule from '../extensions/horizontal_rule';
import Image from '../extensions/image';
import Italic from '../extensions/italic';
import Link from '../extensions/link';
import ListItem from '../extensions/list_item';
import OrderedList from '../extensions/ordered_list';
import Paragraph from '../extensions/paragraph';
import Strike from '../extensions/strike';
import Table from '../extensions/table';
import TableCell from '../extensions/table_cell';
import TableHeader from '../extensions/table_header';
import TableRow from '../extensions/table_row';
import Text from '../extensions/text';
import { ContentEditor } from './content_editor';
import createMarkdownSerializer from './markdown_serializer';
import trackInputRulesAndShortcuts from './track_input_rules_and_shortcuts';
const collectTiptapExtensions = (extensions = []) =>
extensions.map(({ tiptapExtension }) => tiptapExtension);
const createTiptapEditor = ({ extensions = [], ...options } = {}) =>
new Editor({
extensions: [...extensions],
......@@ -48,6 +44,7 @@ export const createContentEditor = ({
renderMarkdown,
uploadsPath,
extensions = [],
serializerConfig = { marks: {}, nodes: {} },
tiptapOptions,
} = {}) => {
if (!isFunction(renderMarkdown)) {
......@@ -82,9 +79,8 @@ export const createContentEditor = ({
];
const allExtensions = [...builtInContentEditorExtensions, ...extensions];
const tiptapExtensions = collectTiptapExtensions(allExtensions).map(trackInputRulesAndShortcuts);
const tiptapEditor = createTiptapEditor({ extensions: tiptapExtensions, ...tiptapOptions });
const serializerConfig = buildSerializerConfig(allExtensions);
const trackedExtensions = allExtensions.map(trackInputRulesAndShortcuts);
const tiptapEditor = createTiptapEditor({ extensions: trackedExtensions, ...tiptapOptions });
const serializer = createMarkdownSerializer({ render: renderMarkdown, serializerConfig });
return new ContentEditor({ tiptapEditor, serializer });
......
import { MarkdownSerializer as ProseMirrorMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
import {
MarkdownSerializer as ProseMirrorMarkdownSerializer,
defaultMarkdownSerializer,
} from 'prosemirror-markdown/src/to_markdown';
import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model';
import Blockquote from '../extensions/blockquote';
import Bold from '../extensions/bold';
import BulletList from '../extensions/bullet_list';
import Code from '../extensions/code';
import CodeBlockHighlight from '../extensions/code_block_highlight';
import HardBreak from '../extensions/hard_break';
import Heading from '../extensions/heading';
import HorizontalRule from '../extensions/horizontal_rule';
import Image from '../extensions/image';
import Italic from '../extensions/italic';
import Link from '../extensions/link';
import ListItem from '../extensions/list_item';
import OrderedList from '../extensions/ordered_list';
import Paragraph from '../extensions/paragraph';
import Strike from '../extensions/strike';
import Table from '../extensions/table';
import TableCell from '../extensions/table_cell';
import TableHeader from '../extensions/table_header';
import TableRow from '../extensions/table_row';
import Text from '../extensions/text';
const defaultSerializerConfig = {
marks: {
[Bold.name]: defaultMarkdownSerializer.marks.strong,
[Code.name]: defaultMarkdownSerializer.marks.code,
[Italic.name]: { open: '_', close: '_', mixable: true, expelEnclosingWhitespace: true },
[Link.name]: {
open() {
return '[';
},
close(state, mark) {
const href = mark.attrs.canonicalSrc || mark.attrs.href;
return `](${state.esc(href)}${
mark.attrs.title ? ` ${state.quote(mark.attrs.title)}` : ''
})`;
},
},
[Strike.name]: {
open: '~~',
close: '~~',
mixable: true,
expelEnclosingWhitespace: true,
},
},
nodes: {
[Blockquote.name]: defaultMarkdownSerializer.nodes.blockquote,
[BulletList.name]: defaultMarkdownSerializer.nodes.bullet_list,
[CodeBlockHighlight.name]: defaultMarkdownSerializer.nodes.code_block,
[HardBreak.name]: defaultMarkdownSerializer.nodes.hard_break,
[Heading.name]: defaultMarkdownSerializer.nodes.heading,
[HorizontalRule.name]: defaultMarkdownSerializer.nodes.horizontal_rule,
[Image.name]: (state, node) => {
const { alt, canonicalSrc, src, title } = node.attrs;
const quotedTitle = title ? ` ${state.quote(title)}` : '';
state.write(`![${state.esc(alt || '')}](${state.esc(canonicalSrc || src)}${quotedTitle})`);
},
[ListItem.name]: defaultMarkdownSerializer.nodes.list_item,
[OrderedList.name]: defaultMarkdownSerializer.nodes.ordered_list,
[Paragraph.name]: defaultMarkdownSerializer.nodes.paragraph,
[Table.name]: (state, node) => {
state.renderContent(node);
},
[TableCell.name]: (state, node) => {
state.renderInline(node);
},
[TableHeader.name]: (state, node) => {
state.renderInline(node);
},
[TableRow.name]: (state, node) => {
const isHeaderRow = node.child(0).type.name === 'tableHeader';
const renderRow = () => {
const cellWidths = [];
state.flushClose(1);
state.write('| ');
node.forEach((cell, _, i) => {
if (i) state.write(' | ');
const { length } = state.out;
state.render(cell, node, i);
cellWidths.push(state.out.length - length);
});
state.write(' |');
state.closeBlock(node);
return cellWidths;
};
const renderHeaderRow = (cellWidths) => {
state.flushClose(1);
state.write('|');
node.forEach((cell, _, i) => {
if (i) state.write('|');
state.write(cell.attrs.align === 'center' ? ':' : '-');
state.write(state.repeat('-', cellWidths[i]));
state.write(cell.attrs.align === 'center' || cell.attrs.align === 'right' ? ':' : '-');
});
state.write('|');
state.closeBlock(node);
};
if (isHeaderRow) {
renderHeaderRow(renderRow());
} else {
renderRow();
}
},
[Text.name]: defaultMarkdownSerializer.nodes.text,
},
};
const wrapHtmlPayload = (payload) => `<div>${payload}</div>`;
......@@ -50,8 +170,16 @@ export default ({ render = () => null, serializerConfig }) => ({
*/
serialize: ({ schema, content }) => {
const proseMirrorDocument = schema.nodeFromJSON(content);
const { nodes, marks } = serializerConfig;
const serializer = new ProseMirrorMarkdownSerializer(nodes, marks);
const serializer = new ProseMirrorMarkdownSerializer(
{
...defaultSerializerConfig.nodes,
...serializerConfig.nodes,
},
{
...defaultSerializerConfig.marks,
...serializerConfig.marks,
},
);
return serializer.serialize(proseMirrorDocument, {
tightLists: true,
......
import { GlButton, GlFormInputGroup } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarImageButton from '~/content_editor/components/toolbar_image_button.vue';
import { configure as configureImageExtension } from '~/content_editor/extensions/image';
import Image from '~/content_editor/extensions/image';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_image_button', () => {
......@@ -29,13 +29,13 @@ describe('content_editor/components/toolbar_image_button', () => {
};
beforeEach(() => {
const { tiptapExtension: Image } = configureImageExtension({
renderMarkdown: jest.fn(),
uploadsPath: '/uploads/',
});
editor = createTestEditor({
extensions: [Image],
extensions: [
Image.configure({
renderMarkdown: jest.fn(),
uploadsPath: '/uploads/',
}),
],
});
buildWrapper();
......
import { GlDropdown, GlDropdownDivider, GlButton, GlFormInputGroup } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarLinkButton from '~/content_editor/components/toolbar_link_button.vue';
import { tiptapExtension as Link } from '~/content_editor/extensions/link';
import Link from '~/content_editor/extensions/link';
import { hasSelection } from '~/content_editor/services/utils';
import { createTestEditor, mockChainedCommands } from '../test_utils';
......@@ -25,9 +25,7 @@ describe('content_editor/components/toolbar_link_button', () => {
const findRemoveLinkButton = () => wrapper.findByText('Remove link');
beforeEach(() => {
editor = createTestEditor({
extensions: [Link],
});
editor = createTestEditor();
});
afterEach(() => {
......
import { GlDropdown, GlButton } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarTableButton from '~/content_editor/components/toolbar_table_button.vue';
import { tiptapExtension as Table } from '~/content_editor/extensions/table';
import { tiptapExtension as TableCell } from '~/content_editor/extensions/table_cell';
import { tiptapExtension as TableHeader } from '~/content_editor/extensions/table_header';
import { tiptapExtension as TableRow } from '~/content_editor/extensions/table_row';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_table_button', () => {
......@@ -23,9 +19,7 @@ describe('content_editor/components/toolbar_table_button', () => {
const getNumButtons = () => findDropdown().findAllComponents(GlButton).length;
beforeEach(() => {
editor = createTestEditor({
extensions: [Table, TableCell, TableRow, TableHeader],
});
editor = createTestEditor();
buildWrapper();
});
......
......@@ -2,7 +2,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarTextStyleDropdown from '~/content_editor/components/toolbar_text_style_dropdown.vue';
import { TEXT_STYLE_DROPDOWN_ITEMS } from '~/content_editor/constants';
import { tiptapExtension as Heading } from '~/content_editor/extensions/heading';
import Heading from '~/content_editor/extensions/heading';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_headings_dropdown', () => {
......
import { tiptapExtension as CodeBlockHighlight } from '~/content_editor/extensions/code_block_highlight';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
import { loadMarkdownApiResult } from '../markdown_processing_examples';
import { createTestEditor } from '../test_utils';
......
import { tiptapExtension as HardBreak } from '~/content_editor/extensions/hard_break';
import HardBreak from '~/content_editor/extensions/hard_break';
import { createTestEditor, createDocBuilder } from '../test_utils';
describe('content_editor/extensions/hard_break', () => {
......
......@@ -2,7 +2,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { once } from 'lodash';
import waitForPromises from 'helpers/wait_for_promises';
import * as Image from '~/content_editor/extensions/image';
import Image from '~/content_editor/extensions/image';
import httpStatus from '~/lib/utils/http_status';
import { loadMarkdownApiResult } from '../markdown_processing_examples';
import { createTestEditor, createDocBuilder } from '../test_utils';
......@@ -24,16 +24,16 @@ describe('content_editor/extensions/image', () => {
.fn()
.mockResolvedValue(loadMarkdownApiResult('project_wiki_attachment_image').body);
const { tiptapExtension } = Image.configure({ renderMarkdown, uploadsPath });
tiptapEditor = createTestEditor({ extensions: [tiptapExtension] });
tiptapEditor = createTestEditor({
extensions: [Image.configure({ renderMarkdown, uploadsPath })],
});
({
builders: { doc, p, image },
eq,
} = createDocBuilder({
tiptapEditor,
names: { image: { nodeType: tiptapExtension.name } },
names: { image: { nodeType: Image.name } },
}));
mock = new MockAdapter(axios);
......
import * as Blockquote from '~/content_editor/extensions/blockquote';
import * as Bold from '~/content_editor/extensions/bold';
import * as Dropcursor from '~/content_editor/extensions/dropcursor';
import * as Paragraph from '~/content_editor/extensions/paragraph';
import buildSerializerConfig from '~/content_editor/services/build_serializer_config';
describe('content_editor/services/build_serializer_config', () => {
describe('given one or more content editor extensions', () => {
it('creates a serializer config that collects all extension serializers by type', () => {
const extensions = [Bold, Blockquote, Paragraph];
const serializerConfig = buildSerializerConfig(extensions);
extensions.forEach(({ tiptapExtension, serializer }) => {
const { name, type } = tiptapExtension;
expect(serializerConfig[`${type}s`][name]).toBe(serializer);
});
});
});
describe('given an extension without serializer', () => {
it('does not include the extension in the serializer config', () => {
const serializerConfig = buildSerializerConfig([Dropcursor]);
expect(serializerConfig.marks[Dropcursor.tiptapExtension.name]).toBe(undefined);
expect(serializerConfig.nodes[Dropcursor.tiptapExtension.name]).toBe(undefined);
});
});
describe('given no extensions', () => {
it('creates an empty serializer config', () => {
expect(buildSerializerConfig()).toStrictEqual({
marks: {},
nodes: {},
});
});
});
});
......@@ -32,13 +32,15 @@ describe('content_editor/services/create_editor', () => {
it('allows providing external content editor extensions', async () => {
const labelReference = 'this is a ~group::editor';
const { tiptapExtension, serializer } = createTestContentEditorExtension();
renderMarkdown.mockReturnValueOnce(
'<p>this is a <span data-reference="label" data-label-name="group::editor">group::editor</span></p>',
);
editor = createContentEditor({
renderMarkdown,
extensions: [createTestContentEditorExtension()],
extensions: [tiptapExtension],
serializerConfig: { nodes: { [tiptapExtension.name]: serializer } },
});
await editor.setSerializedContent(labelReference);
......
......@@ -4,10 +4,10 @@ import {
INPUT_RULE_TRACKING_ACTION,
CONTENT_EDITOR_TRACKING_LABEL,
} from '~/content_editor/constants';
import { tiptapExtension as BulletList } from '~/content_editor/extensions/bullet_list';
import { tiptapExtension as CodeBlockLowlight } from '~/content_editor/extensions/code_block_highlight';
import { tiptapExtension as Heading } from '~/content_editor/extensions/heading';
import { tiptapExtension as ListItem } from '~/content_editor/extensions/list_item';
import BulletList from '~/content_editor/extensions/bullet_list';
import CodeBlockLowlight from '~/content_editor/extensions/code_block_highlight';
import Heading from '~/content_editor/extensions/heading';
import ListItem from '~/content_editor/extensions/list_item';
import trackInputRulesAndShortcuts from '~/content_editor/services/track_input_rules_and_shortcuts';
import { ENTER_KEY, BACKSPACE_KEY } from '~/lib/utils/keys';
import { createTestEditor } from '../test_utils';
......
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