Files
freeCodeCamp/common/app/routes/challenges/components/backend/Back-End.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

174 lines
3.8 KiB
JavaScript

import React, { PropTypes, PureComponent } from 'react';
import { createSelector } from 'reselect';
import { reduxForm } from 'redux-form';
import {
Button,
Col,
Row
} from 'react-bootstrap';
import SolutionInput from '../Solution-Input.jsx';
import TestSuite from '../Test-Suite.jsx';
import Output from '../Output.jsx';
import { submitChallenge, executeChallenge } from '../../redux/actions.js';
import { challengeSelector } from '../../redux/selectors.js';
import { descriptionRegex } from '../../utils.js';
import {
isValidURL,
makeRequired,
createFormValidator
} from '../../../../utils/form.js';
const propTypes = {
id: PropTypes.string,
title: PropTypes.string,
description: PropTypes.arrayOf(PropTypes.string),
tests: PropTypes.array,
output: PropTypes.string,
executeChallenge: PropTypes.func.isRequired,
submitChallenge: PropTypes.func.isRequired,
// provided by redux form
submitting: PropTypes.bool,
fields: PropTypes.object,
resetForm: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired
};
const fields = [ 'solution' ];
const fieldValidators = {
solution: makeRequired(isValidURL)
};
const mapStateToProps = createSelector(
challengeSelector,
state => state.challengesApp.output,
state => state.challengesApp.tests,
(
{
challenge: {
id,
title,
description
} = {}
},
output,
tests
) => ({
id,
title,
tests,
description,
output
})
);
const mapDispatchToActions = {
executeChallenge,
submitChallenge
};
export class BackEnd extends PureComponent {
renderDescription(description) {
if (!Array.isArray(description)) {
return null;
}
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 }
/>
);
});
}
render() {
const {
title,
description,
tests,
output,
// provided by redux-form
fields: { solution },
submitting,
handleSubmit,
executeChallenge
} = this.props;
const buttonCopy = submitting ?
'Submit and go to my next challenge' :
"I've completed this challenge";
return (
<div>
<Col
md={ 6 }
mdOffset={ 3 }
xs={ 12 }
>
<Row>
<h3>{ title }</h3>
{ this.renderDescription(description) }
</Row>
<Row>
<form
name='BackEndChallenge'
onSubmit={ handleSubmit(executeChallenge) }
>
<SolutionInput
placeholder='https://your-app.com'
solution={ solution }
/>
<Button
block={ true }
bsStyle='primary'
className='btn-big'
onClick={ submitting ? null : null }
type={ submitting ? null : 'submit' }
>
{ buttonCopy } (ctrl + enter)
</Button>
</form>
</Row>
<Row>
<Output
defaultOutput={
`/**
* Test output will go here
*/`
}
output={ output }
/>
</Row>
<Row>
<TestSuite tests={ tests } />
</Row>
</Col>
</div>
);
}
}
BackEnd.displayName = 'BackEnd';
BackEnd.propTypes = propTypes;
export default reduxForm(
{
form: 'BackEndChallenge',
fields,
validate: createFormValidator(fieldValidators)
},
mapStateToProps,
mapDispatchToActions
)(BackEnd);