refactor(config): superblocks and map (#50435)

This commit is contained in:
Tom
2023-06-01 07:51:57 -05:00
committed by GitHub
parent 7267fe29ca
commit 373e808938
40 changed files with 558 additions and 1287 deletions

View File

@@ -7,6 +7,6 @@ config/i18n.js
config/misc.js
config/certification-settings.js
config/donation-settings.js
config/superblock-order.js
config/superblocks.js
web/**
docs/**/*.md

4
.gitignore vendored
View File

@@ -166,8 +166,8 @@ config/i18n.js
config/misc.js
config/certification-settings.js
config/donation-settings.js
config/superblock-order.js
config/superblock-order.test.js
config/superblocks.js
config/superblocks.test.js
### Generated utils files ###
utils/block-nameify.js

View File

@@ -10,8 +10,8 @@ config/i18n.js
config/misc.js
config/certification-settings.js
config/donation-settings.js
config/superblock-order.js
config/superblock-order.test.js
config/superblocks.js
config/superblocks.test.js
utils/block-nameify.js
utils/block-nameify.test.js
utils/slugs.js

View File

@@ -1,7 +1,7 @@
import fs from 'fs';
import { setup } from 'jest-json-schema-extended';
import { availableLangs, LangNames, LangCodes } from '../../config/i18n';
import { SuperBlocks } from '../../config/certification-settings';
import { SuperBlocks } from '../../config/superblocks';
import intro from './locales/english/intro.json';
setup();

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { SuperBlocks } from '../../../../config/certification-settings';
import { SuperBlocks } from '../../../../config/superblocks';
import APIIcon from './api';
import D3Icon from './d3';
import DatabaseIcon from './database';

View File

@@ -1,26 +1,24 @@
import i18next from 'i18next';
import React from 'react';
import { SuperBlocks } from '../../../../config/certification-settings';
import {
CurriculumMaps,
getAuditedSuperBlocks,
getNotAuditedSuperBlocks,
superBlockOrder
} from '../../../../config/superblock-order';
import { Languages } from '../../../../config/i18n';
import envData from '../../../../config/env.json';
SuperBlocks,
createFlatSuperBlockMap,
getFirstNotAuditedSuperBlock
} from '../../../../config/superblocks';
import { generateIconComponent } from '../../assets/icons';
import LinkButton from '../../assets/icons/link-button';
import { Link, Spacer } from '../helpers';
import { getSuperBlockTitleForMap } from '../../utils/superblock-map-titles';
import {
curriculumLocale,
showUpcomingChanges,
showNewCurriculum
} from '../../../../config/env.json';
import './map.css';
const { curriculumLocale, showNewCurriculum, showUpcomingChanges } = envData;
interface MapProps {
currentSuperBlock?: SuperBlocks | null;
forLanding?: boolean;
}
@@ -31,6 +29,16 @@ const linkSpacingStyle = {
gap: '15px'
};
const flatSuperBlockMap = createFlatSuperBlockMap({
showNewCurriculum: showNewCurriculum.toString(),
showUpcomingChanges: showUpcomingChanges.toString()
});
const firstNotAuditedSuperBlock = getFirstNotAuditedSuperBlock({
language: curriculumLocale,
showNewCurriculum: showNewCurriculum.toString(),
showUpcomingChanges: showUpcomingChanges.toString()
});
function MapLi({
superBlock,
landing = false
@@ -39,62 +47,9 @@ function MapLi({
landing: boolean;
}) {
return (
<li>
<Link className='btn link-btn btn-lg' to={`/learn/${superBlock}/`}>
<div style={linkSpacingStyle}>
{generateIconComponent(superBlock, 'map-icon')}
{getSuperBlockTitleForMap(superBlock)}
</div>
{landing && <LinkButton />}
</Link>
</li>
);
}
function renderLandingMap() {
const landingSuperOrder =
superBlockOrder[curriculumLocale as Languages][CurriculumMaps.Landing];
return (
<ul data-test-label='certifications'>
{landingSuperOrder.map((superBlock, i) => (
<MapLi superBlock={superBlock} key={i} landing={true} />
))}
</ul>
);
}
function renderLearnMap(currentSuperBlock: MapProps['currentSuperBlock']) {
const tempAuditedSuperBlocks = getAuditedSuperBlocks({
language: curriculumLocale,
showNewCurriculum: showNewCurriculum.toString(),
showUpcomingChanges: showUpcomingChanges.toString()
});
const tempNotAuditedSuperBlocks = getNotAuditedSuperBlocks({
language: curriculumLocale,
showNewCurriculum: showNewCurriculum.toString(),
showUpcomingChanges: showUpcomingChanges.toString()
});
const auditedSuperBlocks = tempAuditedSuperBlocks.filter(
superBlock => superBlock !== currentSuperBlock
);
const notAuditedSuperBlocks = tempNotAuditedSuperBlocks.filter(
superBlock => superBlock !== currentSuperBlock
);
return (
<ul data-test-label='learn-curriculum-map'>
{/* audited superblocks */}
{auditedSuperBlocks.map((superBlock, i) => (
<MapLi key={i} superBlock={superBlock} landing={false} />
))}
{/* has not audited superblocks */}
{notAuditedSuperBlocks.length > 0 && (
<>
{firstNotAuditedSuperBlock === superBlock && (
<>
{' '}
<hr />
<div style={{ textAlign: 'center' }}>
<p style={{ marginBottom: 0 }}>
@@ -112,21 +67,27 @@ function renderLearnMap(currentSuperBlock: MapProps['currentSuperBlock']) {
</>
)}
{/* not audited superblocks */}
{notAuditedSuperBlocks.map((superBlock, i) => (
<MapLi key={i} superBlock={superBlock} landing={false} />
))}
</ul>
<li data-test-label='curriculum-map-button'>
<Link className='btn link-btn btn-lg' to={`/learn/${superBlock}/`}>
<div style={linkSpacingStyle}>
{generateIconComponent(superBlock, 'map-icon')}
{getSuperBlockTitleForMap(superBlock)}
</div>
{landing && <LinkButton />}
</Link>
</li>
</>
);
}
function Map({
forLanding = false,
currentSuperBlock = null
}: MapProps): React.ReactElement {
function Map({ forLanding = false }: MapProps): React.ReactElement {
return (
<div className='map-ui' data-test-label='learn-curriculum-map'>
{forLanding ? renderLandingMap() : renderLearnMap(currentSuperBlock)}
<div className='map-ui' data-test-label='curriculum-map'>
<ul>
{flatSuperBlockMap.map((superBlock, i) => (
<MapLi key={i} superBlock={superBlock} landing={forLanding} />
))}
</ul>
</div>
);
}

View File

@@ -2,7 +2,7 @@ import { useStaticQuery, graphql } from 'gatsby';
import React from 'react';
import Helmet from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { SuperBlocks } from '../../../../config/certification-settings';
import { SuperBlocks } from '../../../../config/superblocks';
interface SEOProps {
title?: string;

View File

@@ -1,5 +1,5 @@
import { HandlerProps } from 'react-reflex';
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
import { Themes } from '../components/settings/theme';
import { fullCertMap } from '../resources/cert-and-project-map';

View File

@@ -1,4 +1,4 @@
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
import { ns as MainApp } from './action-types';
export const savedChallengesSelector = state =>

View File

@@ -1,4 +1,4 @@
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
import config from '../../../config/env.json';
const { showUpcomingChanges } = config;

View File

@@ -44,7 +44,7 @@ import {
import ProjectToolPanel from '../projects/tool-panel';
import SolutionForm from '../projects/solution-form';
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
import { CodeAllyDown } from '../../../components/growth-book/codeally-down';
import './codeally.css';

View File

@@ -6,7 +6,7 @@ import { connect } from 'react-redux';
import ScrollableAnchor from 'react-scrollable-anchor';
import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
import envData from '../../../../../config/env.json';
import { isAuditedCert } from '../../../../../utils/is-audited';
import Caret from '../../../assets/icons/caret';

View File

@@ -6,9 +6,10 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import {
certSlugTypeMap,
superBlockCertTypeMap,
SuperBlocks
superBlockCertTypeMap
} from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
import { createFlashMessage } from '../../../components/Flash/redux';
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
import {

View File

@@ -8,7 +8,7 @@ import type { Dispatch } from 'redux';
import GreenNotCompleted from '../../../assets/icons/green-not-completed';
import GreenPass from '../../../assets/icons/green-pass';
import { executeGA } from '../../../redux/actions';
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
import { ChallengeWithCompletedNode } from '../../../redux/prop-types';
import { isNewJsCert, isNewRespCert } from '../../../utils/is-a-cert';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Alert } from '@freecodecamp/react-bootstrap';
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
import { isOldRespCert, isRelationalDbCert } from '../../../utils/is-a-cert';
import { Link } from '../../../components/helpers';
import { CodeAllyDown } from '../../../components/growth-book/codeally-down';

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
import { generateIconComponent } from '../../../assets/icons';
import { Spacer } from '../../../components/helpers';

View File

@@ -10,7 +10,7 @@ import { configureAnchors } from 'react-scrollable-anchor';
import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { SuperBlocks } from '../../../../config/certification-settings';
import { SuperBlocks } from '../../../../config/superblocks';
import { getSuperBlockTitleForMap } from '../../utils/superblock-map-titles';
import DonateModal from '../../components/Donation/donation-modal';
import Login from '../../components/Header/components/login';

View File

@@ -1,4 +1,4 @@
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
export function isNewRespCert(superBlock: string): boolean {
return superBlock === SuperBlocks.RespWebDesignNew;

View File

@@ -1,5 +1,5 @@
import i18next from 'i18next';
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
// these are keys from i18n translations.json files
enum SuperBlockI18nKeys {

View File

@@ -1,3 +1,5 @@
import { SuperBlocks } from './superblocks';
export const certTypes = {
frontEnd: 'isFrontEndCert',
backEnd: 'isBackEndCert',
@@ -18,27 +20,6 @@ export const certTypes = {
collegeAlgebraPyV8: 'isCollegeAlgebraPyCertV8'
} as const;
export enum SuperBlocks {
RespWebDesignNew = '2022/responsive-web-design',
RespWebDesign = 'responsive-web-design',
JsAlgoDataStruct = 'javascript-algorithms-and-data-structures',
JsAlgoDataStructNew = '2022/javascript-algorithms-and-data-structures',
FrontEndDevLibs = 'front-end-development-libraries',
DataVis = 'data-visualization',
RelationalDb = 'relational-database',
BackEndDevApis = 'back-end-development-and-apis',
QualityAssurance = 'quality-assurance',
SciCompPy = 'scientific-computing-with-python',
DataAnalysisPy = 'data-analysis-with-python',
InfoSec = 'information-security',
MachineLearningPy = 'machine-learning-with-python',
CodingInterviewPrep = 'coding-interview-prep',
TheOdinProject = 'the-odin-project',
ProjectEuler = 'project-euler',
CollegeAlgebraPy = 'college-algebra-with-python',
ExampleCertification = 'example-certification'
}
export const certIds = {
legacyFrontEndChallengeId: '561add10cb82ac38a17513be',
legacyBackEndChallengeId: '660add10cb82ac38a17513be',

View File

@@ -101,12 +101,13 @@ export const LangCodes = {
/**
* This array contains languages that should NOT appear in the language selector.
*/
export const hiddenLangs = ['arabic'];
// German is temporarily disabled until the new RWD is fully translated
export const hiddenLangs = [Languages.Arabic, Languages.German];
/**
* This array contains languages that use the RTL layouts.
*/
export const rtlLangs = ['arabic'];
export const rtlLangs = [Languages.Arabic];
// locale is sourced from a JSON file, so we use getLangCode to
// find the associated enum values

View File

@@ -1,205 +0,0 @@
import { Languages } from './i18n';
import { SuperBlocks } from './certification-settings';
import {
CurriculumMaps,
defaultSuperBlockOrder,
getAuditedSuperBlocks,
getNotAuditedSuperBlocks,
getLearnSuperBlocks,
numberOfSuperBlocksOnLanding,
superBlockOrder,
SuperBlockStates,
TranslationStates
} from './superblock-order';
const superBlocks = Object.values(SuperBlocks);
const translationStates = Object.values(TranslationStates);
const superBlockStates = Object.values(SuperBlockStates);
const superBlockOrderLanguages = Object.keys(superBlockOrder);
describe("'defaultSuperBlockOrder'", () => {
it("should have a matching item for each value in the 'SuperBlocks' object", () => {
expect(defaultSuperBlockOrder).toEqual(expect.arrayContaining(superBlocks));
});
it('should not have any extra keys', () => {
expect(defaultSuperBlockOrder.length).toEqual(superBlocks.length);
});
});
describe("'superBlockOrder'", () => {
superBlockOrderLanguages.forEach(language => {
describe(`'${language}'`, () => {
describe("'landing'", () => {
const landingSuperBlocks =
superBlockOrder[language as Languages][CurriculumMaps.Landing];
it(`should have ${numberOfSuperBlocksOnLanding} items (superBlocks)`, () => {
expect(landingSuperBlocks.length).toEqual(
numberOfSuperBlocksOnLanding
);
});
it('should not have a superBlock out of order', () => {
landingSuperBlocks.forEach((superBlock, index) => {
const defaultIndex = defaultSuperBlockOrder.indexOf(superBlock);
const defaultSbsAfterCurrentSb = defaultSuperBlockOrder.slice(
defaultIndex + 1
);
for (let j = index + 1; j < landingSuperBlocks.length; j++) {
expect(defaultSbsAfterCurrentSb).toContain(landingSuperBlocks[j]);
}
});
});
});
describe("'learn'", () => {
const learn =
superBlockOrder[language as Languages][CurriculumMaps.Learn];
const audited = learn[TranslationStates.Audited];
const notAudited = learn[TranslationStates.NotAudited];
describe("'audited'", () => {
superBlockStates.forEach(superBlockState => {
const stateSuperBlocks = audited[superBlockState];
describe(`'${superBlockState}'`, () => {
it('should not have a superBlock out of order', () => {
stateSuperBlocks.forEach((superBlock, index) => {
const defaultIndex =
defaultSuperBlockOrder.indexOf(superBlock);
const defaultSbsAfterCurrentSb = defaultSuperBlockOrder.slice(
defaultIndex + 1
);
for (let j = index + 1; j < stateSuperBlocks.length; j++) {
expect(defaultSbsAfterCurrentSb).toContain(
stateSuperBlocks[j]
);
}
});
});
});
});
});
describe('not audited', () => {
superBlockStates.forEach(superBlockState => {
const stateSuperBlocks = notAudited[superBlockState];
describe(`'${superBlockState}'`, () => {
it('should not have a superBlock out of order', () => {
stateSuperBlocks.forEach((superBlock, index) => {
const defaultIndex =
defaultSuperBlockOrder.indexOf(superBlock);
const defaultSbsAfterCurrentSb = defaultSuperBlockOrder.slice(
defaultIndex + 1
);
for (let j = index + 1; j < stateSuperBlocks.length; j++) {
expect(defaultSbsAfterCurrentSb).toContain(
stateSuperBlocks[j]
);
}
});
});
});
});
});
it("should have exactly one of each 'SuperBlocks' among it's children", () => {
// flatten all ${language}.learn superblocks into one array
const learnSuperBlocks: SuperBlocks[] = [];
translationStates.forEach(translationState => {
superBlockStates.forEach(superBlockState => {
learnSuperBlocks.push(
...learn[translationState][superBlockState]
);
});
});
superBlocks.forEach(superBlock => {
expect(learnSuperBlocks).toContain(superBlock);
});
});
});
});
});
});
describe("'superBlockOrder' helper functions", () => {
it("'getLearnSuperBlocks('english')' should return the correct array", () => {
const learnSuperBlocks = getLearnSuperBlocks({
language: 'english',
showNewCurriculum: 'true',
showUpcomingChanges: 'true'
});
const test = [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification,
SuperBlocks.RespWebDesign
];
expect(learnSuperBlocks).toStrictEqual(test);
expect(learnSuperBlocks.length).toEqual(test.length);
});
it("'getAuditedSuperBlocks('german')' should return the correct array", () => {
const auditedSuperBlocks = getAuditedSuperBlocks({
language: 'german',
showNewCurriculum: 'true',
showUpcomingChanges: 'true'
});
const test = [
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs
];
expect(auditedSuperBlocks).toStrictEqual(test);
expect(auditedSuperBlocks.length).toEqual(test.length);
});
it("'getNotAuditedSuperBlocks('german')' should return the correct array", () => {
const notAuditedSuperBlocks = getNotAuditedSuperBlocks({
language: 'german',
showNewCurriculum: 'true',
showUpcomingChanges: 'true'
});
console.log(notAuditedSuperBlocks);
const test = [
SuperBlocks.RespWebDesignNew,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
];
expect(notAuditedSuperBlocks).toStrictEqual(test);
expect(notAuditedSuperBlocks.length).toEqual(test.length);
});
});

View File

@@ -1,707 +0,0 @@
import { Languages } from './i18n';
import { SuperBlocks } from './certification-settings';
/*
* .env SHOW_NEW_CURRICULUM = SuperBlockStates.New
* 'New' -> shown only on english staging at the moment
*
* .env SHOW_UPCOMING_CHANGES = SuperBlockStates.Upcoming
* 'Upcoming' is for development -> not shown on stag or prod anywhere
*
*/
export enum CurriculumMaps {
Landing = 'landing',
Learn = 'learn'
}
export enum TranslationStates {
Audited = 'audited',
NotAudited = 'notAudited'
}
export enum SuperBlockStates {
Current = 'current',
New = 'new',
Upcoming = 'upcoming',
Legacy = 'legacy'
}
export const orderedSuperBlockStates = [
SuperBlockStates.Current,
SuperBlockStates.New,
SuperBlockStates.Upcoming,
SuperBlockStates.Legacy
];
type SuperBlockOrder = {
[key in Languages]: {
[CurriculumMaps.Landing]: SuperBlocks[];
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: SuperBlocks[];
[SuperBlockStates.New]: SuperBlocks[];
[SuperBlockStates.Upcoming]: SuperBlocks[];
[SuperBlockStates.Legacy]: SuperBlocks[];
};
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: SuperBlocks[];
[SuperBlockStates.New]: SuperBlocks[];
[SuperBlockStates.Upcoming]: SuperBlocks[];
[SuperBlockStates.Legacy]: SuperBlocks[];
};
};
};
};
// all languages should have this many, one for each current cert
export const numberOfSuperBlocksOnLanding = 12;
/*
* This is the used for tests to make sure a superBlock isn't out of order
* e.g. so that a RWD button isn't below a JS button.
* It compares each array in `superBlockOrder` to this - those arrays do not
* have to include all these superBlocks, but the ones it does include, have
* to be in this order
*/
export const defaultSuperBlockOrder: SuperBlocks[] = [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
];
/*
* The order of superblocks in the arrays below are how they appear on the maps
*
* The 'Landing' map array should contain exactly one superblock for each
* current, non-legacy certification, and only one superblock of each type -
* e.g. only one RWD superblock (button)
*
* The 'Learn' map arrays should contain ALL available SuperBlocks, sorted into
* their various states. These will be used to create the 'superOrder' property.
*
*/
export const superBlockOrder: SuperBlockOrder = {
[Languages.English]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Espanol]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Chinese]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.ChineseTraditional]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Italian]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Portuguese]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.TheOdinProject],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Ukrainian]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.TheOdinProject],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Japanese]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: []
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.German]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: []
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Arabic]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: []
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
}
}
}
};
// The client uses the object above to create the map
// Keep this so it can't change
Object.freeze(superBlockOrder);
function shouldShowSuperblocks({
superBlockState,
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}: {
superBlockState: string;
showNewCurriculum: string;
showUpcomingChanges: string;
}) {
if (
(superBlockState === SuperBlockStates.New &&
showNewCurriculum !== 'true') ||
(superBlockState === SuperBlockStates.Upcoming &&
showUpcomingChanges !== 'true')
) {
return false;
}
return true;
}
type Config = {
language: string;
showNewCurriculum?: string;
showUpcomingChanges?: string;
};
export function getLearnSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}: Config): SuperBlocks[] {
const learnSuperBlocks: SuperBlocks[] = [];
Object.values(TranslationStates).forEach(translationState => {
Object.values(SuperBlockStates).forEach(superBlockState => {
if (
shouldShowSuperblocks({
superBlockState,
showNewCurriculum,
showUpcomingChanges
})
) {
learnSuperBlocks.push(
...superBlockOrder[language as Languages][CurriculumMaps.Learn][
translationState as TranslationStates
][superBlockState as SuperBlockStates]
);
}
});
});
return learnSuperBlocks;
}
export function getAuditedSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}: Config): SuperBlocks[] {
const auditedSuperBlocks: SuperBlocks[] = [];
Object.values(SuperBlockStates).forEach(superBlockState => {
if (
shouldShowSuperblocks({
superBlockState,
showNewCurriculum,
showUpcomingChanges
})
) {
auditedSuperBlocks.push(
...superBlockOrder[language as Languages][CurriculumMaps.Learn][
TranslationStates.Audited
][superBlockState as SuperBlockStates]
);
}
});
return auditedSuperBlocks;
}
export function getNotAuditedSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}: Config): SuperBlocks[] {
const notAuditedSuperBlocks: SuperBlocks[] = [];
Object.values(SuperBlockStates).forEach(superBlockState => {
if (
shouldShowSuperblocks({
superBlockState,
showNewCurriculum,
showUpcomingChanges
})
) {
notAuditedSuperBlocks.push(
...superBlockOrder[language as Languages][CurriculumMaps.Learn][
TranslationStates.NotAudited
][superBlockState as SuperBlockStates]
);
}
});
return notAuditedSuperBlocks;
}

