Files
freeCodeCamp/packages/learn/src/templates/Challenges/classic/Show.js

257 lines
6.5 KiB
JavaScript
Raw Normal View History

2018-04-06 14:51:52 +01:00
/* global graphql */
import React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex';
import Editor from './Editor';
import Preview from '../components/Preview';
import SidePanel from '../components/Side-Panel';
import TestSuite from '../components/Test-Suite';
import Output from '../components/Output';
2018-04-06 14:51:52 +01:00
import CompletionModal from '../components/CompletionModal';
import HelpModal from '../components/HelpModal';
import ResetModal from '../components/ResetModal';
2018-04-06 14:51:52 +01:00
import { randomCompliment } from '../utils/get-words';
import { challengeTypes } from '../../../../utils/challengeTypes';
import { ChallengeNode } from '../../../redux/propTypes';
2018-04-06 14:51:52 +01:00
import {
createFiles,
challengeFilesSelector,
challengeTestsSelector,
2018-04-06 14:51:52 +01:00
initTests,
updateChallengeMeta,
challengeMounted,
updateSuccessMessage,
consoleOutputSelector
} from '../redux';
2018-04-06 14:51:52 +01:00
import './classic.css';
import ToolPanel from '../components/Tool-Panel';
import Spacer from '../../../components/util/Spacer';
2018-04-06 14:51:52 +01:00
const mapStateToProps = createSelector(
challengeFilesSelector,
challengeTestsSelector,
consoleOutputSelector,
(files, tests, output) => ({
files,
tests,
output
})
);
2018-04-06 14:51:52 +01:00
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
createFiles,
initTests,
updateChallengeMeta,
challengeMounted,
updateSuccessMessage
},
dispatch
);
2018-04-06 14:51:52 +01:00
const propTypes = {
challengeMounted: PropTypes.func.isRequired,
2018-04-06 14:51:52 +01:00
createFiles: PropTypes.func.isRequired,
data: PropTypes.shape({
challengeNode: ChallengeNode
}),
files: PropTypes.shape({
key: PropTypes.string
}),
initTests: PropTypes.func.isRequired,
output: PropTypes.string,
2018-04-06 14:51:52 +01:00
pathContext: PropTypes.shape({
challengeMeta: PropTypes.shape({
nextchallengePath: PropTypes.string
})
}),
tests: PropTypes.arrayOf(
PropTypes.shape({
text: PropTypes.string,
testString: PropTypes.string
})
),
updateChallengeMeta: PropTypes.func.isRequired,
updateSuccessMessage: PropTypes.func.isRequired
2018-04-06 14:51:52 +01:00
};
class ShowClassic extends PureComponent {
componentDidMount() {
const {
challengeMounted,
2018-04-06 14:51:52 +01:00
createFiles,
initTests,
updateChallengeMeta,
updateSuccessMessage,
data: { challengeNode: { files, title, fields: { tests } } },
2018-04-06 14:51:52 +01:00
pathContext: { challengeMeta }
} = this.props;
createFiles(files);
initTests(tests);
updateChallengeMeta({ ...challengeMeta, title });
updateSuccessMessage(randomCompliment());
challengeMounted(challengeMeta.id);
2018-04-06 14:51:52 +01:00
}
2018-04-06 15:45:49 +01:00
componentDidUpdate(prevProps) {
const { data: { challengeNode: { title: prevTitle } } } = prevProps;
const {
challengeMounted,
2018-04-06 15:45:49 +01:00
createFiles,
initTests,
updateChallengeMeta,
updateSuccessMessage,
2018-04-06 15:45:49 +01:00
data: {
challengeNode: { files, title: currentTitle, fields: { tests } }
},
pathContext: { challengeMeta }
} = this.props;
if (prevTitle !== currentTitle) {
updateSuccessMessage(randomCompliment());
2018-04-06 15:45:49 +01:00
createFiles(files);
initTests(tests);
updateChallengeMeta({ ...challengeMeta, title: currentTitle });
challengeMounted(challengeMeta.id);
2018-04-06 15:45:49 +01:00
}
}
2018-04-06 14:51:52 +01:00
render() {
const {
data: {
challengeNode: {
challengeType,
fields: { blockName },
title,
description,
guideUrl
}
},
files,
tests,
output
2018-04-06 14:51:52 +01:00
} = this.props;
const editors = Object.keys(files)
.map(key => files[key])
.map((file, index) => (
<ReflexContainer orientation='horizontal' key={file.key + index}>
2018-04-06 14:51:52 +01:00
{index !== 0 && <ReflexSplitter />}
<ReflexElement flex={1} propagateDimensions={true} renderOnResize={true} renderOnResizeRate={20}>
2018-04-06 14:51:52 +01:00
<Editor {...file} fileKey={file.key} />
</ReflexElement>
{index + 1 === Object.keys(files).length && <ReflexSplitter propagate={true} />}
{index + 1 === Object.keys(files).length ? (
<ReflexElement flex={0.25} propagateDimensions={true} renderOnResize={true} renderOnResizeRate={20}>
<Output
defaultOutput={`
/**
* Your output will go here.
* Any console.log() statements
* will appear in here as well.
*/
`}
output={output}
/>
</ReflexElement>
) : null}
</ReflexContainer>
2018-04-06 14:51:52 +01:00
));
2018-04-11 14:51:47 +01:00
const showPreview =
2018-04-11 14:43:23 +01:00
challengeType === challengeTypes.html ||
2018-04-11 14:51:47 +01:00
challengeType === challengeTypes.modern;
2018-04-06 14:51:52 +01:00
const blockNameTitle = `${blockName} - ${title}`;
return (
<Fragment>
<Helmet title={`${blockNameTitle} | Learn freeCodeCamp`} />
<ToolPanel />
2018-04-06 14:51:52 +01:00
<ReflexContainer orientation='vertical'>
<ReflexElement flex={1}>
<SidePanel
className='full-height'
description={description}
guideUrl={guideUrl}
title={blockNameTitle}
/>
</ReflexElement>
<ReflexSplitter />
<ReflexElement flex={1}>
{editors}
2018-04-06 14:51:52 +01:00
</ReflexElement>
<ReflexSplitter />
<ReflexElement flex={0.5}>
{showPreview ? <Preview className='full-height' /> : null}
<Spacer />
<TestSuite tests={tests} />
</ReflexElement>
2018-04-06 14:51:52 +01:00
</ReflexContainer>
2018-04-06 14:51:52 +01:00
<CompletionModal />
<HelpModal />
<ResetModal />
2018-04-06 14:51:52 +01:00
</Fragment>
);
}
}
ShowClassic.displayName = 'ShowClassic';
ShowClassic.propTypes = propTypes;
export default connect(mapStateToProps, mapDispatchToProps)(ShowClassic);
export const query = graphql`
query ClassicChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
title
guideUrl
description
challengeType
fields {
blockName
tests {
text
testString
}
}
2018-04-11 14:43:23 +01:00
required {
link
raw
src
}
2018-04-06 14:51:52 +01:00
files {
indexhtml {
key
ext
name
contents
head
tail
}
indexjs {
key
ext
name
contents
head
tail
}
2018-04-11 14:43:23 +01:00
indexjsx {
key
ext
name
contents
head
tail
}
2018-04-06 14:51:52 +01:00
}
}
}
`;