Refactoring Markdown lifecycle methods to React 16.3 (#19436)

* Removed componentWillReceiveProps

* Memoized markdown factory

* Refactored some tests
This commit is contained in:
Marco Vettorello 2018-06-13 15:17:21 +02:00 committed by GitHub
parent d5589bff48
commit 49724b2996
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 51 deletions

View file

@ -76,10 +76,12 @@ class MarkdownVisComponent extends Component {
*/
render() {
return (
<div className="markdown-vis">
<div
className="markdown-vis"
style={{ fontSize: `${this.props.fontSize}pt` }}
>
<Markdown
data-test-subj="markdownBody"
style={{ fontSize: `${this.props.fontSize}pt` }}
markdown={this.props.markdown}
openLinksInNewTab={this.props.openLinksInNewTab}
/>

View file

@ -28,7 +28,6 @@
-webkit-text-size-adjust: 100%;
line-height: 1.5;
color: @text-color;
font-size: 16px;
word-wrap: break-word;
}

View file

@ -19,17 +19,21 @@
import './github_markdown.less';
import classNames from 'classnames';
import React, { Component } from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import MarkdownIt from 'markdown-it';
import { memoize } from 'lodash';
/**
* Return a memoized markdown rendering function that use the specified
* whiteListedRules and openLinksInNewTab configurations.
* @param {Array of Strings} whiteListedRules - white list of markdown rules
* list of rules can be found at https://github.com/markdown-it/markdown-it/issues/361
* @param {Boolean} openLinksInNewTab
* @return {MarkdownIt}
* @return {Function} Returns an Object to use with dangerouslySetInnerHTML
* with the rendered markdown HTML
*/
export function markdownFactory(whiteListedRules, openLinksInNewTab = false) {
export const markdownFactory = memoize((whiteListedRules = [], openLinksInNewTab = false) => {
let markdownIt;
// It is imperitive that the html config property be set to false, to mitigate XSS: the output of markdown-it is
@ -56,70 +60,42 @@ export function markdownFactory(whiteListedRules, openLinksInNewTab = false) {
return originalLinkRender(tokens, idx, options, env, self);
};
}
return markdownIt;
}
export class Markdown extends Component {
constructor(props) {
super(props);
this.markdownIt = markdownFactory(this.props.whiteListedRules, this.props.openLinksInNewTab);
this.state = {
renderedMarkdown: this.transformMarkdown(this.props),
};
}
/**
* This method is used to actually render markdown from the passed parameter
* This method is used to render markdown from the passed parameter
* into HTML. It will just return an empty string when the markdown is empty.
* Since we want to use this with dangerouslySetInnerHTML, this method returns
* the required object format with an __html key in it.
* @param {String} markdown - The markdown String
* @return {String} - Returns the rendered HTML as string.
*/
transformMarkdown(params) {
if (!params.markdown) {
return { __html: '' };
}
return { __html: this.markdownIt.render(params.markdown) };
}
componentWillReceiveProps(props) {
const hasOpenLinksInNewTabChanged = props.openLinksInNewTab !== this.props.openLinksInNewTab;
const hasMarkdownChanged = props.markdown !== this.props.markdown;
const hasWhiteListerRulesChanged = props.whiteListedRules !== this.props.whiteListedRules;
if (hasOpenLinksInNewTabChanged || hasWhiteListerRulesChanged) {
this.markdownIt = markdownFactory(props.whiteListedRules, props.openLinksInNewTab);
}
if (hasMarkdownChanged || hasOpenLinksInNewTabChanged || hasWhiteListerRulesChanged) {
this.setState({
renderedMarkdown: this.transformMarkdown(props),
});
}
}
return (markdown) => {
return markdown ? markdownIt.render(markdown) : '';
};
}, (whiteListedRules = [], openLinksInNewTab = false) => {
return whiteListedRules.join('_').concat(openLinksInNewTab);
});
export class Markdown extends PureComponent {
render() {
const {
className,
markdown, //eslint-disable-line no-unused-vars
openLinksInNewTab, //eslint-disable-line no-unused-vars
whiteListedRules, //eslint-disable-line no-unused-vars
markdown,
openLinksInNewTab,
whiteListedRules,
...rest
} = this.props;
const classes = classNames('markdown-body', className);
const markdownRenderer = markdownFactory(whiteListedRules, openLinksInNewTab);
const renderedMarkdown = markdownRenderer(markdown);
return (
<div
className={classes}
{...rest}
className={classes}
/*
* Justification for dangerouslySetInnerHTML:
* The Markdown Visulization is, believe it or not, responsible for rendering Markdown.
* This relies on `markdown-it` to produce safe and correct HTML.
*/
dangerouslySetInnerHTML={this.state.renderedMarkdown} //eslint-disable-line react/no-danger
dangerouslySetInnerHTML={{ __html: renderedMarkdown }} //eslint-disable-line react/no-danger
/>
);
}