Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
rdmd detail work (#568)
Browse files Browse the repository at this point in the history
* a shit ton of little rdmd detail work

- install css-loader for the explorer
- fix <Image[align]> attr
- whitelist <span/> tags
- whitelist [class] attrs
- despecify core GFM styles (font-size; table display)
- update default <Callout/> warning emoji
- stretch <Headers/> to 100% width
- fix various null states
- show links for flavored <Embed/> blocks
- better comments and defaults for <CodeTabs/>
- better dark color scheme for pinned blocks

* update tests

* revert dist changes
  • Loading branch information
rafegoldberg authored Mar 28, 2020
1 parent 99f3395 commit 79ae0fd
Show file tree
Hide file tree
Showing 23 changed files with 1,314 additions and 93 deletions.
1,168 changes: 1,153 additions & 15 deletions packages/api-explorer/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/api-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"devDependencies": {
"@readme/eslint-config": "^2.0.0",
"@readme/oas-examples": "^3.0.0",
"css-loader": "^3.4.2",
"eslint": "^6.5.0",
"jest": "^25.1.0",
"jsinspect": "^0.12.6",
Expand Down
9 changes: 9 additions & 0 deletions packages/api-explorer/style/markdown-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
> :not(.pin) {
display: none !important;
}
> .pin {
.reference-layout-column & {
color: #bbbec1;
p { color: inherit !important }
> .CodeTabs, > pre {
filter: invert(.9) !important;
}
}
}
}
.hub-reference-theme-column & {
& {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Components Embed 1`] = `"<div class=\\"embed\\"><a href=\\"https://gist.github.com/chaddy81/f852004d6d1510eec1f6\\" rel=\\"noopener noreferrer\\" style=\\"display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-decoration: none;\\" target=\\"_blank\\"><b style=\\"color: rgb(51, 51, 51);\\">View Embed:</b> <span style=\\"opacity: 0.75;\\">https://gist.github.com/chaddy81/f852004d6d1510eec1f6</span></a></div>"`;
32 changes: 16 additions & 16 deletions packages/markdown/__tests__/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@ exports[`anchor target: should allow _blank if using HTML 1`] = `"<p><a href=\\"
exports[`anchor target: should default to _self 1`] = `"<p><a href=\\"https://example.com\\" target=\\"_self\\" title=\\"\\">test</a></p>"`;
exports[`anchors 1`] = `
"<p><a href=\\"http://example.com\\" target=\\"_self\\" title=\\"\\">link</a>
<a href=\\"\\" target=\\"_self\\" title=\\"\\">xss</a>
<a href=\\"/docs/slug\\" target=\\"_self\\" title=\\"\\" class=\\"doc-link\\" data-sidebar=\\"slug\\">doc</a>
<a href=\\"/reference-link/slug\\" target=\\"_self\\" title=\\"\\">ref</a>
<a href=\\"/changelog/slug\\" target=\\"_self\\" title=\\"\\">blog</a>
<a href=\\"/changelog/slug\\" target=\\"_self\\" title=\\"\\">changelog</a>
"<p><a href=\\"http://example.com\\" target=\\"_self\\" title=\\"\\">link</a><br>
<a href=\\"\\" target=\\"_self\\" title=\\"\\">xss</a><br>
<a href=\\"/docs/slug\\" target=\\"_self\\" title=\\"\\" class=\\"doc-link\\" data-sidebar=\\"slug\\">doc</a><br>
<a href=\\"/reference-link/slug\\" target=\\"_self\\" title=\\"\\">ref</a><br>
<a href=\\"/changelog/slug\\" target=\\"_self\\" title=\\"\\">blog</a><br>
<a href=\\"/changelog/slug\\" target=\\"_self\\" title=\\"\\">changelog</a><br>
<a href=\\"/page/slug\\" target=\\"_self\\" title=\\"\\">page</a></p>"
`;
exports[`anchors with baseUrl 1`] = `
"<p><a href=\\"doc:slug\\">doc</a>
<a href=\\"ref:slug\\">ref</a>
<a href=\\"blog:slug\\">blog</a>
<a href=\\"changelog:slug\\">changelog</a>
<a href=\\"page:slug\\">page</a>
"<p><a href=\\"doc:slug\\">doc</a><br>
<a href=\\"ref:slug\\">ref</a><br>
<a href=\\"blog:slug\\">blog</a><br>
<a href=\\"changelog:slug\\">changelog</a><br>
<a href=\\"page:slug\\">page</a><br>
</p>"
`;
exports[`check list items 1`] = `
"<ul>
"<ul class=\\"contains-task-list\\">
<li class=\\"task-list-item\\"><input type=\\"checkbox\\" disabled=\\"\\"> checklistitem1</li>
<li class=\\"task-list-item\\"><input type=\\"checkbox\\" disabled=\\"\\" checked=\\"\\"> checklistitem1</li>
</ul>"
`;
exports[`emojis 1`] = `
"<p><img src=\\"/img/emojis/joy.png\\" alt=\\":joy:\\" title=\\":joy:\\" class=\\"emoji\\" height=\\"auto\\" width=\\"auto\\" caption=\\"\\">
<i class=\\"fa fa-lock\\"></i>
"<p><img src=\\"/img/emojis/joy.png\\" alt=\\":joy:\\" title=\\":joy:\\" class=\\"emoji\\" align=\\"absmiddle\\" height=\\"20\\" width=\\"20\\" caption=\\"\\"><br>
<i class=\\"fa fa-lock\\"></i><br>
:unknown-emoji:</p>"
`;
Expand Down Expand Up @@ -644,14 +644,14 @@ exports[`export multiple Markdown renderers renders plain markdown as React 1`]
</React.Fragment>
`;

exports[`image 1`] = `"<p><img src=\\"http://example.com/image.png\\" alt=\\"Image\\" caption=\\"\\" height=\\"auto\\" width=\\"auto\\"></p>"`;
exports[`image 1`] = `"<p><img src=\\"http://example.com/image.png\\" alt=\\"Image\\" align=\\"\\" caption=\\"\\" height=\\"auto\\" title=\\"\\" width=\\"auto\\"></p>"`;
exports[`list items 1`] = `
"<ul>
<li>listitem1</li>
</ul>"
`;
exports[`magic image 1`] = `"<figure width=\\"auto\\" height=\\"auto\\" style=\\"width: auto;\\"><img src=\\"https://files.readme.io/6f52e22-man-eating-pizza-and-making-an-ok-gesture.jpg\\" alt=\\"A guy. Eating pizza. And making an OK gesture.\\" title=\\"man-eating-pizza-and-making-an-ok-gesture.jpg\\" caption=\\"A guy. Eating pizza. And making an OK gesture.\\" align=\\"\\" height=\\"\\" width=\\"\\"><figcaption>A guy. Eating pizza. And making an OK gesture.</figcaption></figure>"`;
exports[`magic image 1`] = `"<figure align=\\"\\" width=\\"auto\\" height=\\"auto\\" style=\\"width: auto;\\"><img src=\\"https://files.readme.io/6f52e22-man-eating-pizza-and-making-an-ok-gesture.jpg\\" alt=\\"A guy. Eating pizza. And making an OK gesture.\\" title=\\"man-eating-pizza-and-making-an-ok-gesture.jpg\\" caption=\\"A guy. Eating pizza. And making an OK gesture.\\" align=\\"\\" height=\\"auto\\" width=\\"auto\\"><figcaption>A guy. Eating pizza. And making an OK gesture.</figcaption></figure>"`;
exports[`tables 1`] = `"<table><thead><tr><th>Tables</th><th style=\\"text-align: center;\\">Are</th><th style=\\"text-align: right;\\">Cool</th></tr></thead><tbody><tr><td>col 3 is</td><td style=\\"text-align: center;\\">right-aligned</td><td style=\\"text-align: right;\\">$1600</td></tr><tr><td>col 2 is</td><td style=\\"text-align: center;\\">centered</td><td style=\\"text-align: right;\\">$12</td></tr><tr><td>zebra stripes</td><td style=\\"text-align: center;\\">are neat</td><td style=\\"text-align: right;\\">$1</td></tr></tbody></table>"`;
5 changes: 4 additions & 1 deletion packages/markdown/__tests__/components.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,14 @@ world
const wrap = mount(
markdown.react('[Embed Title](https://gist.github.com/chaddy81/f852004d6d1510eec1f6 "@jsfiddle")')
);
expect(wrap.html()).toBe('<div class="embed"><div class="embed-media"></div></div>');
expect(wrap.html()).toMatchSnapshot();
});

it('Heading', () => {
const wrap = mount(markdown.react('### Heading Level 3\n\n### Heading Level 3'));
expect(wrap.find('Heading')).toHaveLength(2);

const blank = mount(markdown.react('Pretest.\n\n###\n\nPosttest.'));
expect(blank.find('Heading').text()).toBe('');
});
});
2 changes: 1 addition & 1 deletion packages/markdown/__tests__/flavored-compilers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const rehypeSanitize = require('rehype-sanitize');
const parseCallouts = require('../processor/parse/flavored/callout');
const parseCodeTabs = require('../processor/parse/flavored/code-tabs');
const parseEmbeds = require('../processor/parse/flavored/embed');
const options = require('../processor/options.json').markdownOptions;
const options = require('../options.json').markdownOptions;
const parser = require('../processor/parse/magic-block-parser');

