2017-12-15 13:53:32 -08:00
|
|
|
import React, { PureComponent } from 'react';
|
2017-11-09 17:10:30 -08:00
|
|
|
import PropTypes from 'prop-types';
|
2016-07-01 19:32:02 -07:00
|
|
|
import ReactDom from 'react-dom';
|
2016-03-05 21:06:04 -08:00
|
|
|
import { createSelector } from 'reselect';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
|
2017-03-13 16:17:07 -07:00
|
|
|
import ns from './ns.json';
|
|
|
|
|
2017-12-16 14:53:07 +03:00
|
|
|
import HelpModal from './Help-Modal.jsx';
|
2017-07-31 20:04:01 -07:00
|
|
|
import ToolPanel from './Tool-Panel.jsx';
|
2017-11-29 15:44:51 -08:00
|
|
|
import ChallengeTitle from './Challenge-Title.jsx';
|
|
|
|
import ChallengeDescription from './Challenge-Description.jsx';
|
|
|
|
import TestSuite from './Test-Suite.jsx';
|
|
|
|
import Output from './Output.jsx';
|
2016-07-11 21:54:55 -07:00
|
|
|
import {
|
2017-12-16 14:53:07 +03:00
|
|
|
openHelpModal,
|
2016-07-11 21:54:55 -07:00
|
|
|
updateHint,
|
2016-08-16 15:59:23 -07:00
|
|
|
executeChallenge,
|
2017-07-31 20:04:01 -07:00
|
|
|
unlockUntrustedCode,
|
|
|
|
|
|
|
|
challengeMetaSelector,
|
|
|
|
testsSelector,
|
|
|
|
outputSelector,
|
|
|
|
hintIndexSelector,
|
2018-01-29 17:26:03 +00:00
|
|
|
codeLockedSelector,
|
|
|
|
guideURLSelector
|
2017-11-29 15:44:51 -08:00
|
|
|
} from './redux';
|
2017-07-31 20:04:01 -07:00
|
|
|
|
2017-11-29 15:44:51 -08:00
|
|
|
import { descriptionRegex } from './utils';
|
|
|
|
import { challengeSelector } from '../../redux';
|
|
|
|
import { makeToast } from '../../Toasts/redux';
|
2016-03-05 21:06:04 -08:00
|
|
|
|
2016-11-08 17:26:58 -08:00
|
|
|
const mapDispatchToProps = {
|
2016-07-11 17:44:50 -07:00
|
|
|
makeToast,
|
|
|
|
executeChallenge,
|
|
|
|
updateHint,
|
2017-12-16 14:53:07 +03:00
|
|
|
openHelpModal,
|
2016-08-16 15:59:23 -07:00
|
|
|
unlockUntrustedCode
|
2016-07-11 17:44:50 -07:00
|
|
|
};
|
2016-03-05 21:06:04 -08:00
|
|
|
const mapStateToProps = createSelector(
|
2016-05-13 20:04:56 -07:00
|
|
|
challengeSelector,
|
2017-07-31 20:04:01 -07:00
|
|
|
challengeMetaSelector,
|
|
|
|
testsSelector,
|
|
|
|
outputSelector,
|
|
|
|
hintIndexSelector,
|
|
|
|
codeLockedSelector,
|
2018-01-29 17:26:03 +00:00
|
|
|
guideURLSelector,
|
2016-05-13 20:04:56 -07:00
|
|
|
(
|
2018-01-29 17:26:03 +00:00
|
|
|
{ description },
|
2017-07-31 20:04:01 -07:00
|
|
|
{ title },
|
2016-05-13 20:04:56 -07:00
|
|
|
tests,
|
2016-06-14 18:47:43 -07:00
|
|
|
output,
|
2016-08-16 15:59:23 -07:00
|
|
|
hintIndex,
|
2016-11-08 17:26:58 -08:00
|
|
|
isCodeLocked,
|
2018-01-29 17:26:03 +00:00
|
|
|
guideUrl
|
2016-05-13 20:04:56 -07:00
|
|
|
) => ({
|
|
|
|
title,
|
2018-01-07 18:01:01 -06:00
|
|
|
guideUrl,
|
2016-05-13 20:04:56 -07:00
|
|
|
description,
|
|
|
|
tests,
|
2016-06-14 18:47:43 -07:00
|
|
|
output,
|
2017-12-16 14:53:07 +03:00
|
|
|
isCodeLocked
|
2016-05-13 20:04:56 -07:00
|
|
|
})
|
2016-03-05 21:06:04 -08:00
|
|
|
);
|
2017-01-12 06:54:43 +00:00
|
|
|
const propTypes = {
|
|
|
|
description: PropTypes.arrayOf(PropTypes.string),
|
|
|
|
executeChallenge: PropTypes.func,
|
2018-01-07 18:01:01 -06:00
|
|
|
guideUrl: PropTypes.string,
|
2017-01-12 06:54:43 +00:00
|
|
|
hint: PropTypes.string,
|
|
|
|
isCodeLocked: PropTypes.bool,
|
|
|
|
makeToast: PropTypes.func,
|
2017-12-16 14:53:07 +03:00
|
|
|
openHelpModal: PropTypes.func,
|
2017-01-12 06:54:43 +00:00
|
|
|
output: PropTypes.string,
|
|
|
|
tests: PropTypes.arrayOf(PropTypes.object),
|
|
|
|
title: PropTypes.string,
|
|
|
|
unlockUntrustedCode: PropTypes.func,
|
|
|
|
updateHint: PropTypes.func
|
|
|
|
};
|
2016-03-05 21:06:04 -08:00
|
|
|
|
|
|
|
export class SidePanel extends PureComponent {
|
2017-12-18 12:43:48 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.bindTopDiv = this.bindTopDiv.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUpdate(nextProps) {
|
|
|
|
const { title } = this.props;
|
|
|
|
if (title !== nextProps.title) {
|
|
|
|
const node = ReactDom.findDOMNode(this.descriptionTop);
|
|
|
|
setTimeout(() => { node.scrollIntoView({ behavior: 'smooth'}); }, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bindTopDiv(node) {
|
|
|
|
this.descriptionTop = node;
|
|
|
|
}
|
2016-03-05 21:06:04 -08:00
|
|
|
|
2017-01-26 21:07:22 -08:00
|
|
|
renderDescription(description = [ 'Happy Coding!' ]) {
|
2016-03-05 21:06:04 -08:00
|
|
|
return description.map((line, index) => {
|
|
|
|
if (descriptionRegex.test(line)) {
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
dangerouslySetInnerHTML={{ __html: line }}
|
2016-06-14 18:47:43 -07:00
|
|
|
key={ line.slice(-6) + index }
|
|
|
|
/>
|
2016-03-05 21:06:04 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<p
|
|
|
|
className='wrappable'
|
|
|
|
dangerouslySetInnerHTML= {{ __html: line }}
|
2016-06-14 18:47:43 -07:00
|
|
|
key={ line.slice(-6) + index }
|
|
|
|
/>
|
2016-03-05 21:06:04 -08:00
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2016-05-27 17:11:25 -07:00
|
|
|
const {
|
|
|
|
title,
|
|
|
|
description,
|
|
|
|
tests = [],
|
2016-06-14 18:47:43 -07:00
|
|
|
output,
|
|
|
|
hint,
|
|
|
|
executeChallenge,
|
|
|
|
updateHint,
|
2016-07-11 17:44:50 -07:00
|
|
|
makeToast,
|
2017-12-16 14:53:07 +03:00
|
|
|
openHelpModal,
|
2016-08-16 15:59:23 -07:00
|
|
|
isCodeLocked,
|
2018-01-07 18:01:01 -06:00
|
|
|
unlockUntrustedCode,
|
|
|
|
guideUrl
|
2016-05-27 17:11:25 -07:00
|
|
|
} = this.props;
|
2016-03-05 21:06:04 -08:00
|
|
|
return (
|
|
|
|
<div
|
2017-03-13 16:17:07 -07:00
|
|
|
className={ `${ns}-instructions-panel` }
|
2016-03-05 21:06:04 -08:00
|
|
|
ref='panel'
|
2017-08-12 22:02:01 -07:00
|
|
|
role='complementary'
|
2016-06-14 18:47:43 -07:00
|
|
|
>
|
2017-12-18 12:43:48 +00:00
|
|
|
<div ref={ this.bindTopDiv } />
|
2016-03-05 21:06:04 -08:00
|
|
|
<div>
|
2017-03-13 16:17:07 -07:00
|
|
|
<ChallengeTitle>
|
|
|
|
{ title }
|
|
|
|
</ChallengeTitle>
|
2017-05-23 21:00:59 -04:00
|
|
|
<ChallengeDescription>
|
|
|
|
{ this.renderDescription(description) }
|
|
|
|
</ChallengeDescription>
|
2016-03-05 21:06:04 -08:00
|
|
|
</div>
|
2016-06-14 18:47:43 -07:00
|
|
|
<ToolPanel
|
|
|
|
executeChallenge={ executeChallenge }
|
2018-01-07 18:01:01 -06:00
|
|
|
guideUrl={ guideUrl }
|
2016-06-14 18:47:43 -07:00
|
|
|
hint={ hint }
|
2016-08-16 15:59:23 -07:00
|
|
|
isCodeLocked={ isCodeLocked }
|
2016-06-14 18:47:43 -07:00
|
|
|
makeToast={ makeToast }
|
2017-12-16 14:53:07 +03:00
|
|
|
openHelpModal={ openHelpModal }
|
2016-08-16 15:59:23 -07:00
|
|
|
unlockUntrustedCode={ unlockUntrustedCode }
|
2016-06-14 18:47:43 -07:00
|
|
|
updateHint={ updateHint }
|
|
|
|
/>
|
2017-12-16 14:53:07 +03:00
|
|
|
<HelpModal />
|
2017-01-26 21:07:22 -08:00
|
|
|
<Output
|
|
|
|
defaultOutput={
|
|
|
|
`/**
|
|
|
|
* Your output will go here.
|
|
|
|
* Any console.log() statements
|
|
|
|
* will appear in here as well.
|
|
|
|
*/`
|
|
|
|
}
|
|
|
|
output={ output }
|
|
|
|
/>
|
2016-03-05 21:06:04 -08:00
|
|
|
<br />
|
2016-05-27 22:21:52 -07:00
|
|
|
<TestSuite tests={ tests } />
|
2016-03-05 21:06:04 -08:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-12 06:54:43 +00:00
|
|
|
SidePanel.displayName = 'SidePanel';
|
|
|
|
SidePanel.propTypes = propTypes;
|
|
|
|
|
2016-06-14 18:47:43 -07:00
|
|
|
export default connect(
|
|
|
|
mapStateToProps,
|
2016-11-08 17:26:58 -08:00
|
|
|
mapDispatchToProps
|
2016-06-14 18:47:43 -07:00
|
|
|
)(SidePanel);
|