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

View File

@ -1,3 +1,5 @@
/* eslint max-len:0 */
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import markdownSyntax from 'markup-it/syntaxes/markdown'; import markdownSyntax from 'markup-it/syntaxes/markdown';
@ -5,39 +7,44 @@ import htmlSyntax from 'markup-it/syntaxes/html';
import MarkitupReactRenderer from '../MarkitupReactRenderer'; import MarkitupReactRenderer from '../MarkitupReactRenderer';
describe('MarkitupReactRenderer', () => { describe('MarkitupReactRenderer', () => {
it('should re-render properly after a value and syntax update', () => {
const component = shallow( describe('basics', () => {
<MarkitupReactRenderer it('should re-render properly after a value and syntax update', () => {
value="# Title" const component = shallow(
syntax={markdownSyntax} <MarkitupReactRenderer
/> value="# Title"
); syntax={markdownSyntax}
const tree1 = component.html(); />
component.setProps({ );
value: '<h1>Title</h1>', const tree1 = component.html();
syntax: htmlSyntax 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', () => { describe('Markdown rendering', () => {
const component = shallow( describe('General', () => {
<MarkitupReactRenderer it('should render markdown', () => {
value="# Title" const value = `
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 = `
# H1 # H1
Text with **bold** & _em_ elements Text with **bold** & _em_ elements
@ -63,18 +70,69 @@ Text with **bold** & _em_ elements
###### H6 ###### H6
`; `;
const component = shallow( const component = shallow(
<MarkitupReactRenderer <MarkitupReactRenderer
value={value} value={value}
syntax={markdownSyntax} syntax={markdownSyntax}
/> />
); );
const tree = component.html(); const tree = component.html();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });
});
it('should render HTML as is using Markdown', () => { describe('Links', () => {
const value = ` 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 # Title
<dl> <dl>
@ -82,37 +140,43 @@ Text with **bold** & _em_ elements
<dd>Testing HTML in Markdown</dd> <dd>Testing HTML in Markdown</dd>
</dl> </dl>
`; `;
const component = shallow( const component = shallow(
<MarkitupReactRenderer <MarkitupReactRenderer
value={value} value={value}
syntax={markdownSyntax} syntax={markdownSyntax}
/> />
); );
const tree = component.html(); const tree = component.html();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
});
});
}); });
it('should support custom syntax', () => { describe('custom elements', () => {
const value = ''; it('should support custom syntax', () => {
const component = shallow( const value = '';
<MarkitupReactRenderer const component = shallow(
value={value} <MarkitupReactRenderer
syntax={markdownSyntax} value={value}
/> syntax={markdownSyntax}
); />
const tree = component.html(); );
expect(tree).toMatchSnapshot(); const tree = component.html();
expect(tree).toMatchSnapshot();
});
}); });
it('should render HTML', () => { describe('HTML rendering', () => {
const value = '<p class="test class">Paragraph with <em>inline</em> element</p>'; it('should render HTML', () => {
const component = shallow( const value = '<p class="test class">Paragraph with <em>inline</em> element</p>';
<MarkitupReactRenderer const component = shallow(
value={value} <MarkitupReactRenderer
syntax={htmlSyntax} value={value}
/> syntax={htmlSyntax}
); />
const tree = component.html(); );
expect(tree).toMatchSnapshot(); const tree = component.html();
expect(tree).toMatchSnapshot();
});
}); });
}); });