mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-20 12:03:11 -04:00
feat(client): show demo on demand in labs (#55569)
Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7b05b89d03
commit
b9893bb4d6
@@ -80,6 +80,7 @@ exports.createPages = async function createPages({
|
||||
certification
|
||||
challengeType
|
||||
dashedName
|
||||
demoType
|
||||
disableLoopProtectTests
|
||||
disableLoopProtectPreview
|
||||
fields {
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
"click-here": "Click here to sign in",
|
||||
"save": "Save",
|
||||
"save-code": "Save your Code",
|
||||
"show-demo": "Show Demo",
|
||||
"no-thanks": "No thanks",
|
||||
"yes-please": "Yes please",
|
||||
"update-email": "Update my Email",
|
||||
|
||||
@@ -213,7 +213,6 @@ const ShowProjectLinks = (props: ShowProjectLinksProps): JSX.Element => {
|
||||
challengeData={challengeData}
|
||||
closeText={t('buttons.close')}
|
||||
previewTitle={projectTitle}
|
||||
showProjectPreview={true}
|
||||
/>
|
||||
<ExamResultsModal projectTitle={projectTitle} examResults={examResults} />
|
||||
|
||||
|
||||
@@ -229,7 +229,6 @@ function TimelineInner({
|
||||
challengeData={challengeData}
|
||||
closeText={t('buttons.close')}
|
||||
previewTitle={projectTitle}
|
||||
showProjectPreview={true}
|
||||
/>
|
||||
<ExamResultsModal
|
||||
projectTitle={projectTitle}
|
||||
|
||||
@@ -461,7 +461,6 @@ function CertificationSettings(props: CertificationSettingsProps) {
|
||||
challengeData={challengeData}
|
||||
previewTitle={projectTitle}
|
||||
closeText={t('buttons.close')}
|
||||
showProjectPreview={true}
|
||||
/>
|
||||
<ExamResultsModal projectTitle={projectTitle} examResults={examResults} />
|
||||
</section>
|
||||
|
||||
@@ -184,6 +184,7 @@ export type ChallengeNode = {
|
||||
challengeOrder: number;
|
||||
challengeType: number;
|
||||
dashedName: string;
|
||||
demoType: 'onClick' | 'onLoad' | null;
|
||||
description: string;
|
||||
challengeFiles: ChallengeFiles;
|
||||
fields: Fields;
|
||||
|
||||
@@ -110,7 +110,6 @@ interface ShowClassicProps extends Pick<PreviewProps, 'previewMounted'> {
|
||||
challengeMeta: ChallengeMeta;
|
||||
projectPreview: {
|
||||
challengeData: CompletedChallenge;
|
||||
showProjectPreview: boolean;
|
||||
};
|
||||
};
|
||||
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
||||
@@ -183,6 +182,7 @@ function ShowClassic({
|
||||
challenge: {
|
||||
challengeFiles: seedChallengeFiles,
|
||||
block,
|
||||
demoType,
|
||||
title,
|
||||
description,
|
||||
instructions,
|
||||
@@ -202,7 +202,7 @@ function ShowClassic({
|
||||
pageContext: {
|
||||
challengeMeta,
|
||||
challengeMeta: { isFirstStep, nextChallengePath, prevChallengePath },
|
||||
projectPreview: { challengeData, showProjectPreview }
|
||||
projectPreview: { challengeData }
|
||||
},
|
||||
createFiles,
|
||||
cancelTests,
|
||||
@@ -359,7 +359,10 @@ function ShowClassic({
|
||||
);
|
||||
|
||||
initTests(tests);
|
||||
if (showProjectPreview) openModal('projectPreview');
|
||||
// Typically, this kind of preview only appears on the first step of a
|
||||
// project and is shown (once) automatically. In contrast, labs are more
|
||||
// freeform, so the preview is shown on demand.
|
||||
if (demoType === 'onLoad') openModal('projectPreview');
|
||||
updateChallengeMeta({
|
||||
...challengeMeta,
|
||||
title,
|
||||
@@ -371,9 +374,11 @@ function ShowClassic({
|
||||
};
|
||||
|
||||
const renderInstructionsPanel = ({
|
||||
toolPanel
|
||||
toolPanel,
|
||||
hasDemo
|
||||
}: {
|
||||
toolPanel: React.ReactNode;
|
||||
hasDemo: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<SidePanel
|
||||
@@ -397,6 +402,7 @@ function ShowClassic({
|
||||
instructionsPanelRef={instructionsPanelRef}
|
||||
toolPanel={toolPanel}
|
||||
superBlock={superBlock}
|
||||
hasDemo={hasDemo}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -420,7 +426,7 @@ function ShowClassic({
|
||||
resizeProps={resizeProps}
|
||||
title={title}
|
||||
usesMultifileEditor={usesMultifileEditor}
|
||||
showProjectPreview={showProjectPreview}
|
||||
showProjectPreview={demoType === 'onLoad'}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -448,7 +454,8 @@ function ShowClassic({
|
||||
hasEditableBoundaries={hasEditableBoundaries}
|
||||
hasPreview={showPreview}
|
||||
instructions={renderInstructionsPanel({
|
||||
toolPanel: null
|
||||
toolPanel: null,
|
||||
hasDemo: demoType === 'onClick'
|
||||
})}
|
||||
notes={notes}
|
||||
onPreviewResize={onPreviewResize}
|
||||
@@ -482,7 +489,8 @@ function ShowClassic({
|
||||
hasEditableBoundaries={hasEditableBoundaries}
|
||||
hasPreview={showPreview}
|
||||
instructions={renderInstructionsPanel({
|
||||
toolPanel: <ToolPanel guideUrl={guideUrl} videoUrl={videoUrl} />
|
||||
toolPanel: <ToolPanel guideUrl={guideUrl} videoUrl={videoUrl} />,
|
||||
hasDemo: demoType === 'onClick'
|
||||
})}
|
||||
isFirstStep={isFirstStep}
|
||||
layoutState={layout}
|
||||
@@ -512,7 +520,6 @@ function ShowClassic({
|
||||
challengeData={challengeData}
|
||||
closeText={t('buttons.start-coding')}
|
||||
previewTitle={t('learn.project-preview-title')}
|
||||
showProjectPreview={showProjectPreview}
|
||||
/>
|
||||
<ShortcutsModal />
|
||||
</LearnLayout>
|
||||
@@ -529,6 +536,7 @@ export const query = graphql`
|
||||
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
||||
challenge {
|
||||
block
|
||||
demoType
|
||||
title
|
||||
description
|
||||
id
|
||||
|
||||
@@ -16,7 +16,6 @@ import './project-preview-modal.css';
|
||||
|
||||
interface ProjectPreviewMountedPayload {
|
||||
challengeData: CompletedChallenge | null;
|
||||
showProjectPreview: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@@ -25,7 +24,6 @@ interface Props {
|
||||
projectPreviewMounted: (payload: ProjectPreviewMountedPayload) => void;
|
||||
challengeData: CompletedChallenge | null;
|
||||
setEditorFocusability: (focusability: boolean) => void;
|
||||
showProjectPreview: boolean;
|
||||
previewTitle: string;
|
||||
closeText: string;
|
||||
}
|
||||
@@ -45,7 +43,6 @@ function ProjectPreviewModal({
|
||||
projectPreviewMounted,
|
||||
challengeData,
|
||||
setEditorFocusability,
|
||||
showProjectPreview,
|
||||
previewTitle,
|
||||
closeText
|
||||
}: Props): JSX.Element {
|
||||
@@ -66,9 +63,7 @@ function ProjectPreviewModal({
|
||||
<Modal.Body>
|
||||
<Preview
|
||||
previewId={projectPreviewId}
|
||||
previewMounted={() =>
|
||||
projectPreviewMounted({ challengeData, showProjectPreview })
|
||||
}
|
||||
previewMounted={() => projectPreviewMounted({ challengeData })}
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import React, { useEffect, ReactElement, ReactNode } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { Test } from '../../../redux/prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@freecodecamp/ui';
|
||||
|
||||
import { Test } from '../../../redux/prop-types';
|
||||
import { SuperBlocks } from '../../../../../shared/config/curriculum';
|
||||
import { initializeMathJax } from '../../../utils/math-jax';
|
||||
import { challengeTestsSelector } from '../redux/selectors';
|
||||
import { openModal } from '../redux/actions';
|
||||
import { Spacer } from '../../../components/helpers';
|
||||
import TestSuite from './test-suite';
|
||||
|
||||
import './side-panel.css';
|
||||
@@ -16,11 +20,22 @@ const mapStateToProps = createSelector(
|
||||
tests
|
||||
})
|
||||
);
|
||||
interface SidePanelProps {
|
||||
|
||||
const mapDispatchToProps: {
|
||||
openModal: (modal: string) => void;
|
||||
} = {
|
||||
openModal
|
||||
};
|
||||
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
type DispatchProps = typeof mapDispatchToProps;
|
||||
|
||||
interface SidePanelProps extends DispatchProps, StateProps {
|
||||
block: string;
|
||||
challengeDescription: ReactElement;
|
||||
challengeTitle: ReactElement;
|
||||
instructionsPanelRef: React.RefObject<HTMLDivElement>;
|
||||
hasDemo: boolean;
|
||||
toolPanel: ReactNode;
|
||||
superBlock: SuperBlocks;
|
||||
tests: Test[];
|
||||
@@ -31,10 +46,13 @@ export function SidePanel({
|
||||
challengeDescription,
|
||||
challengeTitle,
|
||||
instructionsPanelRef,
|
||||
hasDemo,
|
||||
toolPanel,
|
||||
superBlock,
|
||||
tests
|
||||
tests,
|
||||
openModal
|
||||
}: SidePanelProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
const mathJaxChallenge =
|
||||
superBlock === SuperBlocks.RosettaCode ||
|
||||
@@ -50,6 +68,14 @@ export function SidePanel({
|
||||
tabIndex={-1}
|
||||
>
|
||||
{challengeTitle}
|
||||
{hasDemo && (
|
||||
<>
|
||||
<Button size='small' onClick={() => openModal('projectPreview')}>
|
||||
{t('buttons.show-demo')}
|
||||
</Button>
|
||||
<Spacer size='xSmall' />
|
||||
</>
|
||||
)}
|
||||
{challengeDescription}
|
||||
{toolPanel}
|
||||
<TestSuite tests={tests} />
|
||||
@@ -59,4 +85,4 @@ export function SidePanel({
|
||||
|
||||
SidePanel.displayName = 'SidePanel';
|
||||
|
||||
export default connect(mapStateToProps)(SidePanel);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SidePanel);
|
||||
|
||||
@@ -348,9 +348,8 @@ function* updatePython(challengeData) {
|
||||
}
|
||||
|
||||
function* previewProjectSolutionSaga({ payload }) {
|
||||
if (!payload) return;
|
||||
const { showProjectPreview, challengeData } = payload;
|
||||
if (!showProjectPreview) return;
|
||||
if (!payload?.challengeData) return;
|
||||
const { challengeData } = payload;
|
||||
|
||||
try {
|
||||
if (canBuildChallenge(challengeData)) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
const path = require('path');
|
||||
const { sortChallengeFiles } = require('../sort-challengefiles');
|
||||
const {
|
||||
challengeTypes,
|
||||
viewTypes
|
||||
} = require('../../../shared/config/challenge-types');
|
||||
const { viewTypes } = require('../../../shared/config/challenge-types');
|
||||
|
||||
const backend = path.resolve(
|
||||
__dirname,
|
||||
@@ -150,8 +147,7 @@ exports.createChallengePages = function (createPage) {
|
||||
// it during the curriculum build process and attach it to the first challenge?
|
||||
// That would remove the need to analyse allChallengeEdges.
|
||||
function getProjectPreviewConfig(challenge, allChallengeEdges) {
|
||||
const { block, challengeOrder, challengeType, usesMultifileEditor } =
|
||||
challenge;
|
||||
const { block } = challenge;
|
||||
|
||||
const challengesInBlock = allChallengeEdges
|
||||
.filter(({ node: { challenge } }) => challenge.block === block)
|
||||
@@ -169,15 +165,6 @@ function getProjectPreviewConfig(challenge, allChallengeEdges) {
|
||||
}));
|
||||
|
||||
return {
|
||||
showProjectPreview:
|
||||
challengeOrder === 0 &&
|
||||
usesMultifileEditor &&
|
||||
// TODO: handle the special cases better. Create a meta property for
|
||||
// showProjectPreview, maybe? Then we can remove all the following cases
|
||||
challengeType !== challengeTypes.multifileCertProject &&
|
||||
challengeType !== challengeTypes.multifilePythonCertProject &&
|
||||
challengeType !== challengeTypes.python &&
|
||||
challengeType !== challengeTypes.js,
|
||||
challengeData: {
|
||||
challengeType: lastChallenge.challengeType,
|
||||
challengeFiles: projectPreviewChallengeFiles
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5d8a4cfbe6b6180ed9a1c9de
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 614ccc21ea91ef1736b9b578
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5f33071498eb2472b87ddee4
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 6140c7e645d8e905819f1dd4
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 61695197ac34f0407e339882
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 61537485c4f2a624f18d7794
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 61437d575fb98f57fa8f7f36
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 619665c9abd72906f3ad30f9
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5d822fd413a79914d39e98c9
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5dc174fcf86c76b9248c6eb2
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 60eebd07ea685b0e777b5583
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 646c47867800472a4ed5d2ea
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 61fd5a93fd62bb35968adeab
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 612e6afc009b450a437940a1
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 60a3e3396c7b40068ad6996a
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 615f2abbe7d18d49a1e0e1c8
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 635060a5c03c950f46174cb5
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 657386f11fb8265660bfac75
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 64061a98f704a014b44afdb2
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 6650c9a94d6e13d14a043a69
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5d5a813321b9e3db6c106a46
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 63ec14d1c216aa063f0be4af
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 652f948489abbb81e6bf5a01
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 641d9a19bff38d34d5a5edb8
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5ddb965c65d27e1512d44d9a
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 642db8c409d9991d0b3b2f0d
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 6461815bc48998eb15d55349
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 64e4e4c4ec263b62ae7bf54d
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 63c620161fc2b49ac340ffc4
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 63db7f4677d06d7500a13321
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 641cd18eb67c661d8a9e11f3
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 65386e889dd615940cb3e042
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 657a0ea50da0c8d9d6d7950a
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 663d0ab797cb716189ffcc0a
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
demoType: onLoad
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 668f08ea07b99b1f4a91acab
|
||||
title: Build a Recipe Page
|
||||
challengeType: 14
|
||||
dashedName: build-a-recipe-page
|
||||
demoType: onClick
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -107,6 +107,7 @@ const schema = Joi.object()
|
||||
checksum: Joi.number(),
|
||||
// TODO: require this only for normal challenges, not certs
|
||||
dashedName: Joi.string().regex(slugRE),
|
||||
demoType: Joi.string().valid('onClick', 'onLoad'),
|
||||
description: Joi.when('challengeType', {
|
||||
is: [
|
||||
challengeTypes.step,
|
||||
|
||||
@@ -104,6 +104,7 @@ const schema = Joi.object()
|
||||
checksum: Joi.number(),
|
||||
// TODO: require this only for normal challenges, not certs
|
||||
dashedName: Joi.string().regex(slugRE),
|
||||
demoType: Joi.string().valid('onClick', 'onLoad'),
|
||||
description: Joi.when('challengeType', {
|
||||
is: [
|
||||
challengeTypes.step,
|
||||
|
||||
@@ -163,7 +163,8 @@ async function createFirstChallenge(
|
||||
projectPath: newChallengeDir + '/',
|
||||
stepNum: 1,
|
||||
challengeType: 0,
|
||||
challengeSeeds
|
||||
challengeSeeds,
|
||||
isFirstChallenge: true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ type StepOptions = {
|
||||
challengeSeeds: Record<string, ChallengeSeed>;
|
||||
stepNum: number;
|
||||
challengeType: number;
|
||||
isFirstChallenge?: boolean;
|
||||
};
|
||||
|
||||
export interface ChallengeSeed {
|
||||
@@ -40,7 +41,8 @@ function getStepTemplate({
|
||||
challengeId,
|
||||
challengeSeeds,
|
||||
stepNum,
|
||||
challengeType
|
||||
challengeType,
|
||||
isFirstChallenge = false
|
||||
}: StepOptions): string {
|
||||
const seedTexts = Object.values(challengeSeeds)
|
||||
.map(({ contents, ext, editableRegionBoundaries }: ChallengeSeed) => {
|
||||
@@ -67,12 +69,18 @@ function getStepTemplate({
|
||||
const seedHeadSection = getSeedSection(seedHeads, 'before-user-code');
|
||||
const seedTailSection = getSeedSection(seedTails, 'after-user-code');
|
||||
|
||||
const demoString = isFirstChallenge
|
||||
? `
|
||||
# demoType can either be 'onClick' or 'onLoad'. If the project or lab doesn't have a preview, delete the property
|
||||
demoType: onClick`
|
||||
: '';
|
||||
|
||||
return (
|
||||
`---
|
||||
id: ${challengeId.toString()}
|
||||
title: Step ${stepNum}
|
||||
challengeType: ${challengeType}
|
||||
dashedName: step-${stepNum}
|
||||
dashedName: step-${stepNum}${demoString}
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -16,13 +16,15 @@ interface Options {
|
||||
challengeType: number;
|
||||
projectPath?: string;
|
||||
challengeSeeds?: Record<string, ChallengeSeed>;
|
||||
isFirstChallenge?: boolean;
|
||||
}
|
||||
|
||||
const createStepFile = ({
|
||||
stepNum,
|
||||
challengeType,
|
||||
projectPath = getProjectPath(),
|
||||
challengeSeeds = {}
|
||||
challengeSeeds = {},
|
||||
isFirstChallenge = false
|
||||
}: Options): ObjectID => {
|
||||
const challengeId = new ObjectID();
|
||||
|
||||
@@ -30,7 +32,8 @@ const createStepFile = ({
|
||||
challengeId,
|
||||
challengeSeeds,
|
||||
stepNum,
|
||||
challengeType
|
||||
challengeType,
|
||||
isFirstChallenge
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
||||
|
||||
Reference in New Issue
Block a user