Support more elements. Import dicitonaries from markup-it. Added more tests.

This commit is contained in:
Andrey Okonetchnikov 2016-09-22 22:34:43 +02:00
parent 9392fdbe30
commit 57688af42e
2 changed files with 169 additions and 91 deletions

View File

@ -1,43 +1,57 @@
import React, { PropTypes } from 'react';
import MarkupIt, { Syntax, JSONUtils } from 'markup-it';
import MarkupIt, { Syntax, JSONUtils, BLOCKS, STYLES, ENTITIES } from 'markup-it';
const defaultRenderers = {
'doc': 'article',
'header_one': 'h1',
'header_two': 'h2',
'header_three': 'h3',
'header_four': 'h4',
'header_five': 'h5',
'header_six': 'h6',
'paragraph': 'p',
'ordered_list': 'ol',
'unordered_list': 'ul',
'list_item': 'li',
'link': 'a',
'image': 'img',
'BOLD': 'strong',
'ITALIC': 'em',
'text': null,
'unstyled': null,
[BLOCKS.DOCUMENT]: 'article',
[BLOCKS.TEXT]: null,
[BLOCKS.CODE]: 'code',
[BLOCKS.BLOCKQUOTE]: 'blockquote',
[BLOCKS.PARAGRAPH]: 'p',
[BLOCKS.FOOTNOTE]: 'footnote',
[BLOCKS.HTML]: (props) => null,
[BLOCKS.HR]: 'hr',
[BLOCKS.HEADING_1]: 'h1',
[BLOCKS.HEADING_2]: 'h2',
[BLOCKS.HEADING_3]: 'h3',
[BLOCKS.HEADING_4]: 'h4',
[BLOCKS.HEADING_5]: 'h5',
[BLOCKS.HEADING_6]: 'h6',
[BLOCKS.TABLE]: 'table',
[BLOCKS.TABLE_ROW]: 'tr',
[BLOCKS.TABLE_CELL]: 'td',
[BLOCKS.OL_LIST]: 'ol',
[BLOCKS.UL_LIST]: 'ul',
[BLOCKS.LIST_ITEM]: 'li',
[STYLES.TEXT]: null,
[STYLES.BOLD]: 'strong',
[STYLES.ITALIC]: 'em',
[STYLES.CODE]: 'code',
[STYLES.STRIKETHROUGH]: 'del',
[ENTITIES.LINK]: 'a',
[ENTITIES.IMAGE]: 'img',
[ENTITIES.FOOTNOTE_REF]: 'sup',
[ENTITIES.HARD_BREAK]: 'br'
};
function renderToken(token, index = 0, key = '0') {
const { type, data, text, tokens } = token;
const element = defaultRenderers[type];
const nodeType = defaultRenderers[type];
key = `${key}.${index}`;
// Only render if type is registered as renderer
if (typeof element !== 'undefined') {
if (typeof nodeType !== 'undefined') {
let children = null;
if (Array.isArray(tokens) && tokens.length) {
children = tokens.map((token, idx) => renderToken(token, idx, key));
} else if (type === 'text') {
children = text;
}
if (element !== null) {
if (nodeType !== null) {
// If this is a react element
return React.createElement(
element,
nodeType,
{ key, ...data }, // Add key as a prop
Array.isArray(children) && children.length === 1
? children[0] : children); // Pass single child if possible

View File

@ -1,3 +1,5 @@
/* eslint max-len:0 */
import React from 'react';
import { shallow } from 'enzyme';
import markdownSyntax from 'markup-it/syntaxes/markdown';
@ -5,39 +7,44 @@ import htmlSyntax from 'markup-it/syntaxes/html';
import MarkitupReactRenderer from '../MarkitupReactRenderer';
describe('MarkitupReactRenderer', () => {
it('should re-render properly after a value and syntax update', () => {
const component = shallow(
<MarkitupReactRenderer
value="# Title"
syntax={markdownSyntax}
/>
);
const tree1 = component.html();
component.setProps({
value: '<h1>Title</h1>',
syntax: htmlSyntax
describe('basics', () => {
it('should re-render properly after a value and syntax update', () => {
const component = shallow(
<MarkitupReactRenderer
value="# Title"
syntax={markdownSyntax}
/>
);
const tree1 = component.html();
component.setProps({
value: '<h1>Title</h1>',
syntax: htmlSyntax
});
const tree2 = component.html();
expect(tree1).toEqual(tree2);
});
it('should not update the parser if syntax didn\'t change', () => {
const component = shallow(
<MarkitupReactRenderer
value="# Title"
syntax={markdownSyntax}
/>
);
const syntax1 = component.instance().props.syntax;
component.setProps({
value: '## Title',
});
const syntax2 = component.instance().props.syntax;
expect(syntax1).toEqual(syntax2);
});
const tree2 = component.html();
expect(tree1).toEqual(tree2);
});
it('should not update the parser if syntax didn\'t change', () => {
const component = shallow(
<MarkitupReactRenderer
value="# Title"
syntax={markdownSyntax}
/>
);
const syntax1 = component.instance().props.syntax;
component.setProps({
value: '## Title',
});
const syntax2 = component.instance().props.syntax;
expect(syntax1).toEqual(syntax2);
});
it('should render markdown', () => {
const value = `
describe('Markdown rendering', () => {
describe('General', () => {
it('should render markdown', () => {
const value = `
# H1
Text with **bold** & _em_ elements
@ -63,18 +70,69 @@ Text with **bold** & _em_ elements
###### H6
`;
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
});
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
});
});
it('should render HTML as is using Markdown', () => {
const value = `
describe('Links', () => {
it('should render links', () => {
const value = `
I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3].
[1]: http://google.com/ "Google"
[2]: http://search.yahoo.com/ "Yahoo Search"
[3]: http://search.msn.com/ "MSN Search"
`;
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
const expected = '<article><p>I get 10 times more traffic from <a href="http://google.com/" title="Google">Google</a> than from <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p></article>';
expect(tree).toEqual(expected);
});
});
describe('Code', () => {
it('should render code', () => {
const value = 'Use the `printf()` function.';
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
const expected = '<article><p>Use the <code>printf()</code> function.</p></article>';
expect(tree).toEqual(expected);
});
it('should render code 2', () => {
const value = '``There is a literal backtick (`) here.``';
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
const expected = '<article><p><code>There is a literal backtick (`) here.</code></p></article>';
expect(tree).toEqual(expected);
});
});
describe('HTML', () => {
it('should render HTML as is using Markdown', () => {
const value = `
# Title
<dl>
@ -82,37 +140,43 @@ Text with **bold** & _em_ elements
<dd>Testing HTML in Markdown</dd>
</dl>
`;
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
});
});
});
it('should support custom syntax', () => {
const value = '';
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
describe('custom elements', () => {
it('should support custom syntax', () => {
const value = '';
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={markdownSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
});
});
it('should render HTML', () => {
const value = '<p class="test class">Paragraph with <em>inline</em> element</p>';
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={htmlSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
describe('HTML rendering', () => {
it('should render HTML', () => {
const value = '<p class="test class">Paragraph with <em>inline</em> element</p>';
const component = shallow(
<MarkitupReactRenderer
value={value}
syntax={htmlSyntax}
/>
);
const tree = component.html();
expect(tree).toMatchSnapshot();
});
});
});