From af8ea8014264a945d15ce23726424037de99369b Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Mon, 26 Sep 2016 13:31:36 +0200 Subject: [PATCH] Added support for custom renderers --- .../Widgets/MarkitupReactRenderer.js | 19 +++-- .../__tests__/MarkitupReactRenderer.spec.js | 76 ++++++++++++------- .../MarkitupReactRenderer.spec.js.snap | 6 +- 3 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/components/Widgets/MarkitupReactRenderer.js b/src/components/Widgets/MarkitupReactRenderer.js index 2f2e2948..364266bb 100644 --- a/src/components/Widgets/MarkitupReactRenderer.js +++ b/src/components/Widgets/MarkitupReactRenderer.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'; import MarkupIt, { Syntax, BLOCKS, STYLES, ENTITIES } from 'markup-it'; import htmlSyntax from 'markup-it/syntaxes/html'; -const defaultRenderers = { +const defaultSchema = { [BLOCKS.DOCUMENT]: 'article', [BLOCKS.TEXT]: null, [BLOCKS.CODE]: 'code', @@ -41,30 +41,28 @@ const defaultRenderers = { [ENTITIES.HARD_BREAK]: 'br' }; -function renderToken(token, index = 0, key = '0') { +function renderToken(schema, token, index = 0, key = '0') { const type = token.get('type'); const data = token.get('data'); const text = token.get('text'); - const raw = token.get('raw'); const tokens = token.get('tokens'); - const nodeType = defaultRenderers[type]; + const nodeType = schema[type]; key = `${key}.${index}`; // Only render if type is registered as renderer if (typeof nodeType !== 'undefined') { let children = null; if (tokens.size) { - children = tokens.map((token, idx) => renderToken(token, idx, key)); + children = tokens.map((token, idx) => renderToken(schema, token, idx, key)); } else if (type === 'text') { children = text; } if (nodeType !== null) { - + // If this is a function we want to pass the `token` as an argument if (typeof nodeType === 'function') { return nodeType(token); } // If this is a react element - console.log(data.toJS()); return React.createElement( nodeType, { key, ...data.toJS() }, // Add key as a prop @@ -93,13 +91,14 @@ export default class MarkitupReactRenderer extends React.Component { } render() { - const { value } = this.props; + const { value, schema } = this.props; const content = this.parser.toContent(value); - return renderToken(content.get('token')); + return renderToken({ ...defaultSchema, ...schema }, content.get('token')); } } MarkitupReactRenderer.propTypes = { value: PropTypes.string, - syntax: PropTypes.instanceOf(Syntax).isRequired + syntax: PropTypes.instanceOf(Syntax).isRequired, + schema: PropTypes.objectOf(PropTypes.node) }; diff --git a/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js b/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js index 8705e420..67fe203e 100644 --- a/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js +++ b/src/components/Widgets/__tests__/MarkitupReactRenderer.spec.js @@ -2,8 +2,12 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { padStart } from 'lodash'; +import { Map } from 'immutable'; +import MarkupIt from 'markup-it'; import markdownSyntax from 'markup-it/syntaxes/markdown'; import htmlSyntax from 'markup-it/syntaxes/html'; +import reInline from 'markup-it/syntaxes/markdown/re/inline'; import MarkitupReactRenderer from '../MarkitupReactRenderer'; describe('MarkitupReactRenderer', () => { @@ -76,26 +80,24 @@ Text with **bold** & _em_ elements syntax={markdownSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); describe('Headings', () => { for (const heading of [...Array(6).keys()]) { it(`should render Heading ${heading + 1}`, () => { - const value = padStart(' Title', heading + 7, '#') + const value = padStart(' Title', heading + 7, '#'); const component = shallow( ); - const tree = component.html(); - expect(tree).toMatchSnapshot() - }) + expect(component.html()).toMatchSnapshot(); + }); } - }) + }); describe('Lists', () => { it('should render lists', () => { @@ -116,8 +118,7 @@ Text with **bold** & _em_ elements syntax={markdownSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); @@ -136,8 +137,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3] syntax={markdownSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); @@ -150,8 +150,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3] syntax={markdownSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); it('should render code 2', () => { @@ -162,8 +161,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3] syntax={markdownSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); @@ -183,23 +181,50 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3] syntax={markdownSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); }); describe('custom elements', () => { - it('should support custom syntax', () => { - const value = ''; + it('should extend default renderers with custom ones', () => { + const myRule = MarkupIt.Rule('mediaproxy') + .regExp(reInline.link, (state, match) => { + if (match[0].charAt(0) !== '!') { + return; + } + + return { + data: Map({ + alt: match[1], + src: match[2], + title: match[3] + }).filter(Boolean) + }; + }); + + const myCustomSchema = { + 'mediaproxy': (token) => { + const src = token.getIn(['data', 'src']); + const alt = token.getIn(['data', 'alt']); + return {alt}/; + } + }; + + const myMarkdownSyntax = markdownSyntax.addInlineRules(myRule); + const value = ` +## Title + +![mediaproxy test](http://url.to.image) +`; const component = shallow( ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); @@ -212,8 +237,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3] syntax={htmlSyntax} /> ); - const tree = component.html(); - expect(tree).toMatchSnapshot(); + expect(component.html()).toMatchSnapshot(); }); }); }); diff --git a/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap b/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap index 920539bf..74658e3e 100644 --- a/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap +++ b/src/components/Widgets/__tests__/__snapshots__/MarkitupReactRenderer.spec.js.snap @@ -24,8 +24,4 @@ exports[`MarkitupReactRenderer Markdown rendering Links should render links 1`] exports[`MarkitupReactRenderer Markdown rendering Lists should render lists 1`] = `"
  1. ol item 1
  2. ol item 2
    • Sublist 1
    • Sublist 2
    • Sublist 3
      1. Sub-Sublist 1
      2. Sub-Sublist 2
      3. Sub-Sublist 3
  3. ol item 3
"`; -exports[`MarkitupReactRenderer custom elements should extend default renderers with custom ones 1`] = `"

"`; - -exports[`MarkitupReactRenderer custom elements should support custom syntax 1`] = `"
"`; - -exports[`MarkitupReactRenderer custom elements should support custom syntaxes 1`] = `"

"`; +exports[`MarkitupReactRenderer custom elements should extend default renderers with custom ones 1`] = `"

Title

\"mediaproxy

"`;