mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2025-12-30 03:03:06 -05:00
feat: unify curriculum layout (#53336)
This commit is contained in:
@@ -34,10 +34,7 @@ import {
|
||||
} from '../../../redux/prop-types';
|
||||
import { editorToneOptions } from '../../../utils/tone/editor-config';
|
||||
import { editorNotes } from '../../../utils/tone/editor-notes';
|
||||
import {
|
||||
challengeTypes,
|
||||
isFinalProject
|
||||
} from '../../../../../shared/config/challenge-types';
|
||||
import { challengeTypes } from '../../../../../shared/config/challenge-types';
|
||||
import {
|
||||
executeChallenge,
|
||||
saveEditorContent,
|
||||
@@ -66,8 +63,8 @@ import {
|
||||
setScrollbarArrowStyles
|
||||
} from '../utils/index';
|
||||
import { getScrollbarWidth } from '../../../utils/scrollbar-width';
|
||||
import { isProjectBased } from '../../../utils/curriculum-layout';
|
||||
import LowerJaw from './lower-jaw';
|
||||
|
||||
import './editor.css';
|
||||
|
||||
const MonacoEditor = Loadable(() => import('react-monaco-editor'));
|
||||
@@ -511,7 +508,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
||||
monaco.KeyMod.WinCtrl | monaco.KeyCode.Enter
|
||||
],
|
||||
run: () => {
|
||||
if (props.usesMultifileEditor && !isFinalProject(props.challengeType)) {
|
||||
if (props.usesMultifileEditor && !isProjectBased(props.challengeType)) {
|
||||
if (challengeIsComplete()) {
|
||||
tryToSubmitChallenge();
|
||||
} else {
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
challengeTestsSelector
|
||||
} from '../redux/selectors';
|
||||
import './hotkeys.css';
|
||||
import { isFinalProject } from '../../../../../shared/config/challenge-types';
|
||||
import { isProjectBased } from '../../../utils/curriculum-layout';
|
||||
import type { EditorProps } from '../classic/editor';
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
@@ -121,7 +121,7 @@ function Hotkeys({
|
||||
if (
|
||||
usesMultifileEditor &&
|
||||
typeof challengeType == 'number' &&
|
||||
!isFinalProject(challengeType)
|
||||
!isProjectBased(challengeType)
|
||||
) {
|
||||
if (testsArePassing) {
|
||||
submitChallenge();
|
||||
|
||||
@@ -19,15 +19,7 @@ import { completedChallengesSelector } from '../../../redux/selectors';
|
||||
import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types';
|
||||
import { playTone } from '../../../utils/tone';
|
||||
import { makeExpandedBlockSelector, toggleBlock } from '../redux';
|
||||
import {
|
||||
isNewJsCert,
|
||||
isSciCompPyCert,
|
||||
isNewRespCert
|
||||
} from '../../../utils/is-a-cert';
|
||||
import {
|
||||
isCodeAllyPractice,
|
||||
isFinalProject
|
||||
} from '../../../../../shared/config/challenge-types';
|
||||
import { isGridBased, isProjectBased } from '../../../utils/curriculum-layout';
|
||||
import Challenges from './challenges';
|
||||
import '../intro.css';
|
||||
|
||||
@@ -118,21 +110,12 @@ class Block extends Component<BlockProps> {
|
||||
});
|
||||
|
||||
const isProjectBlock = challenges.some(({ challenge }) => {
|
||||
const isTakeHomeProject = blockDashedName === 'take-home-projects';
|
||||
|
||||
const projectCondition = [
|
||||
isFinalProject(challenge.challengeType),
|
||||
isCodeAllyPractice(challenge.challengeType)
|
||||
].some(Boolean);
|
||||
|
||||
return projectCondition && !isTakeHomeProject;
|
||||
return isProjectBased(challenge.challengeType, blockDashedName);
|
||||
});
|
||||
|
||||
const isGridBlock = [
|
||||
isNewRespCert(superBlock),
|
||||
isNewJsCert(superBlock),
|
||||
isSciCompPyCert(superBlock) && !isProjectBlock
|
||||
].some(Boolean);
|
||||
const isGridBlock = challenges.some(({ challenge }) => {
|
||||
return isGridBased(superBlock, challenge.challengeType);
|
||||
});
|
||||
|
||||
const isAudited = isAuditedSuperBlock(curriculumLocale, superBlock, {
|
||||
showNewCurriculum,
|
||||
|
||||
47
client/src/utils/curriculum-layout.ts
Normal file
47
client/src/utils/curriculum-layout.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { challengeTypes } from '../../../shared/config/challenge-types';
|
||||
import { SuperBlocks } from '../../../shared/config/superblocks';
|
||||
|
||||
// Show a grid layout on the superblock level
|
||||
|
||||
const gridBasedSuperBlocks = [
|
||||
SuperBlocks.RespWebDesignNew,
|
||||
SuperBlocks.JsAlgoDataStructNew,
|
||||
SuperBlocks.SciCompPy
|
||||
];
|
||||
|
||||
export const isGridBased = (
|
||||
superBlock: SuperBlocks,
|
||||
challengeType: unknown = null
|
||||
) => {
|
||||
// Python projects should not be displayed as a grid, but should be displayed
|
||||
// as a list of projects. Otherwise, if we do not do this the project will be
|
||||
// shown as a single certificaton project, which is not what we want.
|
||||
|
||||
if (challengeType === 10) return false;
|
||||
|
||||
return gridBasedSuperBlocks.includes(superBlock);
|
||||
};
|
||||
|
||||
// Show a single project in a certification layout
|
||||
|
||||
const projectBasedChallengeTypes = [
|
||||
challengeTypes.frontEndProject,
|
||||
challengeTypes.backEndProject,
|
||||
challengeTypes.jsProject,
|
||||
challengeTypes.pythonProject,
|
||||
challengeTypes.codeAllyCert,
|
||||
challengeTypes.multifileCertProject,
|
||||
challengeTypes.exam,
|
||||
challengeTypes.codeAllyPractice
|
||||
];
|
||||
|
||||
export const isProjectBased = (
|
||||
challengeType: number,
|
||||
blockDashedName: unknown = null
|
||||
) => {
|
||||
// Is project based but should be collapsable, this differs from the
|
||||
// other projects which are not collapsable.
|
||||
if (blockDashedName === 'take-home-projects') return false;
|
||||
|
||||
return projectBasedChallengeTypes.includes(challengeType);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AllChallengesInfo } from '../redux/prop-types';
|
||||
import { isFinalProject } from '../../../shared/config/challenge-types';
|
||||
import { isProjectBased } from './curriculum-layout';
|
||||
|
||||
export function getCompletedPercentage(
|
||||
completedChallengesIds: string[] = [],
|
||||
@@ -48,7 +48,7 @@ export const getCurrentBlockIds = (
|
||||
.filter(edge => edge.node.challenge.block === block)
|
||||
.map(edge => edge.node.challenge.id);
|
||||
|
||||
return isFinalProject(challengeType)
|
||||
return isProjectBased(challengeType)
|
||||
? currentCertificateIds
|
||||
: currentBlockIds;
|
||||
};
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
import { SuperBlocks } from '../../../shared/config/superblocks';
|
||||
|
||||
export function isNewRespCert(superBlock: string): boolean {
|
||||
return superBlock === String(SuperBlocks.RespWebDesignNew);
|
||||
}
|
||||
|
||||
export function isOldRespCert(superBlock: string): boolean {
|
||||
return superBlock === String(SuperBlocks.RespWebDesign);
|
||||
}
|
||||
|
||||
export function isNewJsCert(superBlock: string): boolean {
|
||||
return superBlock === String(SuperBlocks.JsAlgoDataStructNew);
|
||||
}
|
||||
|
||||
export function isRelationalDbCert(superBlock: string): boolean {
|
||||
return superBlock === String(SuperBlocks.RelationalDb);
|
||||
}
|
||||
|
||||
export function isCollegeAlgebraPyCert(superBlock: string): boolean {
|
||||
return superBlock === String(SuperBlocks.CollegeAlgebraPy);
|
||||
}
|
||||
|
||||
export function isSciCompPyCert(superBlock: string): boolean {
|
||||
return superBlock === String(SuperBlocks.SciCompPy);
|
||||
}
|
||||
|
||||
@@ -50,28 +50,6 @@ export const challengeTypes = {
|
||||
fillInTheBlank
|
||||
};
|
||||
|
||||
export const isFinalProject = (challengeType: number) => {
|
||||
// TODO: remove the type check once everything is converted to TS
|
||||
if (typeof challengeType !== 'number')
|
||||
throw Error('challengeType must be a number');
|
||||
return (
|
||||
challengeType === frontEndProject ||
|
||||
challengeType === backEndProject ||
|
||||
challengeType === jsProject ||
|
||||
challengeType === pythonProject ||
|
||||
challengeType === codeAllyCert ||
|
||||
challengeType === multifileCertProject ||
|
||||
challengeType === exam
|
||||
);
|
||||
};
|
||||
|
||||
export const isCodeAllyPractice = (challengeType: number) => {
|
||||
// TODO: remove the type check once everything is converted to TS
|
||||
if (typeof challengeType !== 'number')
|
||||
throw Error('challengeType must be a number');
|
||||
return challengeType === codeAllyPractice;
|
||||
};
|
||||
|
||||
export const hasNoTests = (challengeType: number): boolean =>
|
||||
challengeType === multipleChoice ||
|
||||
challengeType === theOdinProject ||
|
||||
|
||||
Reference in New Issue
Block a user