const DivCompiler = require('../processor/compile/div');
Expand Down
2 changes: 1 addition & 1 deletion packages/markdown/__tests__/flavored-parsers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const rehypeSanitize = require('rehype-sanitize');

const parseCallouts = require('../processor/parse/flavored/callout');
const parseCodeTabs = require('../processor/parse/flavored/code-tabs');
const options = require('../processor/options.json').markdownOptions;
const options = require('../options.json').markdownOptions;

const sanitize = { attributes: [], tagNames: [] };
const process = (text, opts = options) =>
Expand Down
4 changes: 2 additions & 2 deletions packages/markdown/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const React = require('react');
const BaseUrlContext = require('../contexts/BaseUrl');

const markdown = require('../index');
const settings = require('../processor/options.json');
const settings = require('../options.json');

test('image', () => {
expect(mount(markdown.default('![Image](http://example.com/image.png)', settings)).html()).toMatchSnapshot();
Expand Down Expand Up @@ -193,7 +193,7 @@ test('should strip dangerous iframe tag', () => {

test('should strip dangerous img attributes', () => {
expect(mount(markdown.default('<img src="x" onerror="alert(\'charlie\')">', settings)).html()).toBe(
'<img src="x" alt="" caption="" height="auto" width="auto">'
'<img src="x" align="" alt="" caption="" height="auto" title="" width="auto">'
);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/markdown/__tests__/magic-block-parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const remarkParse = require('remark-parse');
const rehypeSanitize = require('rehype-sanitize');

const parser = require('../processor/parse/magic-block-parser');
const options = require('../processor/options.json').markdownOptions;
const options = require('../options.json').markdownOptions;

const sanitize = { attributes: [] };
const process = (text, opts = options) =>
Expand Down
19 changes: 18 additions & 1 deletion packages/markdown/components/Embed.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,24 @@ class Embed extends React.Component {
render() {
return (
<div className="embed">
<div className="embed-media" dangerouslySetInnerHTML={{ __html: this.props.html }}></div>
{this.props.html ? (
<div className="embed-media" dangerouslySetInnerHTML={{ __html: this.props.html }}></div>
) : (
<a
href={this.props.url}
rel="noopener noreferrer"
style={{
display: 'block',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
textDecoration: 'none',
}}
target="_blank"
>
<b style={{ color: '#333' }}>View Embed:</b> <span style={{ opacity: 0.75 }}>{this.props.url}</span>
</a>
)}
</div>
);
}
Expand Down
9 changes: 6 additions & 3 deletions packages/markdown/components/Heading/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ function generateHeadingId(e, anchors) {
}

function Heading(props) {
if (!props.children) return '';
const id = `section-${generateHeadingId(props.children[0], props.anchors)}`;
return React.createElement(props.tag, { className: 'heading header-scroll' }, [
<div key={`anchor-${id}`} className="anchor waypoint" id={id} />,
<div key={`heading-text-${id}`}>{props.children}</div>,
<div key={`heading-anchor-${id}`} className="heading-anchor anchor waypoint" id={id} />,
<div key={`heading-text-${id}`} className="heading-text">
{props.children}
</div>,
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a key={`anchor-icon-${id}`} className="fa fa-anchor" href={`#${id}`} />,
<a key={`heading-anchor-icon-${id}`} className="heading-anchor-icon fa fa-anchor" href={`#${id}`} />,
]);
}

Expand Down
9 changes: 6 additions & 3 deletions packages/markdown/components/Heading/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
justify-content: flex-start;
align-items: center;
position: relative;
.anchor {
.heading-text {
flex: 1 100%;
}
.heading-anchor {
top: -1rem !important;
}
.anchor, .fa-anchor {
.heading-anchor, .heading-anchor-icon {
position: absolute !important;
display: inline !important;
order: -1;
Expand All @@ -23,7 +26,7 @@
opacity: 1;
}
}
&:not(:hover) .fa-anchor {
&:not(:hover) .heading-anchor-icon {
opacity: 0;
}
}
28 changes: 19 additions & 9 deletions packages/markdown/components/Image.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ const React = require('react');
const PropTypes = require('prop-types');

const Image = props => {
const [title, align, width = 'auto', height = 'auto'] = props.title ? props.title.split(', ') : [];
const { align, title, alt, width, height, caption } = props;
const extras = { align, width, height };
if (props.caption)
if (caption)
return (
<figure {...extras} style={{ width: extras.width }}>
<img {...props} alt={props.alt} title={title} />
<figcaption>{props.caption}</figcaption>
<figure {...extras} style={{ width }}>
<img {...props} alt={alt} title={title} {...extras} />
<figcaption>{caption}</figcaption>
</figure>
);
return <img {...props} alt={props.alt} title={title} {...extras} />;
return <img {...props} alt={alt} title={title} {...extras} />;
};

Image.propTypes = {
Expand All @@ -30,13 +30,23 @@ Image.defaultProps = {
align: '',
alt: '',
caption: '',
height: '',
height: 'auto',
src: '',
title: '',
width: '',
width: 'auto',
};

module.exports = sanitizeSchema => {
sanitizeSchema.attributes.img = ['className', 'title', 'alt', 'width', 'height', 'align', 'src', 'caption'];
sanitizeSchema.attributes.img = [
'className',
'title',
'alt',
'width',
'height',
'align',
'src',
'caption',
'longDesc',
];
return Image;
};
9 changes: 6 additions & 3 deletions packages/markdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ const rdmeCalloutCompiler = require('./processor/compile/callout');
const rdmePinCompiler = require('./processor/compile/pin');

// Processor Option Defaults
const options = require('./processor/options.json');
const options = require('./options.json');

// Sanitization Schema Defaults
sanitize.clobberPrefix = '';

sanitize.tagNames.push('span');
sanitize.attributes['*'].push('class', 'className');

sanitize.tagNames.push('rdme-pin');

sanitize.tagNames.push('embed');
Expand All @@ -79,10 +82,10 @@ export function normalize(blocks) {
// normalize magic block lines
// eslint-disable-next-line no-param-reassign
blocks = blocks
.replace(/\[block:/g, '\n[block:')
.replace(/\[block:/g, '\n\n[block:')
.replace(/\[\/block\]/g, '[/block]\n')
.trim()
.replace(/^(#+)(.+)/gm, '$1 $2');
.replace(/^(#+)(.+\n\n)/gm, '$1 $2');
return `${blocks}\n\n `;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"correctnewlines": true,
"correctnewlines": false,
"markdownOptions": {
"fences": true,
"commonmark": true,
Expand Down
30 changes: 30 additions & 0 deletions packages/markdown/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/markdown/processor/parse/flavored/callout.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function tokenizer(eat, value) {
const style = {
ℹ️: 'info',
'⚠️': 'warn',
'🚧': 'warn',
'👍': 'okay',
'✅': 'okay',
'❗️': 'error',
Expand Down
12 changes: 11 additions & 1 deletion packages/markdown/processor/parse/flavored/code-tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ function tokenizer(eat, value) {
.split('```')
.filter(val => val.trim())
.map(val => {
/**
* For each of our adjacent code blocks we'll:
* 1) normalize any unbalanced tilde wrappers
* 2) split the matching block in to three parts:
* - syntax [lang] extension
* - [meta] tab name (optional)
* - the [code] snippet text
* @todo: radically shit; simplify this assignment/manipulation logic
*/
// eslint-disable-next-line no-param-reassign
val = ['```', val.replace('```', ''), '```'].join('');
// eslint-disable-next-line unicorn/no-unsafe-regex
const [, lang, meta = null, code = ''] = /```(\w+)?(?: ([\w-.]+))?\s?([^]+)```/gm.exec(val);
const [, lang, meta = null, code = ''] = /```([^\s]+)?(?: *([^\n]+))?\s?([^]+)```/gm.exec(val);

return {
type: 'code',
className: 'tab-panel',
Expand Down
Loading

0 comments on commit 79ae0fd

Please sign in to comment.