markdown-to-jsx
librarymarkdown-to-jsx
library.complex props
(i.e: JSX components, arrays, objects, anything that isn't scalar) of JSX components within Markdown, it's not allowed.Markdown
component and allowed list of JSX componentsMarkdown
component, which abstracts away the internals of markdown-to-jsx
with proper defaults for our app.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import * as Sentry from '@sentry/node';
import { createLogger } from '@unly/utils-simple-logger';
import deepmerge from 'deepmerge';
import MarkdownToJSX, { MarkdownOptions } from 'markdown-to-jsx';
import React from 'react';
import { Alert, Button, Col, DropdownItem, DropdownMenu, DropdownToggle, Nav, NavItem, NavLink, Row, UncontrolledDropdown as Dropdown } from 'reactstrap';
import { Markdown as MarkdownType } from '../../types/Markdown';
import I18nBtnChangeLocale from '../i18n/I18nBtnChangeLocale';
import I18nLink from '../i18n/I18nLink';
import Tooltip from './SimpleTooltip';
const fileLabel = 'components/utils/Markdown';
const logger = createLogger({ // eslint-disable-line no-unused-vars,@typescript-eslint/no-unused-vars
label: fileLabel,
});
type Props = {
text: MarkdownType;
markdownOptions?: MarkdownOptions;
}
const defaultMarkdownOptions: MarkdownOptions = {
// Make some of our own components available
overrides: { // See https://github.com/probablyup/markdown-to-jsx#optionsoverrides---override-any-html-tags-representation
// All links should open in a new tab, and ensure proper security by default
a: {
component: 'a',
props: {
rel: 'noopener', // Security, avoids external site opened through your site to have control over your site
target: '_blank',
},
},
// Reactstrap whitelisted components
Alert,
Button,
Col,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownToggle,
Nav,
NavItem,
NavLink,
Row,
// Our own components
I18nLink,
I18nBtnChangeLocale,
Tooltip,
},
};
/**
* Display "text" property as Markdown, using the "markdown-to-jsx" library
*
* @param props
*/
const Markdown: React.FunctionComponent<Props> = (props): JSX.Element => {
const { text, markdownOptions: _markdownOptions } = props;
const markdownOptions = deepmerge(defaultMarkdownOptions, _markdownOptions || {});
// If providing a non-string input (like "null" or "undefined") then markdown-to-jsx will crash with "Cannot read property 'replace' of undefined" - See https://github.com/probablyup/markdown-to-jsx/issues/314
if(typeof text !== 'string'){
return null;
}
try {
return (
<MarkdownToJSX
options={markdownOptions}
>
{text}
</MarkdownToJSX>
);
} catch (e) {
// Markdown conversion might crash depending on the content, and we must absolutely avoid that
logger.error(e);
Sentry.withScope((scope): void => {
scope.setContext('props', props);
Sentry.captureException(e);
});
return (
<>
{text}
</>
);
}
};
export default Markdown;
Tooltip
componentTooltip
HTML tag is mapped to SimpleTooltip
, which has been created especially for being used within Markdown, because our Tooltip
JSX component requires complex props, and thus wasn't usable as Markdown.1
2
3
4
5
6
7
const markdownTooltipExample = '<Tooltip text="This is a tooltip text written as text and interpreted as JSX component at runtime"><Button>Some text with a tooltip on click/hover</Button></Tooltip>';
<Markdown
text={markdownTooltipExample}
/>