125
config/superblocks.test.ts Normal file
View File

@@ -0,0 +1,125 @@
import { Languages } from './i18n';
import {
SuperBlocks,
SuperBlockStages,
superBlockOrder,
notAuditedSuperBlocks,
createSuperBlockMap,
createFlatSuperBlockMap,
getAuditedSuperBlocks,
getFirstNotAuditedSuperBlock
} from './superblocks';
describe('superBlockOrder', () => {
it('should contain all SuperBlocks', () => {
const allSuperBlocks = Object.values(SuperBlocks);
const superBlockOrderValues = Object.values(superBlockOrder).flat();
expect(superBlockOrderValues).toHaveLength(allSuperBlocks.length);
expect(superBlockOrderValues).toEqual(
expect.arrayContaining(allSuperBlocks)
);
});
});
describe('createSuperBlockMap', () => {
it('should return an object with New and Upcoming when { showNewCurriculum: true, showUpcomingChanges: true }', () => {
const result = createSuperBlockMap({
showNewCurriculum: 'true',
showUpcomingChanges: 'true'
});
expect(result[SuperBlockStages.New]).toHaveLength(
superBlockOrder[SuperBlockStages.New].length
);
expect(result[SuperBlockStages.Upcoming]).toHaveLength(
superBlockOrder[SuperBlockStages.Upcoming].length
);
});
it('should return an object without New and Upcoming when { showNewCurriculum: false, showUpcomingChanges: false }', () => {
const result = createSuperBlockMap({
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
expect(result[SuperBlockStages.New]).toHaveLength(0);
expect(result[SuperBlockStages.Upcoming]).toHaveLength(0);
});
});
describe('createFlatSuperBlockMap', () => {
it('should return an array of SuperBlocks object with New and Upcoming when { showNewCurriculum: true, showUpcomingChanges: true }', () => {
const result = createFlatSuperBlockMap({
showNewCurriculum: 'true',
showUpcomingChanges: 'true'
});
expect(result).toHaveLength(Object.values(superBlockOrder).flat().length);
});
it('should return an array of SuperBlocks without New and Upcoming when { showNewCurriculum: false, showUpcomingChanges: false }', () => {
const result = createFlatSuperBlockMap({
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
const tempSuperBlockMap = { ...superBlockOrder };
tempSuperBlockMap[SuperBlockStages.New] = [];
tempSuperBlockMap[SuperBlockStages.Upcoming] = [];
expect(result).toHaveLength(Object.values(tempSuperBlockMap).flat().length);
});
});
describe('firstNotAuditedSuperBlock', () => {
it("should return 'null' when language is 'english'", () => {
const result = getFirstNotAuditedSuperBlock({
language: Languages.English,
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
expect(result).toBeNull();
});
it("should return a SuperBlock when language is 'chinese'", () => {
const result = getFirstNotAuditedSuperBlock({
language: Languages.Chinese,
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
expect(result).toEqual(SuperBlocks.CollegeAlgebraPy);
});
});
describe('Immutability of superBlockOrder, notAuditedSuperBlocks, and flatSuperBlockMap', () => {
it('should not allow modification of superBlockOrder', () => {
expect(() => {
superBlockOrder[SuperBlockStages.FrontEnd] = [];
}).toThrowError(TypeError);
});
it('should not allow modification of notAuditedSuperBlocks', () => {
expect(() => {
notAuditedSuperBlocks[Languages.English] = [];
}).toThrowError(TypeError);
});
it('should not allow modification of flatSuperBlockMap', () => {
expect(() => {
notAuditedSuperBlocks[Languages.English] = [];
}).toThrowError(TypeError);
});
});
describe('getAuditedSuperBlocks', () => {
Object.keys(notAuditedSuperBlocks).forEach(language => {
it(`should return only audited SuperBlocks for ${language}`, () => {
const auditedSuperBlocks = getAuditedSuperBlocks({
showNewCurriculum: 'true',
showUpcomingChanges: 'true',
language
});
auditedSuperBlocks.forEach(superblock => {
expect(notAuditedSuperBlocks[language as Languages]).not.toContain(
superblock
);
});
});
});
});

243
config/superblocks.ts Normal file
View File

@@ -0,0 +1,243 @@
import { Languages } from './i18n';
// all superblocks
export enum SuperBlocks {
RespWebDesignNew = '2022/responsive-web-design',
RespWebDesign = 'responsive-web-design',
JsAlgoDataStruct = 'javascript-algorithms-and-data-structures',
JsAlgoDataStructNew = '2022/javascript-algorithms-and-data-structures',
FrontEndDevLibs = 'front-end-development-libraries',
DataVis = 'data-visualization',
RelationalDb = 'relational-database',
BackEndDevApis = 'back-end-development-and-apis',
QualityAssurance = 'quality-assurance',
SciCompPy = 'scientific-computing-with-python',
DataAnalysisPy = 'data-analysis-with-python',
InfoSec = 'information-security',
MachineLearningPy = 'machine-learning-with-python',
CodingInterviewPrep = 'coding-interview-prep',
TheOdinProject = 'the-odin-project',
ProjectEuler = 'project-euler',
CollegeAlgebraPy = 'college-algebra-with-python',
ExampleCertification = 'example-certification'
}
/*
* SuperBlockStages.New = SHOW_NEW_CURRICULUM === 'true'
* 'New' -> shown only on english staging at the moment
*
* SuperBlockStages.Upcoming = SHOW_UPCOMING_CHANGES === 'true'
* 'Upcoming' is for development -> not shown on stag or prod anywhere
*/
export enum SuperBlockStages {
FrontEnd = 'frontend',
Backend = 'backend',
Python = 'python',
Extra = 'extra',
Legacy = 'legacy',
New = 'new',
Upcoming = 'upcoming'
}
export type SuperBlockOrder = {
[key in SuperBlockStages]: SuperBlocks[];
};
// order of buttons on map, this should include all superblocks
// new and upcoming superblocks are removed below
export const superBlockOrder: SuperBlockOrder = {
[SuperBlockStages.FrontEnd]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis
],
[SuperBlockStages.Backend]: [
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance
],
[SuperBlockStages.Python]: [
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy
],
[SuperBlockStages.Extra]: [
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler
],
[SuperBlockStages.Legacy]: [SuperBlocks.RespWebDesign],
[SuperBlockStages.New]: [],
[SuperBlockStages.Upcoming]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
]
};
Object.freeze(superBlockOrder);
type NotAuditedSuperBlocks = {
[key in Languages]: SuperBlocks[];
};
// when a superBlock is audited, remove it from its language below
// when adding a new language, add all (not audited) superblocks to the object
export const notAuditedSuperBlocks: NotAuditedSuperBlocks = {
[Languages.English]: [],
[Languages.Espanol]: [
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
],
[Languages.Chinese]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
],
[Languages.ChineseTraditional]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
],
[Languages.Italian]: [
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
],
[Languages.Portuguese]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew
],
[Languages.Ukrainian]: [
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew
],
[Languages.Japanese]: [
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
],
[Languages.German]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
],
[Languages.Arabic]: [
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
]
};
Object.freeze(notAuditedSuperBlocks);
type Config = {
showNewCurriculum: string | undefined;
showUpcomingChanges: string | undefined;
};
type LanguagesConfig = Config & {
language: string;
};
// removes new and upcoming from superBlockOrder
// not used yet, will be used when adding progress indicators to map
export function createSuperBlockMap({
showNewCurriculum,
showUpcomingChanges
}: Config): SuperBlockOrder {
const superBlockMap = { ...superBlockOrder };
if (showNewCurriculum !== 'true') {
superBlockMap[SuperBlockStages.New] = [];
}
if (showUpcomingChanges !== 'true') {
superBlockMap[SuperBlockStages.Upcoming] = [];
}
return superBlockMap;
}
export function createFlatSuperBlockMap({
showNewCurriculum,
showUpcomingChanges
}: Config): SuperBlocks[] {
const superBlockMap = { ...superBlockOrder };
if (showNewCurriculum !== 'true') {
superBlockMap[SuperBlockStages.New] = [];
}
if (showUpcomingChanges !== 'true') {
superBlockMap[SuperBlockStages.Upcoming] = [];
}
return Object.values(superBlockMap).flat();
}
// this is so we know where to display the "help us translate" section
export function getFirstNotAuditedSuperBlock({
language,
showNewCurriculum,
showUpcomingChanges
}: LanguagesConfig): SuperBlocks | null {
const flatSuperBlockMap = createFlatSuperBlockMap({
showNewCurriculum,
showUpcomingChanges
});
for (const superBlock of flatSuperBlockMap) {
if (notAuditedSuperBlocks[language as Languages].includes(superBlock)) {
return superBlock;
}
}
return null;
}
export function getAuditedSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}: LanguagesConfig): SuperBlocks[] {
if (!Object.prototype.hasOwnProperty.call(notAuditedSuperBlocks, language)) {
throw Error(`'${language}' key not found in 'notAuditedSuperBlocks'`);
}
const flatSuperBlockMap = createFlatSuperBlockMap({
showNewCurriculum,
showUpcomingChanges
});
const auditedSuperBlocks = flatSuperBlockMap.filter(
superBlock =>
!notAuditedSuperBlocks[language as Languages].includes(superBlock)
);
return auditedSuperBlocks;
}

