refactor(client, curriculum): consolidate is-FSD checks (#59598)

This commit is contained in:
Huyen Nguyen
2025-04-09 15:49:26 +07:00
committed by GitHub
parent 17800ef172
commit 1f1e6ff626
8 changed files with 37 additions and 23 deletions
+2 -1
View File
@@ -18,6 +18,7 @@ import {
import { stringifyDonationEvents } from '../utils/analytics-strings';
import { stripe } from '../utils/stripe';
import { PaymentProvider } from '../../../shared/config/donation-settings';
import { chapterBasedSuperBlocks } from '../../../shared/config/curriculum';
import {
getSessionChallengeData,
saveCurrentCount
@@ -52,7 +53,7 @@ function* showDonateModalSaga() {
if (
shouldRequestDonation &&
recentlyClaimedBlock &&
recentlyClaimedBlock.superBlock === 'full-stack-developer'
chapterBasedSuperBlocks.includes(recentlyClaimedBlock.superBlock)
) {
yield put(preventBlockDonationRequests());
} else if (shouldRequestDonation || isModalRecentlyShown) {
@@ -8,7 +8,10 @@ import { createSelector } from 'reselect';
import { Spacer } from '@freecodecamp/ui';
import { challengeTypes } from '../../../../../shared/config/challenge-types';
import { SuperBlocks } from '../../../../../shared/config/curriculum';
import {
chapterBasedSuperBlocks,
SuperBlocks
} from '../../../../../shared/config/curriculum';
import envData from '../../../../config/env.json';
import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited';
import Caret from '../../../assets/icons/caret';
@@ -407,7 +410,7 @@ class Block extends Component<BlockProps> {
!isEmptyBlock && (
<>
{layoutToComponent[blockLayout]}
{superBlock !== SuperBlocks.FullStackDeveloper && (
{!chapterBasedSuperBlocks.includes(superBlock) && (
<Spacer size='xs' />
)}
</>
@@ -11,7 +11,10 @@ import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
import { SuperBlocks } from '../../../../shared/config/curriculum';
import {
chapterBasedSuperBlocks,
SuperBlocks
} from '../../../../shared/config/curriculum';
import DonateModal from '../../components/Donation/donation-modal';
import Login from '../../components/Header/components/login';
import Map from '../../components/Map';
@@ -172,8 +175,6 @@ const SuperBlockIntroductionPage = (props: SuperBlockProps) => {
cert => superBlockToCertMap[superBlock] === cert.certSlug
);
const superBlockWithAccordionView = [SuperBlocks.FullStackDeveloper];
const getInitiallyExpandedBlock = (): string => {
// if coming from breadcrumb click
if (
@@ -258,7 +259,7 @@ const SuperBlockIntroductionPage = (props: SuperBlockProps) => {
{t(`intro:misc-text.courses`)}
</h2>
<Spacer size='m' />
{superBlockWithAccordionView.includes(superBlock) ? (
{chapterBasedSuperBlocks.includes(superBlock) ? (
<SuperBlockAccordion
challenges={superBlockChallenges}
superBlock={superBlock}
+3 -6
View File
@@ -15,6 +15,7 @@ const {
const { isAuditedSuperBlock } = require('../shared/utils/is-audited');
const { createPoly } = require('../shared/utils/polyvinyl');
const { chapterBasedSuperBlocks } = require('../shared/config/curriculum');
const {
getSuperOrder,
getSuperBlockFromDir,
@@ -287,16 +288,12 @@ async function buildChallenges({ path: filePath }, curriculum, lang) {
challengeBlock.challenges = [...challengeBlock.challenges, challenge];
}
function isSuperBlockWithChapters(superBlock) {
return superBlock === 'full-stack-developer';
}
// This is a slightly weird abstraction, but it lets us define helper functions
// without passing around a ton of arguments.
function generateChallengeCreator(lang, englishPath, i18nPath) {
function addMetaToChallenge(challenge, meta) {
function addChapterAndModuleToChallenge(challenge) {
if (isSuperBlockWithChapters(challenge.superBlock)) {
if (chapterBasedSuperBlocks.includes(challenge.superBlock)) {
challenge.chapter = getChapterFromBlock(
challenge.block,
fullStackSuperBlockStructure
@@ -329,7 +326,7 @@ function generateChallengeCreator(lang, englishPath, i18nPath) {
challenge.blockType = meta.blockType;
challenge.blockLayout = meta.blockLayout;
challenge.hasEditableBoundaries = !!meta.hasEditableBoundaries;
challenge.order = isSuperBlockWithChapters(meta.superBlock)
challenge.order = chapterBasedSuperBlocks.includes(meta.superBlock)
? getBlockOrder(meta.dashedName, fullStackSuperBlockStructure)
: meta.order;
+7 -4
View File
@@ -2,7 +2,10 @@ const Joi = require('joi');
Joi.objectId = require('joi-objectid')(Joi);
const { challengeTypes } = require('../../shared/config/challenge-types');
const { SuperBlocks } = require('../../shared/config/curriculum');
const {
SuperBlocks,
chapterBasedSuperBlocks
} = require('../../shared/config/curriculum');
const {
availableCharacters,
availableBackgrounds,
@@ -128,7 +131,7 @@ const schema = Joi.object()
block: Joi.string().regex(slugRE).required(),
blockId: Joi.objectId(),
blockType: Joi.when('superBlock', {
is: [SuperBlocks.FullStackDeveloper],
is: chapterBasedSuperBlocks,
then: Joi.valid(
'workshop',
'lab',
@@ -151,7 +154,7 @@ const schema = Joi.object()
).required(),
challengeOrder: Joi.number(),
chapter: Joi.string().when('superBlock', {
is: 'full-stack-developer',
is: chapterBasedSuperBlocks,
then: Joi.required(),
otherwise: Joi.optional()
}),
@@ -211,7 +214,7 @@ const schema = Joi.object()
isLocked: Joi.bool(),
isPrivate: Joi.bool(),
module: Joi.string().when('superBlock', {
is: 'full-stack-developer',
is: chapterBasedSuperBlocks,
then: Joi.required(),
otherwise: Joi.optional()
}),
+5 -2
View File
@@ -1,6 +1,9 @@
const Joi = require('joi');
const { SuperBlocks } = require('../../shared/config/curriculum');
const {
SuperBlocks,
chapterBasedSuperBlocks
} = require('../../shared/config/curriculum');
const slugRE = new RegExp('^[a-z0-9-]+$');
const slugWithSlashRE = new RegExp('^[a-z0-9-/]+$');
@@ -33,7 +36,7 @@ const schema = Joi.object()
.valid(...Object.values(SuperBlocks))
.required(),
order: Joi.number().when('superBlock', {
is: 'full-stack-developer',
is: chapterBasedSuperBlocks,
then: Joi.forbidden(),
otherwise: Joi.required()
}),
+7 -4
View File
@@ -51,7 +51,10 @@ const {
createContent,
testId
} = require('../../client/src/templates/Challenges/utils/frame');
const { SuperBlocks } = require('../../shared/config/curriculum');
const {
SuperBlocks,
chapterBasedSuperBlocks
} = require('../../shared/config/curriculum');
const ChallengeTitles = require('./utils/challenge-titles');
const MongoIds = require('./utils/mongo-ids');
const createPseudoWorker = require('./utils/pseudo-worker');
@@ -313,11 +316,11 @@ function populateTestsForLang({ lang, challenges, meta, superBlocks }) {
);
});
filteredMeta.forEach((meta, index) => {
// Upcoming changes are in developmen so are not required to be in
// order. FullStackDeveloper does not use the meta for order.
// Upcoming changes are in development so are not required to be in
// order. Chapter-based super blocks do not use the meta for order.
if (
!meta.isUpcomingChange &&
meta.superBlock !== SuperBlocks.FullStackDeveloper
!chapterBasedSuperBlocks.includes(meta.superBlock)
) {
it(`${meta.superBlock} ${meta.name} must be in order`, function () {
assert.equal(meta.order, index);
+3
View File
@@ -240,6 +240,9 @@ export const notAuditedSuperBlocks: NotAuditedSuperBlocks = {
Object.freeze(notAuditedSuperBlocks);
export const chapterBasedSuperBlocks = [SuperBlocks.FullStackDeveloper];
Object.freeze(chapterBasedSuperBlocks);
type Config = {
showUpcomingChanges: boolean;
};