Skip to content

Commit

Permalink
👀 JsDemo capture console
Browse files Browse the repository at this point in the history
  • Loading branch information
rrcobb committed May 7, 2021
1 parent c6ee468 commit efada58
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 18 deletions.
98 changes: 84 additions & 14 deletions JsDemo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {useState} from 'react';
import React, {useState, useEffect} from 'react';
import clsx from 'clsx';
import * as styles from './demo.module.css';

/*
Expand All @@ -11,8 +12,8 @@ const codeBlocks = nodes =>
.map(pre => pre.props.children)
.filter(codeEl => codeEl.props.mdxType === 'code');

const makeSrcString = nodes =>
codeBlocks(nodes)
const makeSrcString = blocks =>
blocks
.map(block => {
if (block.props.className === 'language-js') {
return `<script>\n${block.props.children}\n</script>`;
Expand All @@ -26,9 +27,59 @@ const makeSrcString = nodes =>
})
.join('\n\n');

// global count of jsDemos as an id
// so that we can match up the log messages from embedded frames with the correct parents
let demoId = 1;

const JsDemo = ({children, defer = false}) => {
let [run, setRun] = useState(!defer);
const srcString = makeSrcString(React.Children.toArray(children));
const blocks = codeBlocks(React.Children.toArray(children));
const srcString = makeSrcString(blocks);
const hasJs = blocks.some(block => block.props.className === 'language-js');
const hasHtml = blocks.some(block => block.props.className === 'language-html');

// capture console output and show the log messages
const [captured, setCaptured] = useState(['']);
const [id, setId] = useState(null);

useEffect(() => {
let componentId = demoId++;
setId(componentId);
const logChildFrameMessage = event => {
if (event.data.id && event.data.id === componentId) {
if (event.data.message === 'clearConsole') {
setCaptured([]);
} else if (event.data.message === 'frameConsoleLog') {
setCaptured(messages => [...messages, ...event.data.value]);
} else if (event.data.message === 'frameError') {
setCaptured(messages => [...messages, event.data.error]);
}
}
};
window.addEventListener('message', logChildFrameMessage);
return () => window.removeEventListener('message', logChildFrameMessage);
}, []);

/*
Inject two handlers to the embedded iframe
1 - post a message to the parent window on error
2 - post a message to the parent window on a console log
Then, post a message to the parent window clearing the console
That way, if the iframe runs twice, we don't get double logs
*/
const injectCaptureConsole = `<script>
window.parent.postMessage({message: 'clearConsole', id: ${id}}, '*');
const originalLog = console.log;
console.log = function (...args) {
window.parent.postMessage({message: 'frameConsoleLog', id: ${id}, value: args}, '*');
originalLog(...args);
};
window.onerror = function(message, source, lineno, colno, error) {
window.parent.postMessage({message: 'frameError', id: ${id}, error: error}, '*');
return true;
}
</script>`;

return (
<div className={styles.wrapper}>
<div className={styles.tab}>
Expand All @@ -37,16 +88,35 @@ const JsDemo = ({children, defer = false}) => {
</div>
<div className={`${styles.tab} ${styles.result}`}>
<div className={styles.label}>Result</div>
<button
className={'MuiButtonBase-root MuiButton-root MuiButton-containedPrimary'}
onClick={() => setRun(true)}
>
Run
</button>
<iframe
className={styles.output}
srcDoc={run ? srcString : 'Click run to see the result'}
></iframe>
{defer && (
<button
className={'MuiButtonBase-root MuiButton-root MuiButton-containedPrimary'}
onClick={() => setRun(true)}
>
Run
</button>
)}
<div className={styles.output}>
<iframe
className={clsx(styles.frame, !hasHtml && styles.hide)}
srcDoc={
run ? [injectCaptureConsole, srcString].join('\n') : 'Click run to see the result'
}
></iframe>
{hasJs && (
<div className={clsx(styles.frame, styles.console)}>
{captured.map((message, index) =>
message instanceof Error ? (
<div key={index} className={styles.error}>
&raquo; {message.toString()}
</div>
) : (
<div key={index}>&raquo; {message}</div>
)
)}
</div>
)}
</div>
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions MagicDemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const MagicDemo = ({template, initialState = {}}) => {

const inputs = names.map(name => (
<MagicInput
key={name}
type={type(initialState[name])}
color={colorMap[name]}
name={name}
Expand Down
12 changes: 10 additions & 2 deletions T002-HTML-syntax/p000-demos.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ alert('Hi there!');
```js
console.log('5 * 5 is ...');
console.log(5 * 5);
console.log('6 * 5 is ...');
console.log(6 * 5);
throw new Error('something went wrong');
```

</JsDemo>
Expand All @@ -69,8 +72,13 @@ let indexToEat = 0;
button.addEventListener('click', function () {
let items = document.querySelectorAll('li');
let snack = items[indexToEat];
snack.innerText = 'The monster ate it!';
indexToEat = indexToEat + 1;
if (snack) {
console.log('num num num, ' + snack.innerText + ', tasty');
snack.innerText = 'The monster ate it!';
indexToEat = indexToEat + 1;
} else {
console.log('😢 monster is sad, no more snacks');
}
});
```

Expand Down
31 changes: 29 additions & 2 deletions demo.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ div:global(.language-css)::before {
row-gap: 4px;
grid-template:
'label button .' 32px
'output output output' 230px / 25% 50% 25%;
'output output output' auto / 25% 50% 25%;
}

.result button {
Expand All @@ -150,7 +150,34 @@ div:global(.language-css)::before {
grid-area: label;
}

.result iframe {
.result .output {
grid-area: output;
width: 100%;
display: flex;
flex-direction: column;
background-color: #2d3748;
row-gap: 24px;
}

.result .frame {
width: 100%;
flex: 1 1 100%;
max-height: 230px;
border: none;
background-color: rgb(255, 255, 255);
border-radius: 0.3em;
}

.result .console {
padding: 8px;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
}

.result .console .error {
color: #f0897d;
}

.hide {
display: none;
}

0 comments on commit efada58

Please sign in to comment.