diff --git a/client/gatsby-node.js b/client/gatsby-node.js index 173c7e2a73..ced7ee5195 100644 --- a/client/gatsby-node.js +++ b/client/gatsby-node.js @@ -87,9 +87,21 @@ exports.createPages = function createPages({ graphql, actions, reporter }) { src } challengeOrder + challengeFiles { + name + ext + contents + head + tail + } + solutions { + contents + ext + } superBlock superOrder template + usesMultifileEditor } } } diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 4c8a9f0206..b2baf28f22 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -62,7 +62,8 @@ "verify-email": "Verify Email", "submit-and-go": "Submit and go to next challenge", "go-to-next": "Go to next challenge", - "ask-later": "Ask me later" + "ask-later": "Ask me later", + "start-coding": "Start coding!" }, "landing": { "big-heading-1": "Learn to code — for free.", @@ -288,7 +289,8 @@ "preview": "Preview" }, "help-translate": "We are still translating the following certifications.", - "help-translate-link": "Help us translate." + "help-translate-link": "Help us translate.", + "project-preview-title": "Here's a preview of what you will build" }, "donate": { "title": "Support our nonprofit", diff --git a/client/src/redux/prop-types.ts b/client/src/redux/prop-types.ts index 1ac13fbbac..b48238930b 100644 --- a/client/src/redux/prop-types.ts +++ b/client/src/redux/prop-types.ts @@ -106,7 +106,7 @@ export type MarkdownRemark = { type Question = { text: string; answers: string[]; solution: number }; type Fields = { slug: string; blockName: string; tests: Test[] }; -type Required = { +export type Required = { link: string; raw: boolean; src: string; diff --git a/client/src/templates/Challenges/classic/show.tsx b/client/src/templates/Challenges/classic/show.tsx index dd975d58e3..f1e4fa997e 100644 --- a/client/src/templates/Challenges/classic/show.tsx +++ b/client/src/templates/Challenges/classic/show.tsx @@ -28,6 +28,9 @@ import CompletionModal from '../components/completion-modal'; import HelpModal from '../components/help-modal'; import Output from '../components/output'; import Preview from '../components/preview'; +import ProjectPreviewModal, { + PreviewConfig +} from '../components/project-preview-modal'; import SidePanel from '../components/side-panel'; import VideoModal from '../components/video-modal'; import { @@ -41,7 +44,10 @@ import { initConsole, initTests, isChallengeCompletedSelector, - updateChallengeMeta + previewMounted, + updateChallengeMeta, + openModal, + setEditorFocusability } from '../redux'; import { getGuideUrl } from '../utils'; import MultifileEditor from './MultifileEditor'; @@ -68,7 +74,10 @@ const mapDispatchToProps = (dispatch: Dispatch) => updateChallengeMeta, challengeMounted, executeChallenge, - cancelTests + cancelTests, + previewMounted, + openModal, + setEditorFocusability }, dispatch ); @@ -87,10 +96,13 @@ interface ShowClassicProps { output: string[]; pageContext: { challengeMeta: ChallengeMeta; + projectPreview: PreviewConfig & { showProjectPreview: boolean }; }; t: TFunction; tests: Test[]; updateChallengeMeta: (arg0: ChallengeMeta) => void; + openModal: (modal: string) => void; + setEditorFocusability: (canFocus: boolean) => void; } interface ShowClassicState { @@ -231,6 +243,7 @@ class ShowClassic extends Component { initConsole, initTests, updateChallengeMeta, + openModal, data: { challengeNode: { challengeFiles, @@ -240,11 +253,15 @@ class ShowClassic extends Component { helpCategory } }, - pageContext: { challengeMeta } + pageContext: { + challengeMeta, + projectPreview: { showProjectPreview } + } } = this.props; initConsole(''); createFiles(challengeFiles ?? []); initTests(tests); + if (showProjectPreview) openModal('projectPreview'); updateChallengeMeta({ ...challengeMeta, title, @@ -358,7 +375,11 @@ class ShowClassic extends Component { renderPreview() { return ( - + ); } @@ -383,7 +404,8 @@ class ShowClassic extends Component { const { executeChallenge, pageContext: { - challengeMeta: { nextChallengePath, prevChallengePath } + challengeMeta: { nextChallengePath, prevChallengePath }, + projectPreview }, challengeFiles, t @@ -443,6 +465,7 @@ class ShowClassic extends Component { + ); @@ -456,9 +479,6 @@ export default connect( mapDispatchToProps )(withTranslation()(ShowClassic)); -// TODO: handle jsx (not sure why it doesn't get an editableRegion) EDIT: -// probably because the dummy challenge didn't include it, so Gatsby couldn't -// infer it. export const query = graphql` query ClassicChallenge($slug: String!) { challengeNode(fields: { slug: { eq: $slug } }) { diff --git a/client/src/templates/Challenges/components/preview.css b/client/src/templates/Challenges/components/preview.css index 4601370abb..11a22f1310 100644 --- a/client/src/templates/Challenges/components/preview.css +++ b/client/src/templates/Challenges/components/preview.css @@ -1,6 +1,7 @@ .challenge-preview, .challenge-preview-frame { height: 100%; + min-height: 70vh; width: 100%; padding: 0; margin: 0; diff --git a/client/src/templates/Challenges/components/preview.tsx b/client/src/templates/Challenges/components/preview.tsx index 2ce520fa5b..02f1c4ab8b 100644 --- a/client/src/templates/Challenges/components/preview.tsx +++ b/client/src/templates/Challenges/components/preview.tsx @@ -1,30 +1,23 @@ import React, { useState, useEffect } from 'react'; -import { withTranslation } from 'react-i18next'; -import { connect } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; +import { useTranslation } from 'react-i18next'; -import { previewMounted } from '../redux'; +import { mainPreviewId } from '../utils/frame'; import './preview.css'; -const mainId = 'fcc-main-frame'; - -const mapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - previewMounted - }, - dispatch - ); - interface PreviewProps { className?: string; disableIframe?: boolean; previewMounted: () => void; - t: (text: string) => string; + previewId?: string; } -function Preview({ disableIframe, previewMounted, t }: PreviewProps) { +function Preview({ + disableIframe, + previewMounted, + previewId +}: PreviewProps): JSX.Element { + const { t } = useTranslation(); const [iframeStatus, setIframeStatus] = useState(false); const iframeToggle = iframeStatus ? 'disable' : 'enable'; @@ -36,11 +29,14 @@ function Preview({ disableIframe, previewMounted, t }: PreviewProps) { setIframeStatus(disableIframe); }, [disableIframe]); + // TODO: remove type assertion once frame.js has been migrated. + const id: string = previewId ?? (mainPreviewId as string); + return (