diff --git a/src/components/Widgets/MarkitupReactRenderer.js b/src/components/Widgets/MarkitupReactRenderer.js new file mode 100644 index 00000000..26137f7d --- /dev/null +++ b/src/components/Widgets/MarkitupReactRenderer.js @@ -0,0 +1,69 @@ +import React, { PropTypes } from 'react'; +import MarkupIt, { Syntax, JSONUtils } 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, +}; + +export default class MarkitupReactRenderer extends React.Component { + + renderToken = (token) => { + const { type, data, text, tokens } = token; + const element = defaultRenderers[type]; + + // Only render if type is registered as renderer + if (typeof element !== 'undefined') { + let children = null; + if (Array.isArray(tokens) && tokens.length) { + children = tokens.map(this.renderToken); + } else if (type === 'text') { + children = text; + } + if (element !== null) { + // If this is a react element + return React.createElement(element, data, children); + } else { + // If this is a text node + return children; + } + } + return null; + } + + render() { + const { value, syntax } = this.props; + + if (typeof this.parser === 'undefined') { + this.parser = new MarkupIt(syntax); + } + + const content = this.parser.toContent(value); + const json = JSONUtils.encode(content); + // console.log(JSON.stringify(json, null, 2)); + + return ( +
{this.renderToken(json.token)}
+ ); + } +} + +MarkitupReactRenderer.propTypes = { + value: PropTypes.string, + syntax: PropTypes.instanceOf(Syntax).isRequired +}; diff --git a/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js b/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js new file mode 100644 index 00000000..65ede560 --- /dev/null +++ b/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js @@ -0,0 +1,70 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import markdownSyntax from 'markup-it/syntaxes/markdown'; +import htmlSyntax from 'markup-it/syntaxes/html'; +import MarkitupReactRenderer from '../MarkitupReactRenderer'; + +describe('MarkitupReactRenderer', () => { + it('should render markdown', () => { + const value = ` +# H1 + +Text with **bold** & _em_ elements + +## H2 + +* ul item 1 +* ul item 2 + +### H3 + +1. ol item 1 +1. ol item 2 +1. ol item 3 + +#### H4 + +[link title](http://google.com) + +##### H5 + +![alt text](https://pbs.twimg.com/profile_images/678903331176214528/TQTdqGwD.jpg) + +###### H6 + +`; + const component = renderer.create( + + ); + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should support custom syntax', () => { + const value = ` +`; + const component = renderer.create( + + ); + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('should render HTML', () => { + const value = '

Paragraph with inline element

'; + const component = renderer.create( + + ); + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap b/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap new file mode 100644 index 00000000..2f959a8f --- /dev/null +++ b/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap @@ -0,0 +1,90 @@ +exports[`MarkitupReactRenderer should render HTML 1`] = ` +
+
+

+ Paragraph with + + inline + + element +

+
+
+`; + +exports[`MarkitupReactRenderer should render markdown 1`] = ` +
+
+

+ H1 +

+

+ Text with + + bold + + & + + em + + elements +

+

+ H2 +

+
    +
  • + ul item 1 +
  • +
  • + ul item 2 +
  • +
+

+ H3 +

+
    +
  1. + ol item 1 +
  2. +
  3. + ol item 2 +
  4. +
  5. + ol item 3 +
  6. +
+

+ H4 +

+

+ + link title + +

+
+ H5 +
+

+ alt text +

+
+ H6 +
+
+
+`;