refactor console to its own reducer and actions, add clear button, clean up console component

This commit is contained in:
Cassie Tarakajian 2017-01-11 14:13:49 -05:00
parent d1c1279061
commit 8120fdd7db
6 changed files with 59 additions and 78 deletions

View file

@ -6,9 +6,9 @@ export function clearConsole() {
}; };
} }
export function dispatchConsoleEvent(...args) { export function dispatchConsoleEvent(messages) {
return { return {
type: ActionTypes.CONSOLE_EVENT, type: ActionTypes.CONSOLE_EVENT,
event: args[0].data event: messages
}; };
} }

View file

@ -4,75 +4,12 @@ const upArrowUrl = require('../../../images/up-arrow.svg');
const downArrowUrl = require('../../../images/down-arrow.svg'); const downArrowUrl = require('../../../images/down-arrow.svg');
import classNames from 'classnames'; import classNames from 'classnames';
/**
* How many console messages to store
* @type {Number}
*/
const consoleMax = 200;
class Console extends React.Component { class Console extends React.Component {
constructor(props) {
super(props);
this.appendConsoleEvent = this.appendConsoleEvent.bind(this);
this.clearConsole = this.clearConsole.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.isPlaying && !this.props.isPlaying) {
this.children = [];
} else if (nextProps.consoleEvent !== this.props.consoleEvent && this.props.isPlaying) {
nextProps.consoleEvent.forEach(consoleEvent => {
const args = consoleEvent.arguments;
Object.keys(args).forEach((key) => {
if (args[key].includes('Exiting potential infinite loop')) {
this.props.stopSketch();
this.props.expandConsole();
this.appendConsoleEvent(consoleEvent);
}
});
this.appendConsoleEvent(consoleEvent);
});
}
}
// shouldComponentUpdate(nextProps) {
// return (nextProps.consoleEvent !== this.props.consoleEvent)
// || (nextProps.isPlaying && !this.props.isPlaying)
// || (this.props.isExpanded !== nextProps.isExpanded)
// || ;
// }
componentDidUpdate() { componentDidUpdate() {
this.refs.console_messages.scrollTop = this.refs.console_messages.scrollHeight; this.refs.console_messages.scrollTop = this.refs.console_messages.scrollHeight;
} }
appendConsoleEvent(consoleEvent) {
const args = consoleEvent.arguments;
const method = consoleEvent.method;
let nextChild;
if (Object.keys(args).length === 0) {
nextChild = (
<div key={this.children.length} className="preview-console__undefined">
<span key={`${this.children.length}-0`}>{'undefined'}</span>
</div>
);
} else {
nextChild = (
<div key={this.children.length} className={`preview-console__${method}`}>
{Object.keys(args).map((key) => <span key={`${this.children.length}-${key}`}>{args[key]}</span>)}
</div>
);
}
this.children.push(nextChild);
}
clearConsole() {
this.children = [];
}
render() { render() {
const childrenToDisplay = this.children.slice(-consoleMax);
const consoleClass = classNames({ const consoleClass = classNames({
'preview-console': true, 'preview-console': true,
'preview-console--collapsed': !this.props.isExpanded 'preview-console--collapsed': !this.props.isExpanded
@ -81,11 +18,11 @@ class Console extends React.Component {
return ( return (
<div ref="console" className={consoleClass} role="main" tabIndex="0" title="console"> <div ref="console" className={consoleClass} role="main" tabIndex="0" title="console">
<div className="preview-console__header"> <div className="preview-console__header">
<h2 className="preview-console__header-title">console</h2> <h2 className="preview-console__header-title">Console</h2>
<div className="preview-console__header-buttons"> <div className="preview-console__header-buttons">
<button className="preview-console__clear" onClick={this.clearConsole} aria-label="clear console"> <a className="preview-console__clear" onClick={this.props.clearConsole} aria-label="clear console">
Clear Clear
</button> </a>
<button className="preview-console__collapse" onClick={this.props.collapseConsole} aria-label="collapse console"> <button className="preview-console__collapse" onClick={this.props.collapseConsole} aria-label="collapse console">
<InlineSVG src={downArrowUrl} /> <InlineSVG src={downArrowUrl} />
</button> </button>
@ -95,7 +32,22 @@ class Console extends React.Component {
</div> </div>
</div> </div>
<div ref="console_messages" className="preview-console__messages"> <div ref="console_messages" className="preview-console__messages">
{childrenToDisplay} {this.props.consoleEvents.map((consoleEvent, index) => {
const args = consoleEvent.arguments;
const method = consoleEvent.method;
if (Object.keys(args).length === 0) {
return (
<div key={index} className="preview-console__undefined">
<span key={`${index}-0`}>{'undefined'}</span>
</div>
);
}
return (
<div key={index} className={`preview-console__${method}`}>
{Object.keys(args).map((key) => <span key={`${index}-${key}`}>{args[key]}</span>)}
</div>
);
})}
</div> </div>
</div> </div>
); );
@ -104,12 +56,12 @@ class Console extends React.Component {
} }
Console.propTypes = { Console.propTypes = {
consoleLines: PropTypes.array, consoleEvents: PropTypes.array,
isPlaying: PropTypes.bool.isRequired, isPlaying: PropTypes.bool.isRequired,
isExpanded: PropTypes.bool.isRequired, isExpanded: PropTypes.bool.isRequired,
collapseConsole: PropTypes.func.isRequired, collapseConsole: PropTypes.func.isRequired,
expandConsole: PropTypes.func.isRequired, expandConsole: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired, clearConsole: PropTypes.func.isRequired
}; };
export default Console; export default Console;

View file

@ -87,8 +87,17 @@ class PreviewFrame extends React.Component {
} }
if (this.props.dispatchConsoleEvent) { if (this.props.dispatchConsoleEvent) {
window.addEventListener('message', (msg) => { window.addEventListener('message', (messageEvent) => {
this.props.dispatchConsoleEvent(msg); messageEvent.data.forEach(message => {
const args = message.arguments;
Object.keys(args).forEach((key) => {
if (args[key].includes('Exiting potential infinite loop')) {
this.props.stopSketch();
this.props.expandConsole();
}
});
});
this.props.dispatchConsoleEvent(messageEvent.data);
}); });
} }
} }
@ -375,7 +384,9 @@ PreviewFrame.propTypes = {
endSketchRefresh: PropTypes.func.isRequired, endSketchRefresh: PropTypes.func.isRequired,
previewIsRefreshing: PropTypes.bool.isRequired, previewIsRefreshing: PropTypes.bool.isRequired,
fullView: PropTypes.bool, fullView: PropTypes.bool,
setBlobUrl: PropTypes.func.isRequired setBlobUrl: PropTypes.func.isRequired,
stopSketch: PropTypes.func.isRequired,
expandConsole: PropTypes.func.isRequired
}; };
export default PreviewFrame; export default PreviewFrame;

View file

@ -324,12 +324,12 @@ class IDEView extends React.Component {
collapseSidebar={this.props.collapseSidebar} collapseSidebar={this.props.collapseSidebar}
/> />
<Console <Console
consoleLines={this.props.console} consoleEvents={this.props.console}
isPlaying={this.props.ide.isPlaying} isPlaying={this.props.ide.isPlaying}
isExpanded={this.props.ide.consoleIsExpanded} isExpanded={this.props.ide.consoleIsExpanded}
expandConsole={this.props.expandConsole} expandConsole={this.props.expandConsole}
collapseConsole={this.props.collapseConsole} collapseConsole={this.props.collapseConsole}
stopSketch={this.props.stopSketch} clearConsole={this.props.clearConsole}
/> />
</SplitPane> </SplitPane>
<div className="preview-frame-holder"> <div className="preview-frame-holder">
@ -359,6 +359,8 @@ class IDEView extends React.Component {
endSketchRefresh={this.props.endSketchRefresh} endSketchRefresh={this.props.endSketchRefresh}
stopSketch={this.props.stopSketch} stopSketch={this.props.stopSketch}
setBlobUrl={this.props.setBlobUrl} setBlobUrl={this.props.setBlobUrl}
stopSketch={this.props.stopSketch}
expandConsole={this.props.expandConsole}
/> />
</div> </div>
</SplitPane> </SplitPane>
@ -587,7 +589,8 @@ IDEView.propTypes = {
resetProject: PropTypes.func.isRequired, resetProject: PropTypes.func.isRequired,
closeForceAuthentication: PropTypes.func.isRequired, closeForceAuthentication: PropTypes.func.isRequired,
openForceAuthentication: PropTypes.func.isRequired, openForceAuthentication: PropTypes.func.isRequired,
console: PropTypes.array.isRequired console: PropTypes.array.isRequired,
clearConsole: PropTypes.func.isRequired
}; };
function mapStateToProps(state) { function mapStateToProps(state) {

View file

@ -6,7 +6,7 @@ const initialState = [];
const console = (state = initialState, action) => { const console = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case ActionTypes.CONSOLE_EVENT: case ActionTypes.CONSOLE_EVENT:
return [...state, action.event].slice(-consoleMax); return state.concat(action.event).slice(-consoleMax);
case ActionTypes.CLEAR_CONSOLE: case ActionTypes.CLEAR_CONSOLE:
return []; return [];
default: default:

View file

@ -52,6 +52,7 @@
padding: #{5 / $base-font-size}rem; padding: #{5 / $base-font-size}rem;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center;
} }
.preview-console__header-title { .preview-console__header-title {
@ -70,6 +71,7 @@
} }
.preview-console__collapse { .preview-console__collapse {
padding-top: #{3 / $base-font-size}rem;
@include themify() { @include themify() {
@extend %icon; @extend %icon;
} }
@ -79,6 +81,7 @@
} }
.preview-console__expand { .preview-console__expand {
padding-top: #{3 / $base-font-size}rem;
@include themify() { @include themify() {
@extend %icon; @extend %icon;
} }
@ -87,3 +90,15 @@
display: inline-block; display: inline-block;
} }
} }
.preview-console__header-buttons {
display: flex;
align-items: center;
}
.preview-console__clear {
padding-right: #{10 / $base-font-size}rem;
.preview-console--collapsed & {
display: none;
}
}