Files
freeCodeCamp/common/app/routes/challenges/components/classic/Side-Panel.jsx
Berkeley Martinez f1d936198e feat(challenges): add backend challenge infrastructure (#11058)
* Feat: Initial backend view

* Feat: Refactor frame runner

* Feat: backend challenge submit runs tests

* Feat: Backend challenge request

* Feat: Whitelist hyperdev in csp

* Fix: Use app tests instead of challenge tests

* Feat: Allow hyperdev subdomains

* Fix(csp): allow hypderdev.space subdomains

* feat(challenge): submit backend

* feat: Add timeout to test runner (5 sec)

* chore(seed): Add more to test backend

* fix(csp): s/hyperdev/gomix/g

* fix(app): fix code mirror skeleton filepath

* fix(app): remove Gitter saga import

* fix(app): codemirrorskeleton does not need it's own folder
fix(app): cmk needs to work with Null types

* fix: No longer restart the browser when challenges change

* fix(app): Update jquery for challenges

* fix(seed): Remove to promise jquery call

* fix(lint): Undo merge error
undefined is no allowed

* fix(app): linting errors due to bad merge

* fix(seed): Remove old seed file
2017-01-26 23:07:22 -06:00

181 lines
4.2 KiB
JavaScript

import React, { PropTypes } from 'react';
import ReactDom from 'react-dom';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
import PureComponent from 'react-pure-render/component';
import { Col, Row } from 'react-bootstrap';
import TestSuite from '../Test-Suite.jsx';
import Output from '../Output.jsx';
import ToolPanel from './Tool-Panel.jsx';
import { challengeSelector } from '../../redux/selectors';
import {
openBugModal,
updateHint,
executeChallenge,
unlockUntrustedCode
} from '../../redux/actions';
import { descriptionRegex } from '../../utils';
import { makeToast } from '../../../../toasts/redux/actions';
const mapDispatchToProps = {
makeToast,
executeChallenge,
updateHint,
openBugModal,
unlockUntrustedCode
};
const mapStateToProps = createSelector(
challengeSelector,
state => state.app.windowHeight,
state => state.app.navHeight,
state => state.challengesApp.tests,
state => state.challengesApp.output,
state => state.challengesApp.hintIndex,
state => state.challengesApp.isCodeLocked,
state => state.challengesApp.helpChatRoom,
(
{
challenge: {
description,
hints = []
} = {},
title
},
windowHeight,
navHeight,
tests,
output,
hintIndex,
isCodeLocked,
helpChatRoom
) => ({
title,
description,
height: windowHeight - navHeight - 20,
tests,
output,
hint: hints[hintIndex],
isCodeLocked,
helpChatRoom
})
);
export class SidePanel extends PureComponent {
static displayName = 'SidePanel';
static propTypes = {
description: PropTypes.arrayOf(PropTypes.string),
height: PropTypes.number,
helpChatRoom: PropTypes.string,
hint: PropTypes.string,
isCodeLocked: PropTypes.bool,
output: PropTypes.string,
tests: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string,
executeChallenge: PropTypes.func,
openBugModal: PropTypes.func,
makeToast: PropTypes.func,
unlockUntrustedCode: PropTypes.func,
updateHint: PropTypes.func
};
renderDescription(description = [ 'Happy Coding!' ]) {
return description.map((line, index) => {
if (descriptionRegex.test(line)) {
return (
<div
dangerouslySetInnerHTML={{ __html: line }}
key={ line.slice(-6) + index }
/>
);
}
return (
<p
className='wrappable'
dangerouslySetInnerHTML= {{ __html: line }}
key={ line.slice(-6) + index }
/>
);
});
}
componentWillReceiveProps(nextProps) {
if (this.props.title !== nextProps.title) {
ReactDom.findDOMNode(this).scrollTop = 0;
}
}
render() {
const {
title,
description,
height,
tests = [],
output,
hint,
executeChallenge,
updateHint,
makeToast,
helpChatRoom,
openBugModal,
isCodeLocked,
unlockUntrustedCode
} = this.props;
const style = {};
if (height) {
style.height = height + 'px';
}
return (
<div
className='challenges-instructions-panel'
ref='panel'
style={ style }
>
<div>
<h4 className='text-center challenge-instructions-title'>
{ title || 'Happy Coding!' }
</h4>
<hr />
<Row>
<Col
className='challenge-instructions'
xs={ 12 }
>
{ this.renderDescription(description) }
</Col>
</Row>
</div>
<ToolPanel
executeChallenge={ executeChallenge }
helpChatRoom={ helpChatRoom }
hint={ hint }
isCodeLocked={ isCodeLocked }
makeToast={ makeToast }
openBugModal={ openBugModal }
unlockUntrustedCode={ unlockUntrustedCode }
updateHint={ updateHint }
/>
<Output
defaultOutput={
`/**
* Your output will go here.
* Any console.log() statements
* will appear in here as well.
*/`
}
output={ output }
/>
<br />
<TestSuite tests={ tests } />
</div>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(SidePanel);