mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-24 04:33:10 +00:00
Merge pull request #1205 from DSchau/gatsby-source-filesystem/home-example
feat: tweak plugin to use onCreateNode API, and get hot reloading working
This commit is contained in:
@@ -33,14 +33,14 @@ module.exports = {
|
||||
{
|
||||
resolve: 'gatsby-source-filesystem',
|
||||
options: {
|
||||
path: `${__dirname}/src/pages`,
|
||||
name: 'pages',
|
||||
path: `${__dirname}/src/pages`,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-source-filesystem',
|
||||
options: {
|
||||
name: 'packages',
|
||||
name: 'content',
|
||||
path: `${__dirname}/content/`,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
const {readdirSync, readFileSync} = require('fs');
|
||||
const {join, resolve} = require('path');
|
||||
const crypto = require(`crypto`);
|
||||
|
||||
const createContentDigest = obj =>
|
||||
crypto
|
||||
.createHash(`md5`)
|
||||
.update(obj)
|
||||
.digest(`hex`);
|
||||
|
||||
// Store code snippets in GraphQL for the home page examples.
|
||||
// Snippets will be matched with markdown templates of the same name.
|
||||
exports.sourceNodes = ({graphql, actions}) => {
|
||||
exports.onCreateNode = async ({actions, node, loadNodeContent}) => {
|
||||
const {createNode} = actions;
|
||||
const {absolutePath, ext, name, relativeDirectory, sourceInstanceName} = node;
|
||||
|
||||
const path = resolve(__dirname, '../../content/home/examples');
|
||||
const files = readdirSync(path);
|
||||
|
||||
files.forEach(file => {
|
||||
if (file.match(/\.js$/)) {
|
||||
const code = readFileSync(join(path, file), 'utf8');
|
||||
const id = file.replace(/\.js$/, '');
|
||||
|
||||
createNode({
|
||||
id,
|
||||
children: [],
|
||||
parent: 'EXAMPLES',
|
||||
internal: {
|
||||
type: 'ExampleCode',
|
||||
contentDigest: JSON.stringify(code),
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
if (
|
||||
sourceInstanceName === 'content' &&
|
||||
relativeDirectory === 'home/examples' &&
|
||||
ext === '.js'
|
||||
) {
|
||||
const code = await loadNodeContent(node);
|
||||
createNode({
|
||||
id: name,
|
||||
children: [],
|
||||
parent: node.id,
|
||||
code,
|
||||
mdAbsolutePath: absolutePath.replace(/\.js$/, '.md'),
|
||||
internal: {
|
||||
type: 'ExampleCode',
|
||||
contentDigest: createContentDigest(JSON.stringify(code)),
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -38,6 +38,12 @@ class CodeEditor extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.code !== nextProps.code) {
|
||||
this.setState(this._updateState(nextProps.code));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {children} = this.props;
|
||||
const {
|
||||
@@ -68,211 +74,169 @@ class CodeEditor extends Component {
|
||||
<LiveProvider code={showJSX ? code : compiledES6} mountStylesheet={false}>
|
||||
<div
|
||||
css={{
|
||||
[media.greaterThan('xlarge')]: {
|
||||
[media.greaterThan('medium')]: {
|
||||
display: 'flex',
|
||||
alignItems: 'stretch',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
|
||||
[media.lessThan('large')]: {
|
||||
[media.lessThan('small')]: {
|
||||
display: 'block',
|
||||
},
|
||||
}}>
|
||||
{children && (
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 33%',
|
||||
|
||||
[media.lessThan('xlarge')]: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
|
||||
'& h3': {
|
||||
color: colors.dark,
|
||||
maxWidth: '11em',
|
||||
paddingTop: 0,
|
||||
},
|
||||
|
||||
'& p': {
|
||||
marginTop: 15,
|
||||
marginRight: 40,
|
||||
lineHeight: 1.7,
|
||||
|
||||
[media.greaterThan('xlarge')]: {
|
||||
marginTop: 25,
|
||||
},
|
||||
},
|
||||
}}>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
css={{
|
||||
[media.greaterThan('medium')]: {
|
||||
flex: '0 0 67%',
|
||||
display: 'flex',
|
||||
alignItems: 'stretch',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
flex: '0 0 70%',
|
||||
overflow: 'hidden',
|
||||
borderRadius: '10px 0 0 10px',
|
||||
|
||||
[media.lessThan('small')]: {
|
||||
display: 'block',
|
||||
[media.lessThan('medium')]: {
|
||||
borderRadius: '10px 10px 0 0',
|
||||
},
|
||||
}}>
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 70%',
|
||||
padding: '0px 10px',
|
||||
background: colors.darker,
|
||||
color: colors.white,
|
||||
}}>
|
||||
<MetaTitle onDark={true}>
|
||||
Live JSX Editor
|
||||
<label
|
||||
css={{
|
||||
fontSize: 14,
|
||||
float: 'right',
|
||||
cursor: 'pointer',
|
||||
}}>
|
||||
<input
|
||||
checked={this.state.showJSX}
|
||||
onChange={event =>
|
||||
this.setState({showJSX: event.target.checked})
|
||||
}
|
||||
type="checkbox"
|
||||
/>{' '}
|
||||
JSX?
|
||||
</label>
|
||||
</MetaTitle>
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
borderRadius: '0',
|
||||
maxHeight: '340px !important',
|
||||
marginTop: '0 !important',
|
||||
marginLeft: '0 !important',
|
||||
paddingLeft: '0 !important',
|
||||
marginRight: '0 !important',
|
||||
paddingRight: '0 !important',
|
||||
marginBottom: '0 !important',
|
||||
paddingBottom: '20px !important',
|
||||
[media.lessThan('medium')]: {
|
||||
marginBottom: '0 !important',
|
||||
},
|
||||
|
||||
'& pre.prism-code[contenteditable]': {
|
||||
outline: 0,
|
||||
overflow: 'auto',
|
||||
marginRight: '0 !important',
|
||||
marginBottom: '0 !important',
|
||||
},
|
||||
}}
|
||||
className="gatsby-highlight">
|
||||
<LiveEditor ignoreTabKey={true} onChange={this._onChange} />
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 30%',
|
||||
overflow: 'hidden',
|
||||
borderRadius: '10px 0 0 10px',
|
||||
border: `1px solid ${colors.error}`,
|
||||
borderRadius: '0 10px 10px 0',
|
||||
fontSize: 12,
|
||||
lineHeight: 1.5,
|
||||
|
||||
[media.lessThan('medium')]: {
|
||||
borderRadius: '10px 10px 0 0',
|
||||
borderRadius: '0 0 10px 10px',
|
||||
},
|
||||
}}>
|
||||
<div
|
||||
css={{
|
||||
padding: '0px 10px',
|
||||
background: colors.darker,
|
||||
background: colors.error,
|
||||
color: colors.white,
|
||||
}}>
|
||||
<MetaTitle onDark={true}>
|
||||
Live JSX Editor
|
||||
<label
|
||||
css={{
|
||||
fontSize: 14,
|
||||
float: 'right',
|
||||
cursor: 'pointer',
|
||||
}}>
|
||||
<input
|
||||
checked={this.state.showJSX}
|
||||
onChange={event =>
|
||||
this.setState({showJSX: event.target.checked})
|
||||
}
|
||||
type="checkbox"
|
||||
/>{' '}
|
||||
JSX?
|
||||
</label>
|
||||
</MetaTitle>
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
borderRadius: '0',
|
||||
maxHeight: '340px !important',
|
||||
marginTop: '0 !important',
|
||||
marginLeft: '0 !important',
|
||||
paddingLeft: '0 !important',
|
||||
marginRight: '0 !important',
|
||||
paddingRight: '0 !important',
|
||||
marginBottom: '0 !important',
|
||||
paddingBottom: '20px !important',
|
||||
[media.lessThan('medium')]: {
|
||||
marginBottom: '0 !important',
|
||||
},
|
||||
|
||||
'& pre.prism-code[contenteditable]': {
|
||||
outline: 0,
|
||||
overflow: 'auto',
|
||||
marginRight: '0 !important',
|
||||
marginBottom: '0 !important',
|
||||
},
|
||||
}}
|
||||
className="gatsby-highlight">
|
||||
<LiveEditor ignoreTabKey={true} onChange={this._onChange} />
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 30%',
|
||||
overflow: 'hidden',
|
||||
border: `1px solid ${colors.error}`,
|
||||
borderRadius: '0 10px 10px 0',
|
||||
fontSize: 12,
|
||||
lineHeight: 1.5,
|
||||
|
||||
[media.lessThan('medium')]: {
|
||||
borderRadius: '0 0 10px 10px',
|
||||
},
|
||||
}}>
|
||||
<div
|
||||
css={{
|
||||
padding: '0px 10px',
|
||||
background: colors.error,
|
||||
<MetaTitle
|
||||
cssProps={{
|
||||
color: colors.white,
|
||||
}}>
|
||||
<MetaTitle
|
||||
cssProps={{
|
||||
color: colors.white,
|
||||
}}>
|
||||
Error
|
||||
</MetaTitle>
|
||||
</div>
|
||||
<pre
|
||||
css={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
color: colors.error,
|
||||
padding: 10,
|
||||
}}>
|
||||
{errorMessage}
|
||||
</pre>
|
||||
Error
|
||||
</MetaTitle>
|
||||
</div>
|
||||
)}
|
||||
{!error && (
|
||||
<pre
|
||||
css={{
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-word',
|
||||
color: colors.error,
|
||||
padding: 10,
|
||||
}}>
|
||||
{errorMessage}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
{!error && (
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 30%',
|
||||
overflow: 'hidden',
|
||||
border: `1px solid ${colors.divider}`,
|
||||
borderRadius: '0 10px 10px 0',
|
||||
|
||||
[media.lessThan('medium')]: {
|
||||
borderRadius: '0 0 10px 10px',
|
||||
},
|
||||
}}>
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 30%',
|
||||
overflow: 'hidden',
|
||||
border: `1px solid ${colors.divider}`,
|
||||
borderRadius: '0 10px 10px 0',
|
||||
|
||||
[media.lessThan('medium')]: {
|
||||
borderRadius: '0 0 10px 10px',
|
||||
},
|
||||
padding: '0 10px',
|
||||
backgroundColor: colors.divider,
|
||||
}}>
|
||||
<div
|
||||
css={{
|
||||
padding: '0 10px',
|
||||
backgroundColor: colors.divider,
|
||||
}}>
|
||||
<MetaTitle>Result</MetaTitle>
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
padding: 10,
|
||||
maxHeight: '340px !important',
|
||||
overflow: 'auto',
|
||||
|
||||
'& input': {
|
||||
width: '100%',
|
||||
display: 'block',
|
||||
border: '1px solid #ccc', // TODO
|
||||
padding: 5,
|
||||
},
|
||||
|
||||
'& button': {
|
||||
marginTop: 10,
|
||||
padding: '5px 10px',
|
||||
},
|
||||
|
||||
'& label': {
|
||||
display: 'block',
|
||||
marginTop: 10,
|
||||
},
|
||||
|
||||
'& textarea': {
|
||||
width: '100%',
|
||||
height: 60,
|
||||
padding: 5,
|
||||
},
|
||||
}}
|
||||
ref={this._setMountRef}
|
||||
/>
|
||||
<MetaTitle>Result</MetaTitle>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
padding: 10,
|
||||
maxHeight: '340px !important',
|
||||
overflow: 'auto',
|
||||
|
||||
'& input': {
|
||||
width: '100%',
|
||||
display: 'block',
|
||||
border: '1px solid #ccc', // TODO
|
||||
padding: 5,
|
||||
},
|
||||
|
||||
'& button': {
|
||||
marginTop: 10,
|
||||
padding: '5px 10px',
|
||||
},
|
||||
|
||||
'& label': {
|
||||
display: 'block',
|
||||
marginTop: 10,
|
||||
},
|
||||
|
||||
'& textarea': {
|
||||
width: '100%',
|
||||
height: 60,
|
||||
padding: 5,
|
||||
},
|
||||
}}
|
||||
ref={this._setMountRef}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</LiveProvider>
|
||||
);
|
||||
|
||||
73
src/components/CodeExample/CodeExample.js
Normal file
73
src/components/CodeExample/CodeExample.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import React, {Component} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {colors, media} from 'theme';
|
||||
import CodeEditor from '../CodeEditor/CodeEditor';
|
||||
|
||||
class CodeExample extends Component {
|
||||
render() {
|
||||
const {children, code, id, loaded} = this.props;
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
css={{
|
||||
marginTop: 40,
|
||||
|
||||
'&:first-child': {
|
||||
marginTop: 0,
|
||||
},
|
||||
|
||||
'& .react-live': {
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
[media.greaterThan('xlarge')]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
marginTop: 80,
|
||||
},
|
||||
|
||||
[media.lessThan('large')]: {
|
||||
display: 'block',
|
||||
},
|
||||
}}>
|
||||
{children && (
|
||||
<div
|
||||
css={{
|
||||
flex: '0 0 33%',
|
||||
[media.lessThan('xlarge')]: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
|
||||
'& h3': {
|
||||
color: colors.dark,
|
||||
maxWidth: '11em',
|
||||
paddingTop: 0,
|
||||
},
|
||||
|
||||
'& p': {
|
||||
marginTop: 15,
|
||||
marginRight: 40,
|
||||
lineHeight: 1.7,
|
||||
|
||||
[media.greaterThan('xlarge')]: {
|
||||
marginTop: 25,
|
||||
},
|
||||
},
|
||||
}}>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
{loaded ? <CodeEditor code={code} /> : <h4>Loading code example...</h4>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CodeExample.propTypes = {
|
||||
children: PropTypes.node,
|
||||
code: PropTypes.string.isRequired,
|
||||
loaded: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default CodeExample;
|
||||
3
src/components/CodeExample/index.js
Normal file
3
src/components/CodeExample/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import CodeExample from './CodeExample';
|
||||
|
||||
export default CodeExample;
|
||||
@@ -7,72 +7,46 @@
|
||||
import ButtonLink from 'components/ButtonLink';
|
||||
import Container from 'components/Container';
|
||||
import Flex from 'components/Flex';
|
||||
import mountCodeExample from 'utils/mountCodeExample';
|
||||
import CodeExample from 'components/CodeExample';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, {Component} from 'react';
|
||||
import {graphql} from 'gatsby';
|
||||
import TitleAndMetaTags from 'components/TitleAndMetaTags';
|
||||
import Layout from 'components/Layout';
|
||||
import {colors, media, sharedStyles} from 'theme';
|
||||
import createOgUrl from 'utils/createOgUrl';
|
||||
import loadScript from 'utils/loadScript';
|
||||
import createOgUrl from 'utils/createOgUrl';
|
||||
import {babelURL} from 'site-constants';
|
||||
import ReactDOM from 'react-dom';
|
||||
import logoWhiteSvg from 'icons/logo-white.svg';
|
||||
|
||||
class Home extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
const {data} = props;
|
||||
|
||||
const code = data.code.edges.reduce((map, {node}) => {
|
||||
map[node.id] = JSON.parse(node.internal.contentDigest);
|
||||
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
const examples = data.examples.edges.map(({node}) => ({
|
||||
content: node.html,
|
||||
id: node.fields.slug.replace(/^.+\//, '').replace('.html', ''),
|
||||
title: node.frontmatter.title,
|
||||
}));
|
||||
|
||||
const marketing = data.marketing.edges.map(({node}) => ({
|
||||
title: node.frontmatter.title,
|
||||
content: node.html,
|
||||
}));
|
||||
|
||||
this.state = {
|
||||
code,
|
||||
examples,
|
||||
marketing,
|
||||
};
|
||||
}
|
||||
state = {
|
||||
babelLoaded: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const {code, examples} = this.state;
|
||||
|
||||
examples.forEach(({id}) => {
|
||||
renderExamplePlaceholder(id);
|
||||
});
|
||||
|
||||
function mountCodeExamples() {
|
||||
examples.forEach(({id}) => {
|
||||
mountCodeExample(id, code[id]);
|
||||
});
|
||||
}
|
||||
|
||||
loadScript(babelURL).then(mountCodeExamples, error => {
|
||||
console.error('Babel failed to load.');
|
||||
|
||||
mountCodeExamples();
|
||||
});
|
||||
loadScript(babelURL).then(
|
||||
() => {
|
||||
this.setState({
|
||||
babelLoaded: true,
|
||||
});
|
||||
},
|
||||
error => {
|
||||
console.error('Babel failed to load.');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {examples, marketing} = this.state;
|
||||
const {location} = this.props;
|
||||
const {babelLoaded} = this.state;
|
||||
const {data, location} = this.props;
|
||||
const {codeExamples, examples, marketing} = data;
|
||||
|
||||
const code = codeExamples.edges.reduce((lookup, {node}) => {
|
||||
lookup[node.mdAbsolutePath] = node;
|
||||
return lookup;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<Layout location={location}>
|
||||
@@ -217,7 +191,7 @@ class Home extends Component {
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
}}>
|
||||
{marketing.map((column, index) => (
|
||||
{marketing.edges.map(({node: column}, index) => (
|
||||
<div
|
||||
key={index}
|
||||
css={{
|
||||
@@ -266,9 +240,9 @@ class Home extends Component {
|
||||
},
|
||||
},
|
||||
]}>
|
||||
{column.title}
|
||||
{column.frontmatter.title}
|
||||
</h3>
|
||||
<div dangerouslySetInnerHTML={{__html: column.content}} />
|
||||
<div dangerouslySetInnerHTML={{__html: column.html}} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -283,27 +257,19 @@ class Home extends Component {
|
||||
/>
|
||||
<section css={sectionStyles}>
|
||||
<div id="examples">
|
||||
{examples.map((example, index) => (
|
||||
<div
|
||||
key={index}
|
||||
css={{
|
||||
marginTop: 40,
|
||||
|
||||
'&:first-child': {
|
||||
marginTop: 0,
|
||||
},
|
||||
|
||||
[media.greaterThan('xlarge')]: {
|
||||
marginTop: 80,
|
||||
},
|
||||
}}>
|
||||
<h3 css={headingStyles}>{example.title}</h3>
|
||||
<div
|
||||
dangerouslySetInnerHTML={{__html: example.content}}
|
||||
/>
|
||||
<div id={example.id} />
|
||||
</div>
|
||||
))}
|
||||
{examples.edges.map(({node}, index) => {
|
||||
const snippet = code[node.fileAbsolutePath];
|
||||
return (
|
||||
<CodeExample
|
||||
key={index}
|
||||
id={snippet.id}
|
||||
code={snippet.code}
|
||||
loaded={babelLoaded}>
|
||||
<h3 css={headingStyles}>{node.frontmatter.title}</h3>
|
||||
<div dangerouslySetInnerHTML={{__html: node.html}} />
|
||||
</CodeExample>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -339,7 +305,6 @@ class Home extends Component {
|
||||
|
||||
Home.propTypes = {
|
||||
data: PropTypes.shape({
|
||||
code: PropTypes.object.isRequired,
|
||||
examples: PropTypes.object.isRequired,
|
||||
marketing: PropTypes.object.isRequired,
|
||||
}).isRequired,
|
||||
@@ -382,22 +347,23 @@ const CtaItem = ({children, primary = false}) => (
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query IndexMarkdown {
|
||||
code: allExampleCode {
|
||||
codeExamples: allExampleCode {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
internal {
|
||||
contentDigest
|
||||
}
|
||||
code
|
||||
mdAbsolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
examples: allMarkdownRemark(
|
||||
filter: {fileAbsolutePath: {regex: "//home/examples//"}}
|
||||
sort: {fields: [frontmatter___order], order: ASC}
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
fileAbsolutePath
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
import CodeEditor from '../components/CodeEditor';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
// TODO This is a huge hack.
|
||||
// Remark transform this template to split code examples and their targets apart.
|
||||
const mountCodeExample = (containerId, code) => {
|
||||
const container = document.getElementById(containerId);
|
||||
const parent = container.parentElement;
|
||||
|
||||
const children = Array.prototype.filter.call(
|
||||
parent.children,
|
||||
child => child !== container,
|
||||
);
|
||||
children.forEach(child => parent.removeChild(child));
|
||||
|
||||
const description = children
|
||||
.map(child => child.outerHTML)
|
||||
.join('')
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
|
||||
ReactDOM.render(
|
||||
<CodeEditor code={code}>
|
||||
{<div dangerouslySetInnerHTML={{__html: description}} />}
|
||||
</CodeEditor>,
|
||||
container,
|
||||
);
|
||||
};
|
||||
|
||||
export default mountCodeExample;
|
||||
Reference in New Issue
Block a user