2017-01-26 21:07:22 -08:00
|
|
|
import React, { PropTypes, PureComponent } from 'react';
|
|
|
|
import { createSelector } from 'reselect';
|
|
|
|
import { reduxForm } from 'redux-form';
|
|
|
|
import {
|
|
|
|
Button,
|
|
|
|
Col,
|
|
|
|
Row
|
|
|
|
} from 'react-bootstrap';
|
|
|
|
|
2017-03-13 16:17:07 -07:00
|
|
|
import ChallengeTitle from '../../Challenge-Title.jsx';
|
|
|
|
import SolutionInput from '../../Solution-Input.jsx';
|
|
|
|
import TestSuite from '../../Test-Suite.jsx';
|
|
|
|
import Output from '../../Output.jsx';
|
2017-01-26 21:07:22 -08:00
|
|
|
import { submitChallenge, executeChallenge } from '../../redux/actions.js';
|
|
|
|
import { challengeSelector } from '../../redux/selectors.js';
|
|
|
|
import { descriptionRegex } from '../../utils.js';
|
|
|
|
import {
|
2017-03-13 16:17:07 -07:00
|
|
|
createFormValidator,
|
2017-01-26 21:07:22 -08:00
|
|
|
isValidURL,
|
2017-03-13 16:17:07 -07:00
|
|
|
makeRequired
|
2017-01-26 21:07:22 -08:00
|
|
|
} from '../../../../utils/form.js';
|
|
|
|
|
2017-01-12 06:54:43 +00:00
|
|
|
// provided by redux form
|
|
|
|
const reduxFormPropTypes = {
|
|
|
|
fields: PropTypes.object,
|
|
|
|
handleSubmit: PropTypes.func.isRequired,
|
|
|
|
resetForm: PropTypes.func.isRequired,
|
|
|
|
submitting: PropTypes.bool
|
|
|
|
};
|
|
|
|
|
2017-01-26 21:07:22 -08:00
|
|
|
const propTypes = {
|
|
|
|
description: PropTypes.arrayOf(PropTypes.string),
|
|
|
|
executeChallenge: PropTypes.func.isRequired,
|
2017-01-12 06:54:43 +00:00
|
|
|
id: PropTypes.string,
|
|
|
|
output: PropTypes.string,
|
2017-01-26 21:07:22 -08:00
|
|
|
submitChallenge: PropTypes.func.isRequired,
|
2017-01-12 06:54:43 +00:00
|
|
|
tests: PropTypes.array,
|
|
|
|
title: PropTypes.string,
|
|
|
|
...reduxFormPropTypes
|
2017-01-26 21:07:22 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
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 (
|
2017-03-13 16:17:07 -07:00
|
|
|
<Row>
|
2017-01-26 21:07:22 -08:00
|
|
|
<Col
|
|
|
|
md={ 6 }
|
|
|
|
mdOffset={ 3 }
|
|
|
|
xs={ 12 }
|
|
|
|
>
|
2017-02-05 14:27:11 -05:00
|
|
|
<Row className='challenge-instructions'>
|
2017-03-13 16:17:07 -07:00
|
|
|
<ChallengeTitle>{ title }</ChallengeTitle>
|
2017-01-26 21:07:22 -08:00
|
|
|
{ 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>
|
2017-06-03 14:29:37 +05:30
|
|
|
<br/>
|
2017-01-26 21:07:22 -08:00
|
|
|
<Output
|
|
|
|
defaultOutput={
|
|
|
|
`/**
|
|
|
|
* Test output will go here
|
|
|
|
*/`
|
|
|
|
}
|
|
|
|
output={ output }
|
|
|
|
/>
|
|
|
|
</Row>
|
|
|
|
<Row>
|
|
|
|
<TestSuite tests={ tests } />
|
|
|
|
</Row>
|
|
|
|
</Col>
|
2017-03-13 16:17:07 -07:00
|
|
|
</Row>
|
2017-01-26 21:07:22 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BackEnd.displayName = 'BackEnd';
|
|
|
|
BackEnd.propTypes = propTypes;
|
|
|
|
|
|
|
|
export default reduxForm(
|
|
|
|
{
|
|
|
|
form: 'BackEndChallenge',
|
|
|
|
fields,
|
|
|
|
validate: createFormValidator(fieldValidators)
|
|
|
|
},
|
|
|
|
mapStateToProps,
|
|
|
|
mapDispatchToActions
|
|
|
|
)(BackEnd);
|