diff --git a/packages/markdown/__tests__/__snapshots__/magic-block-parser.test.js.snap b/packages/markdown/__tests__/__snapshots__/magic-block-parser.test.js.snap index 34866a24a..712f3e90f 100644 --- a/packages/markdown/__tests__/__snapshots__/magic-block-parser.test.js.snap +++ b/packages/markdown/__tests__/__snapshots__/magic-block-parser.test.js.snap @@ -1,5 +1,12 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Blank Magic Blocks 1`] = ` +Object { + "children": Array [], + "type": "root", +} +`; + exports[`Parse Magic Blocks Callout Blocks 1`] = ` Object { "children": Array [ @@ -99,6 +106,75 @@ alert('test');", }, "type": "code-tabs", }, + Object { + "children": Array [ + Object { + "className": "tab-panel", + "data": Object { + "hName": "code", + "hProperties": Object { + "lang": "javascript", + "meta": null, + }, + }, + "lang": "javascript", + "meta": null, + "type": "code", + "value": "$http.post('/someUrl', data).success(successCallback); + +alert('test');", + }, + ], + "data": Object { + "className": "pin", + "hName": "rdme-pin", + }, + "type": "rdme-pin", + }, + ], + "type": "root", +} +`; + +exports[`Parse Magic Blocks Custom Callout Blocks 1`] = ` +Object { + "children": Array [ + Object { + "children": Array [ + Object { + "children": Array [ + Object { + "type": "text", + "value": "✨ ", + }, + Object { + "type": "text", + "value": "Callout Title", + }, + ], + "type": "paragraph", + }, + Object { + "children": Array [ + Object { + "type": "text", + "value": "Lorem ipsum dolor sit amet...", + }, + ], + "type": "paragraph", + }, + ], + "data": Object { + "hName": "rdme-callout", + "hProperties": Object { + "icon": "✨", + "theme": "custom_theme", + "title": "Callout Title", + "value": "Lorem ipsum dolor sit amet...", + }, + }, + "type": "rdme-callout", + }, ], "type": "root", } @@ -159,6 +235,14 @@ Object { "depth": 2, "type": "heading", }, + Object { + "lang": null, + "meta": null, + "type": "code", + "value": "[block:api-header] +{} +[/block]", + }, ], "type": "root", } diff --git a/packages/markdown/__tests__/magic-block-parser.test.js b/packages/markdown/__tests__/magic-block-parser.test.js index 29af12812..02d32c5b5 100644 --- a/packages/markdown/__tests__/magic-block-parser.test.js +++ b/packages/markdown/__tests__/magic-block-parser.test.js @@ -15,6 +15,12 @@ const process = (text, opts = options) => .use(rehypeSanitize) .parse(text); +test('Blank Magic Blocks', () => { + const text = `[block:api-header] + [/block]`; + expect(process(text)).toMatchSnapshot(); +}); + test('Sanitize Schema', () => { parser.sanitize(sanitize); expect(sanitize).toMatchSnapshot(); @@ -27,6 +33,10 @@ describe('Parse Magic Blocks', () => { "title": "MagicBlock Conversion", "level": 2 } + [/block] + + [block:api-header] + {} [/block]`; expect(process(text)).toMatchSnapshot(); }); @@ -68,6 +78,17 @@ describe('Parse Magic Blocks', () => { } ] } + + [/block][block:code] + { + "sidebar": true, + "codes": [ + { + "code": "$http.post('/someUrl', data).success(successCallback);\\n\\nalert('test');", + "language": "javascript" + } + ] + } [/block]`; expect(process(text)).toMatchSnapshot(); }); @@ -120,6 +141,18 @@ describe('Parse Magic Blocks', () => { expect(process(text)).toMatchSnapshot(); }); + it('Custom Callout Blocks', () => { + const text = `[block:callout] + { + "type": "custom_theme", + "icon": "✨", + "title": "Callout Title", + "body": "Lorem ipsum dolor sit amet..." + } + [/block]`; + expect(process(text)).toMatchSnapshot(); + }); + it('Unrecognized Blocks', () => { const text = `[block:unrecognized] { diff --git a/packages/markdown/components/Embed.jsx b/packages/markdown/components/Embed.jsx index 3f2ad4de5..babe12b57 100644 --- a/packages/markdown/components/Embed.jsx +++ b/packages/markdown/components/Embed.jsx @@ -1,74 +1,11 @@ const React = require('react'); const propTypes = require('prop-types'); -// const Embedly = require('embedly'); -// const api = -// process.env.NODE_ENV !== 'test' -// ? new Embedly({ key: '' }) -// : { -// extract: (opts, cb) => { -// cb(false, [{ media: { html: 'testing' } }]); -// }, -// }; - class Embed extends React.Component { - constructor(props) { - super(props); - this.state = { - embedly: false, - }; - } - - // componentDidMount() { - // this.getEmbed(); - // } - - /* istanbul ignore next */ - // getGist(data) { - // const [, gistID] = this.props.url.match( - // /(?:gist.github.com\/(?:.[-_a-zA-Z0-9]+\/)?([-_a-zA-Z0-9]*)(?:\.git|\.js)?)/, - // ); - // fetch(`https://api.github.com/gists/${gistID}`) - // .then(r => r.json()) - // .then(gist => { - // const files = gist.files; - // const keys = Object.keys(files); - // const file = files[keys[0]]; - // data.media.html = `
${file.content}
`; - // this.setState({ embedly: data }); - // }); - // } - - /* istanbul ignore next */ - // getEmbed() { - // api.extract({ url: this.props.url }, (err, obj) => { - // if (err) { - // // eslint-disable-next-line no-console - // console.error(err); - // return err; - // } - // const result = obj[0]; - // if (result.provider_display === 'gist.github.com') return this.getGist(result); - // return this.setState({ embedly: result }); - // }); - // } - render() { return (
-
+
); } diff --git a/packages/markdown/processor/parse/magic-block-parser.js b/packages/markdown/processor/parse/magic-block-parser.js index 742cdf86d..baadea7a3 100644 --- a/packages/markdown/processor/parse/magic-block-parser.js +++ b/packages/markdown/processor/parse/magic-block-parser.js @@ -20,7 +20,15 @@ function tokenize(eat, value) { match = match.trim(); type = type.trim(); - json = (json && JSON.parse(json)) || {}; + try { + json = JSON.parse(json); + } catch (err) { + json = {}; + // eslint-disable-next-line no-console + console.error('Invalid Magic Block JSON:', err); + } + + if (Object.keys(json).length < 1) return eat(match); switch (type) { case 'code': { @@ -57,40 +65,40 @@ function tokenize(eat, value) { { type: 'heading', depth: json.level || 2, - children: this.tokenizeInline(json.title, eat.now()), + children: 'title' in json ? this.tokenizeInline(json.title, eat.now()) : '', }, json ) ); } case 'image': { - return eat(match)( - WrapPinnedBlocks( - json.images.map(img => { - const [url, title] = img.image; - return { - type: 'image', - url, - title, - alt: img.caption, - data: { - hProperties: { - caption: img.caption, - }, - }, - }; - })[0], - json - ) - ); + const imgs = json.images.map(img => { + const [url, title] = img.image; + return { + type: 'image', + url, + title, + alt: img.caption, + data: { + hProperties: { + caption: img.caption, + }, + }, + }; + }); + const img = imgs[0]; + + if (!img.url) return eat(match); + return eat(match)(WrapPinnedBlocks(img, json)); } case 'callout': { - json.type = { + const types = { info: ['ℹ', 'info'], success: ['👍', 'okay'], warning: ['⚠️', 'warn'], danger: ['❗️', 'error'], - }[json.type]; + }; + json.type = json.type in types ? types[json.type] : [json.icon || '👍', json.type]; const [icon, theme] = json.type; return eat(match)( WrapPinnedBlocks( @@ -133,18 +141,16 @@ function tokenize(eat, value) { type: row ? 'tableCell' : 'tableHead', children: this.tokenizeInline(val, eat.now()), }; + // convert falsey values to empty strings + sum[row].children = [...sum[row].children].map(v => v || ''); return sum; }, []); - return eat(match)( - WrapPinnedBlocks( - { - type: 'table', - align: 'align' in json ? json.align : new Array(json.cols).fill('left'), - children, - }, - json - ) - ); + const table = { + type: 'table', + align: 'align' in json ? json.align : new Array(json.cols).fill('left'), + children: children.filter(v => v || false), + }; + return eat(match)(WrapPinnedBlocks(table, json)); } case 'embed': { json.title = json.title || 'Embed'; diff --git a/packages/markdown/processor/parse/variable-parser.js b/packages/markdown/processor/parse/variable-parser.js index 4bac3044e..cffc58524 100644 --- a/packages/markdown/processor/parse/variable-parser.js +++ b/packages/markdown/processor/parse/variable-parser.js @@ -7,6 +7,7 @@ function tokenizeVariable(eat, value, silent) { if (!match) return false; + /* istanbul ignore if */ if (silent) return true; // Escaped variables should just return the text