View File

@@ -1,11 +1,8 @@
const path = require('path');
const {
CurriculumMaps,
superBlockOrder,
SuperBlockStates,
TranslationStates,
orderedSuperBlockStates
} = require('../config/superblock-order');
createFlatSuperBlockMap,
SuperBlocks
} = require('../config/superblocks');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
@@ -26,72 +23,30 @@ exports.testedLang = function testedLang() {
}
};
/*
* creates an object with all the superblocks in
* 'superBlockOrder[lang][learn]' as keys and gives them
* a number (superOrder), starting with 0, as the value
*/
function createSuperOrder({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}) {
if (!Object.prototype.hasOwnProperty.call(superBlockOrder, language)) {
throw Error(`${language} not found in superblock-order.ts`);
function createSuperOrder(superBlocks) {
if (!Array.isArray(superBlocks)) {
throw Error(`superBlocks must be an Array`);
}
if (
!Object.prototype.hasOwnProperty.call(superBlockOrder[language], [
CurriculumMaps.Learn
])
) {
throw Error(
`${language} does not have a 'learn' key in superblock-order.ts`
);
}
const audited =
superBlockOrder[language][CurriculumMaps.Learn][TranslationStates.Audited];
const notAudited =
superBlockOrder[language][CurriculumMaps.Learn][
TranslationStates.NotAudited
];
superBlocks.forEach(superBlock => {
if (!Object.values(SuperBlocks).includes(superBlock)) {
throw Error(`Invalid superBlock: ${superBlock}`);
}
});
const superOrder = {};
let i = 0;
function addToSuperOrder(superBlocks) {
superBlocks.forEach(key => {
superOrder[key] = i;
i++;
});
}
function canAddToSuperOrder(superBlockState) {
if (superBlockState === SuperBlockStates.New)
return showNewCurriculum === 'true';
if (superBlockState === SuperBlockStates.Upcoming)
return showUpcomingChanges === 'true';
return true;
}
function addSuperBlockStates(translationState) {
orderedSuperBlockStates.forEach(state => {
if (canAddToSuperOrder(state)) addToSuperOrder(translationState[state]);
});
}
addSuperBlockStates(audited);
addSuperBlockStates(notAudited);
superBlocks.forEach((superBlock, i) => {
superOrder[superBlock] = i;
});
return superOrder;
}
const superOrder = createSuperOrder({
language: process.env.CURRICULUM_LOCALE,
const flatSuperBlockMap = createFlatSuperBlockMap({
showNewCurriculum: process.env.SHOW_NEW_CURRICULUM,
showUpcomingChanges: process.env.SHOW_UPCOMING_CHANGES
});
const superOrder = createSuperOrder(flatSuperBlockMap);
// gets the superOrder of a superBlock from the object created above
function getSuperOrder(superblock) {

View File

@@ -4,12 +4,33 @@
import fs from 'fs';
import path from 'path';
import { config } from 'dotenv';
import { SuperBlocks } from '../config/certification-settings';
import { SuperBlocks } from '../config/superblocks';
import { createSuperOrder, getSuperOrder, getSuperBlockFromDir } from './utils';
config({ path: path.resolve(__dirname, '../.env') });
const englishTest = {
const mockSuperBlocks = [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject,
SuperBlocks.ExampleCertification
];
const fullSuperOrder = {
[SuperBlocks.RespWebDesignNew]: 0,
[SuperBlocks.JsAlgoDataStruct]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
@@ -24,105 +45,25 @@ const englishTest = {
[SuperBlocks.CollegeAlgebraPy]: 11,
[SuperBlocks.CodingInterviewPrep]: 12,
[SuperBlocks.ProjectEuler]: 13,
[SuperBlocks.RespWebDesign]: 14
};
const upcomingTest = {
[SuperBlocks.RespWebDesignNew]: 0,
[SuperBlocks.JsAlgoDataStruct]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
[SuperBlocks.DataVis]: 3,
[SuperBlocks.RelationalDb]: 4,
[SuperBlocks.BackEndDevApis]: 5,
[SuperBlocks.QualityAssurance]: 6,
[SuperBlocks.SciCompPy]: 7,
[SuperBlocks.DataAnalysisPy]: 8,
[SuperBlocks.InfoSec]: 9,
[SuperBlocks.MachineLearningPy]: 10,
[SuperBlocks.CollegeAlgebraPy]: 11,
[SuperBlocks.CodingInterviewPrep]: 12,
[SuperBlocks.ProjectEuler]: 13,
[SuperBlocks.JsAlgoDataStructNew]: 14,
[SuperBlocks.TheOdinProject]: 15,
[SuperBlocks.ExampleCertification]: 16,
[SuperBlocks.RespWebDesign]: 17
};
const espanolTest = {
[SuperBlocks.RespWebDesignNew]: 0,
[SuperBlocks.JsAlgoDataStruct]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
[SuperBlocks.DataVis]: 3,
[SuperBlocks.RelationalDb]: 4,
[SuperBlocks.BackEndDevApis]: 5,
[SuperBlocks.QualityAssurance]: 6,
[SuperBlocks.SciCompPy]: 7,
[SuperBlocks.DataAnalysisPy]: 8,
[SuperBlocks.RespWebDesign]: 9,
[SuperBlocks.InfoSec]: 10,
[SuperBlocks.MachineLearningPy]: 11,
[SuperBlocks.CollegeAlgebraPy]: 12,
[SuperBlocks.CodingInterviewPrep]: 13,
[SuperBlocks.ProjectEuler]: 14
};
const chineseTest = {
[SuperBlocks.RespWebDesignNew]: 0,
[SuperBlocks.JsAlgoDataStruct]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
[SuperBlocks.DataVis]: 3,
[SuperBlocks.RelationalDb]: 4,
[SuperBlocks.BackEndDevApis]: 5,
[SuperBlocks.QualityAssurance]: 6,
[SuperBlocks.SciCompPy]: 7,
[SuperBlocks.DataAnalysisPy]: 8,
[SuperBlocks.InfoSec]: 9,
[SuperBlocks.MachineLearningPy]: 10,
[SuperBlocks.RespWebDesign]: 11,
[SuperBlocks.CollegeAlgebraPy]: 12,
[SuperBlocks.CodingInterviewPrep]: 13,
[SuperBlocks.ProjectEuler]: 14
[SuperBlocks.RespWebDesign]: 14,
[SuperBlocks.JsAlgoDataStructNew]: 15,
[SuperBlocks.TheOdinProject]: 16,
[SuperBlocks.ExampleCertification]: 17
};
describe('createSuperOrder', () => {
const englishSuperOrder = createSuperOrder({
language: 'english',
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
const superOrder = createSuperOrder(mockSuperBlocks);
it('should create the correct object given an array of SuperBlocks', () => {
expect(superOrder).toStrictEqual(fullSuperOrder);
});
const upcomingSuperOrder = createSuperOrder({
language: 'english',
showNewCurriculum: 'false',
showUpcomingChanges: 'true'
});
const espanolSuperOrder = createSuperOrder({
language: 'espanol',
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
const chineseSuperOrder = createSuperOrder({
language: 'chinese',
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
it("should create the correct object for 'english'", () => {
expect(englishSuperOrder).toStrictEqual(englishTest);
});
it('should create the correct object with upcoming changes shown', () => {
expect(upcomingSuperOrder).toStrictEqual(upcomingTest);
});
it("should create the correct object for 'espanol'", () => {
expect(espanolSuperOrder).toStrictEqual(espanolTest);
});
it("should create the correct object for 'chinese'", () => {
expect(chineseSuperOrder).toStrictEqual(chineseTest);
it('throws when not given an array of SuperBlocks', () => {
expect.assertions(4);
expect(() => getSuperOrder()).toThrow();
expect(() => getSuperOrder(null)).toThrow();
expect(() => getSuperOrder('')).toThrow();
expect(() => getSuperOrder(['respansive-wib-desoin'])).toThrow();
});
});
@@ -146,12 +87,14 @@ describe('getSuperOrder', () => {
});
it('returns unique numbers for all current superblocks', () => {
// Skip non-english tests
if (process.env.CURRICULUM_LOCALE !== 'english') {
return;
}
if (process.env.SHOW_UPCOMING_CHANGES !== 'true') {
if (
process.env.SHOW_NEW_CURRICULUM !== 'true' &&
process.env.SHOW_UPCOMING_CHANGES !== 'true'
) {
expect.assertions(15);
} else if (process.env.SHOW_NEW_CURRICULUM !== 'true') {
expect.assertions(15);
} else if (process.env.SHOW_UPCOMING_CHANGES !== 'true') {
expect.assertions(15);
} else {
expect.assertions(18);
@@ -171,14 +114,21 @@ describe('getSuperOrder', () => {
expect(getSuperOrder(SuperBlocks.CollegeAlgebraPy)).toBe(11);
expect(getSuperOrder(SuperBlocks.CodingInterviewPrep)).toBe(12);
expect(getSuperOrder(SuperBlocks.ProjectEuler)).toBe(13);
expect(getSuperOrder(SuperBlocks.RespWebDesign)).toBe(14);
if (process.env.SHOW_UPCOMING_CHANGES === 'true') {
expect(getSuperOrder(SuperBlocks.JsAlgoDataStructNew)).toBe(14);
expect(getSuperOrder(SuperBlocks.TheOdinProject)).toBe(15);
expect(getSuperOrder(SuperBlocks.ExampleCertification)).toBe(16);
expect(getSuperOrder(SuperBlocks.RespWebDesign)).toBe(17);
} else {
expect(getSuperOrder(SuperBlocks.RespWebDesign)).toBe(14);
if (
process.env.SHOW_NEW_CURRICULUM === 'true' &&
process.env.SHOW_UPCOMING_CHANGES === 'true'
) {
expect(getSuperOrder(SuperBlocks.JsAlgoDataStructNew)).toBe(15);
expect(getSuperOrder(SuperBlocks.TheOdinProject)).toBe(16);
expect(getSuperOrder(SuperBlocks.ExampleCertification)).toBe(17);
} else if (process.env.SHOW_NEW_CURRICULUM === 'true') {
return;
} else if (process.env.SHOW_UPCOMING_CHANGES === 'true') {
expect(getSuperOrder(SuperBlocks.JsAlgoDataStructNew)).toBe(15);
expect(getSuperOrder(SuperBlocks.TheOdinProject)).toBe(16);
expect(getSuperOrder(SuperBlocks.ExampleCertification)).toBe(17);
}
});
});

View File

@@ -2,6 +2,7 @@ const landingPageElements = {
heading: "[data-test-label='landing-header']",
callToAction: "[data-test-label='landing-big-cta']",
certifications: "[data-test-label='certifications']",
curriculumBtns: "[data-test-label='curriculum-map-button']",
testimonials: "[data-test-label='testimonial-cards']",
landingPageImage: "[data-test-label='landing-page-figure']",
faq: "[data-test-label='landing-page-faq']"
@@ -11,7 +12,7 @@ type LandingPageTypes<T> = T[keyof T];
type LandingPageLogs = LandingPageTypes<typeof landingPageElements>;
const certifications = [
const superBlocks = [
'Responsive Web Design',
'JavaScript Algorithms and Data Structures',
'Front End Development Libraries',
@@ -23,7 +24,10 @@ const certifications = [
'Data Analysis with Python',
'Information Security',
'Machine Learning with Python',
'College Algebra with Python'
'College Algebra with Python',
'Coding Interview Prep',
'Project Euler',
'Legacy Responsive Web Design'
];
describe('Landing page', () => {
@@ -67,13 +71,10 @@ describe('Landing page', () => {
.should('not.exist');
});
it('Has links to all the certifications', function () {
cy.get(landingPageElements.certifications)
.children()
.its('length')
.should('eq', 12);
cy.wrap(certifications).each((cert: LandingPageLogs) => {
cy.get(landingPageElements.certifications).contains(cert);
it('Has links to all superblocks', function () {
cy.get(landingPageElements.curriculumBtns).its('length').should('eq', 15);
cy.wrap(superBlocks).each((cert: LandingPageLogs) => {
cy.get(landingPageElements.curriculumBtns).contains(cert);
});
});

View File

@@ -1,4 +1,4 @@
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
interface Meta {
challengeOrder: string[][];

View File

@@ -1,5 +1,5 @@
const challengerSelector = {
challengeMap: "[data-test-label='learn-curriculum-map']"
curriculumMap: "[data-test-label='curriculum-map']"
} as const;
const learnUrl = {
@@ -43,7 +43,7 @@ describe('Learn Landing page (not logged in)', () => {
it('Should render a curriculum map', () => {
cy.document().then(document => {
const superBlocks = document.querySelectorAll<HTMLAnchorElement>(
`${challengerSelector.challengeMap} > li > a`
`${challengerSelector.curriculumMap} > ul > li > a`
);
expect(superBlocks).to.have.length(15);

View File

@@ -1,4 +1,4 @@
import { SuperBlocks } from '../../../../../config/certification-settings';
import { SuperBlocks } from '../../../../../config/superblocks';
const projects = {
superBlock: SuperBlocks.FrontEndDevLibs,

View File

@@ -120,69 +120,32 @@ export const rtlLangs = [''];
> [!NOTE]
> When a language has been set up in the deployment pipeline AND has a public `/news` instance live, it can be removed from the `hiddenLangs` array and be made available to the public.
### Configure the Language Superblock Order
### Set Translated SuperBlocks
In the [config/superblock-order.ts](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/config/superblock-order.ts) file, you need to set the order and state of all the superblocks for the new language in the `superBlockOrder` object. Copy one of the language keys and all its values, paste it to the bottom of the object (or wherever), and change the key to your new language from the `Languages` enum.
In the [config/superblocks.ts](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/config/superblocks.ts) file, add the new language to the `notAuditedSuperBlocks` object. This lists all the superblocks which are not fully translated. Add an array of superblocks which have not been fully translated to it:
```js
export const superBlockOrder: SuperBlockOrder = {
export const notAuditedSuperBlocks: NotAuditedSuperBlocks = {
...
[Languages.Dothraki]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: []
}
}
}
[Languages.Dothraki]: [
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CollegeAlgebraPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.ProjectEuler,
SuperBlocks.JsAlgoDataStructNew,
SuperBlocks.TheOdinProject
]
}
```
The order of the superblocks in this object is how they appear on the "Landing" page and "Learn" maps. Follow the comments in that file so you know how you are allowed to order the superblocks, then move them to their proper places for the new language.
> [!ATTENTION]
> Do not change the order of any of the keys in the object, just move the superblocks to the different arrays
The `CurriculumMaps.Landing` array should contain exactly one superblock for all our current certifications, and the `CurriculumMaps.Learn` object should have all existing superblocks in it. Translated superblocks go in `TranslationStates.Audited` and non-translated superblocks go in `TranslationStates.NotAudited`. Each of those two objects has four different states a superblock can be in.
- `SuperBlockStates.Current`: Means that the superblock is current, `Responsive Web Design` for example.
- `SuperBlockStates.New`: These only show up when `SHOW_NEW_CURRICULUM` is set to `true` in your `.env` file. It is for displaying new superblocks on a specific build. For example, when we released the new RWD, we only showed it on English to start.
- `SuperBlockStates.Upcoming`: These only show up when `SHOW_UPCOMING_CHANGES` is set to `true` in your `.env` file. It is to show superblocks locally while they are in development. Or, if you just need to hide a superblock from the map for some other reason.
- `SuperBlockStates.Legacy`: A superblock is moved here when a newer version of that superblock has been fully translated and replaced it.
Be sure to only add the superblocks which are **not** fully translated and approved. The translated superblocks will be calculated from this object. When a new superblock is finished being fully translated, remove it from the array for that language.
### Configure Search

View File

@@ -389,4 +389,4 @@ The user is now a proofreader.
## How to Add or Update a Language
Check out the [how to enable new language](how-to-enable-new-languages.md) docs. If you are updating a language the section on [configuring the language superblock order](how-to-enable-new-languages.md#configure-the-language-superblock-order) should be helpful.
Check out the [how to enable new language](how-to-enable-new-languages.md) docs. If you are updating a language the section on [set translated superblocks](how-to-enable-new-languages.md#set-translated-superblocks) should be helpful.

View File

@@ -9,8 +9,7 @@ config({ path: envPath });
import { availableLangs } from '../../config/i18n';
import { getChallengesForLang } from '../../curriculum/get-challenges';
import { SuperBlocks } from '../../config/certification-settings';
import { getAuditedSuperBlocks } from '../../config/superblock-order';
import { SuperBlocks, getAuditedSuperBlocks } from '../../config/superblocks';
// TODO: re-organise the types to a common 'types' folder that can be shared
// between the workspaces so we don't have to declare ChallengeNode here and in

View File

@@ -5,7 +5,7 @@ import { prompt } from 'inquirer';
import { format } from 'prettier';
import ObjectID from 'bson-objectid';
import { SuperBlocks } from '../../config/certification-settings';
import { SuperBlocks } from '../../config/superblocks';
import { createStepFile } from './utils';
import { getSuperBlockSubPath } from './fs-utils';
import { Meta } from './helpers/project-metadata';

View File

@@ -1,6 +1,6 @@
import fs from 'fs/promises';
import path from 'path';
import { SuperBlocks } from '../../config/certification-settings';
import { SuperBlocks } from '../../config/superblocks';
import { getSuperBlockSubPath } from './fs-utils';
describe('getSuperBlockSubPath', () => {

View File

@@ -1,4 +1,4 @@
import { SuperBlocks } from '../../config/certification-settings';
import { SuperBlocks } from '../../config/superblocks';
export function getSuperBlockSubPath(superBlock: SuperBlocks): string {
const pathMap = {

View File

@@ -5,7 +5,7 @@ import readdirp from 'readdirp';
// TODO: remove chai and use jest's assertion errors
import { AssertionError } from 'chai';
import envData from '../../../config/env.json';
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
import {
superblockSchemaValidator,
availableSuperBlocksValidator

View File

@@ -2,7 +2,7 @@ import { mkdirSync, writeFileSync, readFileSync } from 'fs';
import { resolve, dirname } from 'path';
import { submitTypes } from '../../../client/utils/challenge-types';
import { type ChallengeNode } from '../../../client/src/redux/prop-types';
import { SuperBlocks } from '../../../config/certification-settings';
import { SuperBlocks } from '../../../config/superblocks';
type Intro = { [keyValue in SuperBlocks]: IntroProps };
export type Curriculum<T> = {

View File

@@ -1,15 +1,18 @@
const { getAuditedSuperBlocks } = require('../config/superblock-order');
const { Languages } = require('../config/i18n');
const { getAuditedSuperBlocks } = require('../config/superblocks');
const {
showNewCurriculum,
showUpcomingChanges
} = require('../config/env.json');
function isAuditedCert(language, superblock) {
if (!language || !superblock)
throw Error('Both arguments must be provided for auditing');
if (language === Languages.English) {
return true;
}
const auditedSuperBlocks = getAuditedSuperBlocks({ language });
const auditedSuperBlocks = getAuditedSuperBlocks({
showNewCurriculum: showNewCurriculum.toString(),
showUpcomingChanges: showUpcomingChanges.toString(),
language
});
return auditedSuperBlocks.includes(superblock);
}