feat(client): move legacy rwd button to bottom of map (#47596)

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Closes https://github.com/freeCodeCamp/freeCodeCamp/issues/46523
This commit is contained in:
Tom
2022-11-08 09:30:46 -06:00
committed by GitHub
parent e9240196e8
commit 5f73773cd4
20 changed files with 1236 additions and 1074 deletions
+1
View File
@@ -5,4 +5,5 @@ api-server/src/public/**
api-server/lib/**
config/i18n.js
config/certification-settings.js
config/superblock-order.js
web/**
+2
View File
@@ -164,6 +164,8 @@ config/client/test-evaluator.json
config/curriculum.json
config/i18n.js
config/certification-settings.js
config/superblock-order.js
config/superblock-order.test.js
### Generated utils files ###
utils/block-nameify.js
+2
View File
@@ -7,6 +7,8 @@ curriculum/challenges/**/*
config/**/*.json
config/i18n.js
config/certification-settings.js
config/superblock-order.js
config/superblock-order.test.js
docs/i18n
utils/block-nameify.js
utils/block-nameify.test.js
@@ -480,7 +480,7 @@ export class NavLinks extends Component<NavLinksProps, NavlinkStates> {
className='nav-link nav-lang-menu-option'
data-value={lang}
{...(LangCodes[lang] && {
lang: LangCodes[lang] as string
lang: LangCodes[lang]
})}
onClick={this.handleLanguageChange}
onKeyDown={this.handleLanguageMenuKeyDown}
@@ -1,535 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Map /> snapshot: Map 1`] = `
<div>
<div
class="map-ui"
data-test-label="learn-curriculum-map"
>
<ul
data-test-label="certifications"
>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-one/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-four/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-one/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-one/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-one/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-one/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-two/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-two/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-two/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-two/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-three/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-three/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
<li>
<a
class="btn link-btn btn-lg"
href="/learn/super-block-three/"
>
<div
style="display: flex; justify-content: space-between; align-items: center; gap: 15px;"
>
<svg
aria-hidden="true"
class="map-icon"
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M112 48h352v48h48V32a32.09 32.09 0 00-32-32H96a32.09 32.09 0 00-32 32v256H16a16 16 0 00-16 16v16a64.14 64.14 0 0063.91 64H352v-96H112zm492 80H420a36 36 0 00-36 36v312a36 36 0 0036 36h184a36 36 0 0036-36V164a36 36 0 00-36-36zm-12 336H432V176h160z"
/>
</svg>
</div>
<svg
aria-hidden="true"
fill="inherit"
height="20px"
version="1.1"
viewBox="0 0 16 20"
width="18px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<g>
<polygon
points="-2.68014473e-15 -1.06357708e-13 2.01917516 -1.06357708e-13 8.99824941 9.00746464 2.01917516 18.0149293 -2.66453526e-15 18.0149293 7.00955027 9"
/>
<polygon
points="7.99971435 -1.06357708e-13 10.0188895 -1.06357708e-13 16.9979638 9.00746464 10.0188895 18.0149293 7.99971435 18.0149293 15.0092646 9"
/>
</g>
</svg>
</a>
</li>
</ul>
</div>
</div>
`;
+81 -132
View File
@@ -1,39 +1,29 @@
import { graphql, useStaticQuery } from 'gatsby';
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';
import { isAuditedCert } from '../../../../utils/is-audited';
import { generateIconComponent } from '../../assets/icons';
import LinkButton from '../../assets/icons/link-button';
import { ChallengeNode } from '../../redux/prop-types';
import { Link, Spacer } from '../helpers';
import { getSuperBlockTitleForMap } from '../../utils/superblock-map-titles';
import './map.css';
const { curriculumLocale } = envData;
const { curriculumLocale, showNewCurriculum, showUpcomingChanges } = envData;
interface MapProps {
currentSuperBlock?: SuperBlocks | null;
forLanding?: boolean;
}
interface MapData {
allChallengeNode: {
nodes: ChallengeNode[];
};
}
function createSuperBlockTitle(superBlock: SuperBlocks) {
const superBlockTitle = i18next.t(`intro:${superBlock}.title`);
return superBlock === 'coding-interview-prep'
? superBlockTitle
: i18next.t('learn.cert-map-estimates.certs', {
title: superBlockTitle
});
}
const linkSpacingStyle = {
display: 'flex',
justifyContent: 'space-between',
@@ -41,102 +31,91 @@ const linkSpacingStyle = {
gap: '15px'
};
function renderLandingMap(nodes: ChallengeNode[]) {
nodes = nodes.filter(
({ challenge }) => challenge.superBlock !== SuperBlocks.CodingInterviewPrep
function MapLi({
superBlock,
landing = false
}: {
superBlock: SuperBlocks;
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'>
{nodes.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(challenge.superBlock, 'map-icon')}
{i18next.t(`intro:${challenge.superBlock}.title`)}
</div>
<LinkButton />
</Link>
</li>
{landingSuperOrder.map((superBlock, i) => (
<MapLi superBlock={superBlock} key={i} landing={true} />
))}
</ul>
);
}
function renderLearnMap(
nodes: ChallengeNode[],
currentSuperBlock: MapProps['currentSuperBlock']
) {
nodes = nodes.filter(
({ challenge }) => challenge.superBlock !== currentSuperBlock
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
);
return curriculumLocale === 'english' ? (
const notAuditedSuperBlocks = tempNotAuditedSuperBlocks.filter(
superBlock => superBlock !== currentSuperBlock
);
return (
<ul data-test-label='learn-curriculum-map'>
{nodes.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(challenge.superBlock, 'map-icon')}
{createSuperBlockTitle(challenge.superBlock)}
</div>
</Link>
</li>
{/* audited superblocks */}
{auditedSuperBlocks.map((superBlock, i) => (
<MapLi key={i} superBlock={superBlock} landing={false} />
))}
</ul>
) : (
<ul data-test-label='learn-curriculum-map'>
{nodes
.filter(({ challenge }) =>
isAuditedCert(curriculumLocale, challenge.superBlock)
)
.map(({ challenge }, i) => (
<li key={i}>
{/* has not audited superblocks */}
{notAuditedSuperBlocks.length > 0 && (
<>
{' '}
<hr />
<div style={{ textAlign: 'center' }}>
<p style={{ marginBottom: 0 }}>
{i18next.t('learn.help-translate')}{' '}
</p>
<Link
className='btn link-btn btn-lg'
to={`/learn/${challenge.superBlock}/`}
external={true}
sameTab={false}
to={i18next.t('links:help-translate-link-url')}
>
<div style={linkSpacingStyle}>
{generateIconComponent(challenge.superBlock, 'map-icon')}
{createSuperBlockTitle(challenge.superBlock)}
</div>
{i18next.t('learn.help-translate-link')}
</Link>
</li>
))}
<hr />
<div style={{ textAlign: 'center' }}>
<p style={{ marginBottom: 0 }}>{i18next.t('learn.help-translate')} </p>
<Link
external={true}
sameTab={false}
to={i18next.t('links:help-translate-link-url')}
>
{i18next.t('learn.help-translate-link')}
</Link>
<Spacer />
</div>
{nodes
.filter(
({ challenge }) =>
!isAuditedCert(curriculumLocale, challenge.superBlock)
)
.map(({ challenge }, i) => (
<li key={i}>
<Link
className='btn link-btn btn-lg'
to={`/learn/${challenge.superBlock}/`}
>
<div style={linkSpacingStyle}>
{generateIconComponent(challenge.superBlock, 'map-icon')}
{createSuperBlockTitle(challenge.superBlock)}
</div>
</Link>
</li>
))}
<Spacer />
</div>
</>
)}
{/* not audited superblocks */}
{notAuditedSuperBlocks.map((superBlock, i) => (
<MapLi key={i} superBlock={superBlock} landing={false} />
))}
</ul>
);
}
@@ -145,39 +124,9 @@ export function Map({
forLanding = false,
currentSuperBlock = null
}: MapProps): React.ReactElement {
/*
* this query gets the first challenge from each block and the first block
* from each superblock, leaving you with one challenge from each
* superblock
*/
const data: MapData = useStaticQuery(graphql`
query SuperBlockNodes {
allChallengeNode(
sort: { fields: [challenge___superOrder] }
filter: { challenge: { order: { eq: 0 }, challengeOrder: { eq: 0 } } }
) {
nodes {
challenge {
superBlock
dashedName
}
}
}
}
`);
const nodes = data.allChallengeNode.nodes;
const temp = [
nodes[0],
nodes[12],
...nodes.filter((_, i) => i !== 0 && i !== 12)
];
return (
<div className='map-ui' data-test-label='learn-curriculum-map'>
{forLanding
? renderLandingMap(temp)
: renderLearnMap(temp, currentSuperBlock)}
{forLanding ? renderLandingMap() : renderLearnMap(currentSuperBlock)}
</div>
);
}
-27
View File
@@ -1,27 +0,0 @@
import { render } from '@testing-library/react';
import { useStaticQuery } from 'gatsby';
import React from 'react';
import mockChallengeNodes from '../../__mocks__/challenge-nodes';
import { Map } from '.';
beforeEach(() => {
(useStaticQuery as jest.Mock).mockImplementationOnce(() => ({
allChallengeNode: {
nodes: mockChallengeNodes
}
}));
});
// set .scrollTo to avoid errors in default test environment
window.scrollTo = jest.fn();
test('<Map /> snapshot', () => {
const { container } = render(<Map {...props} />);
expect(container).toMatchSnapshot('Map');
});
const props = {
forLanding: true
};
@@ -11,6 +11,7 @@ import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { SuperBlocks } from '../../../../config/certification-settings';
import { getSuperBlockTitleForMap } from '../../utils/superblock-map-titles';
import DonateModal from '../../components/Donation/donation-modal';
import Login from '../../components/Header/components/Login';
import Map from '../../components/Map';
@@ -177,14 +178,7 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
nodesForSuperBlock.map(({ challenge: { block } }) => block)
);
const i18nSuperBlock = t(`intro:${superBlock}.title`);
const i18nTitle =
superBlock === SuperBlocks.CodingInterviewPrep
? i18nSuperBlock
: t(`intro:misc-text.certification`, {
cert: i18nSuperBlock
});
const i18nTitle = getSuperBlockTitleForMap(superBlock);
const defaultCurriculumNames = blockDashedNames;
return (
+22
View File
@@ -0,0 +1,22 @@
import i18next from 'i18next';
import { SuperBlocks } from '../../../config/certification-settings';
// these are keys from i18n translations.json files
enum SuperBlockI18nKeys {
Certification = 'learn.cert-map-estimates.certs'
}
// the key above is used to create the last word for superBlock titles used on
// the map and window. e.g. 'Certification' in Responsive Web Design
// Certification
const superBlocksWithoutLastWord = [SuperBlocks.CodingInterviewPrep];
export function getSuperBlockTitleForMap(superBlock: SuperBlocks) {
const i18nSuperBlock = i18next.t(`intro:${superBlock}.title`);
return superBlocksWithoutLastWord.includes(superBlock)
? i18nSuperBlock
: i18next.t([SuperBlockI18nKeys.Certification], {
title: i18nSuperBlock
});
}
+67 -188
View File
@@ -1,5 +1,16 @@
// ---------------------------------------------------------------------------
import { SuperBlocks } from './certification-settings';
export enum Languages {
English = 'english',
Espanol = 'espanol',
Chinese = 'chinese',
ChineseTrandational = 'chinese-traditional',
Italian = 'italian',
Portuguese = 'portuguese',
Ukrainian = 'ukrainian',
Japanese = 'japanese',
German = 'german',
Arabic = 'arabic'
}
/*
* List of languages with localizations enabled for builds.
*
@@ -10,160 +21,31 @@ import { SuperBlocks } from './certification-settings';
*/
export const availableLangs = {
client: [
'english',
'espanol',
'chinese',
'chinese-traditional',
'italian',
'portuguese',
'ukrainian',
'japanese',
'german',
'arabic'
Languages.English,
Languages.Espanol,
Languages.Chinese,
Languages.ChineseTrandational,
Languages.Italian,
Languages.Portuguese,
Languages.Ukrainian,
Languages.Japanese,
Languages.German,
Languages.Arabic
],
curriculum: [
'english',
'espanol',
'chinese',
'chinese-traditional',
'italian',
'portuguese',
'ukrainian',
'japanese',
'german',
'arabic'
Languages.English,
Languages.Espanol,
Languages.Chinese,
Languages.ChineseTrandational,
Languages.Italian,
Languages.Portuguese,
Languages.Ukrainian,
Languages.Japanese,
Languages.German,
Languages.Arabic
]
};
/*
* List of certifications with localization enabled in their world language.
*
* These certifications have been approved 100% on Crowdin at least during
* their launch, and hence meet the QA standard to be published live. Other
* certifications which have not been audited & approved will fallback to
* English equivalent.
*/
export const auditedCerts = {
espanol: [
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy
],
chinese: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy
],
'chinese-traditional': [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy
],
italian: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep
],
portuguese: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.RelationalDb
],
ukrainian: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.RelationalDb
],
japanese: [
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.RelationalDb
],
german: [
SuperBlocks.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs
],
arabic: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs
]
};
/**
* This contains the list of languages which have a beta->stable release
* that has been 100% translated. This will only be used during the window
* where a beta goes to stable but the translation isn't complete yet.
*/
export const languagesWithAuditedBetaReleases = [
'english',
'portuguese',
'italian',
'ukrainian',
'chinese',
'chinese-traditional',
'arabic'
];
// ---------------------------------------------------------------------------
// Each client language needs an entry in the rest of the variables below
@@ -173,51 +55,48 @@ export const languagesWithAuditedBetaReleases = [
* Use a 639-1 code here https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
*/
export const i18nextCodes = {
english: 'en',
espanol: 'es',
chinese: 'zh',
'chinese-traditional': 'zh-Hant',
italian: 'it',
portuguese: 'pt-BR',
ukrainian: 'uk',
japanese: 'ja',
german: 'de',
arabic: 'ar'
[Languages.English]: 'en',
[Languages.Espanol]: 'es',
[Languages.Chinese]: 'zh',
[Languages.ChineseTrandational]: 'zh-Hant',
[Languages.Italian]: 'it',
[Languages.Portuguese]: 'pt-BR',
[Languages.Ukrainian]: 'uk',
[Languages.Japanese]: 'ja',
[Languages.German]: 'de',
[Languages.Arabic]: 'ar'
};
/* eslint-disable @typescript-eslint/naming-convention */
// These are for the language selector dropdown menu in the footer
export enum LangNames {
english = 'English',
espanol = 'Español',
chinese = '中文(简体字)',
'chinese-traditional' = '中文(繁體字)',
italian = 'Italiano',
portuguese = 'Português',
ukrainian = 'Українська',
japanese = '日本語',
german = 'Deutsch',
arabic = 'العربية'
}
export const LangNames = {
[Languages.English]: 'English',
[Languages.Espanol]: 'Español',
[Languages.Chinese]: '中文(简体字)',
[Languages.ChineseTrandational]: '中文(繁體字)',
[Languages.Italian]: 'Italiano',
[Languages.Portuguese]: 'Português',
[Languages.Ukrainian]: 'Українська',
[Languages.Japanese]: '日本語',
[Languages.German]: 'Deutsch',
[Languages.Arabic]: 'العربية'
};
/* These are for formatting dates and numbers. Used with JS .toLocaleString().
* There's an example in profile/components/Camper.js
* List: https://github.com/unicode-cldr/cldr-dates-modern/tree/master/main
*/
export enum LangCodes {
english = 'en-US',
espanol = 'es-419',
chinese = 'zh',
'chinese-traditional' = 'zh-Hant',
italian = 'it',
portuguese = 'pt-BR',
ukrainian = 'uk',
japanese = 'ja',
german = 'de',
arabic = 'ar'
}
/* eslint-enable @typescript-eslint/naming-convention */
export const LangCodes = {
[Languages.English]: 'en-US',
[Languages.Espanol]: 'es-419',
[Languages.Chinese]: 'zh',
[Languages.ChineseTrandational]: 'zh-Hant',
[Languages.Italian]: 'it',
[Languages.Portuguese]: 'pt-BR',
[Languages.Ukrainian]: 'uk',
[Languages.Japanese]: 'ja',
[Languages.German]: 'de',
[Languages.Arabic]: 'ar'
};
/**
* This array contains languages that should NOT appear in the language selector.
+196
View File
@@ -0,0 +1,196 @@
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.CodingInterviewPrep,
SuperBlocks.JsAlgoDataStructNew,
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'
});
const test = [
SuperBlocks.RespWebDesignNew,
SuperBlocks.DataVis,
SuperBlocks.RelationalDb,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep,
SuperBlocks.JsAlgoDataStructNew
];
expect(notAuditedSuperBlocks).toStrictEqual(test);
expect(notAuditedSuperBlocks.length).toEqual(test.length);
});
});
+626
View File
@@ -0,0 +1,626 @@
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 = 11;
/*
* 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.CodingInterviewPrep
];
/*
* 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
],
[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.Espanol]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesign,
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.RespWebDesign,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
SuperBlocks.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: []
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.RelationalDb,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy,
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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
],
[CurriculumMaps.Learn]: {
[TranslationStates.Audited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RespWebDesignNew,
SuperBlocks.JsAlgoDataStruct,
SuperBlocks.FrontEndDevLibs,
SuperBlocks.DataVis,
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.RelationalDb,
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.ChineseTrandational]: {
[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.BackEndDevApis,
SuperBlocks.QualityAssurance,
SuperBlocks.SciCompPy,
SuperBlocks.DataAnalysisPy,
SuperBlocks.InfoSec,
SuperBlocks.MachineLearningPy
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [
SuperBlocks.RelationalDb,
SuperBlocks.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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
],
[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]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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
],
[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]: [],
[SuperBlockStates.Legacy]: [SuperBlocks.RespWebDesign]
},
[TranslationStates.NotAudited]: {
[SuperBlockStates.Current]: [],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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
],
[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.CodingInterviewPrep],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[SuperBlockStates.Legacy]: []
}
}
},
[Languages.Japanese]: {
[CurriculumMaps.Landing]: [
SuperBlocks.RespWebDesign,
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.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.RespWebDesignNew],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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
],
[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.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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
],
[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.CodingInterviewPrep
],
[SuperBlockStates.New]: [],
[SuperBlockStates.Upcoming]: [SuperBlocks.JsAlgoDataStructNew],
[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;
}
export function getLearnSuperBlocks({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}) {
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'
}) {
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'
}) {
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;
}
+76 -58
View File
@@ -1,12 +1,18 @@
const path = require('path');
const {
CurriculumMaps,
superBlockOrder,
SuperBlockStates,
TranslationStates,
orderedSuperBlockStates
} = require('../config/superblock-order');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const {
availableLangs,
languagesWithAuditedBetaReleases
} = require('../config/i18n');
const { availableLangs } = require('../config/i18n');
const curriculumLangs = availableLangs.curriculum;
// checks that the CURRICULUM_LOCALE exists and is an available language
exports.testedLang = function testedLang() {
if (process.env.CURRICULUM_LOCALE) {
if (curriculumLangs.includes(process.env.CURRICULUM_LOCALE)) {
@@ -20,67 +26,78 @@ exports.testedLang = function testedLang() {
}
};
// TODO: migrate to TS and use the SuperBlocks enum from
// config/certification-settings.ts
const superBlockToOrder = {
'2022/responsive-web-design': 0,
'javascript-algorithms-and-data-structures': 1,
'front-end-development-libraries': 2,
'data-visualization': 3,
'relational-database': 4,
'back-end-development-and-apis': 5,
'quality-assurance': 6,
'scientific-computing-with-python': 7,
'data-analysis-with-python': 8,
'information-security': 9,
'machine-learning-with-python': 10,
'coding-interview-prep': 11,
'responsive-web-design': 12
};
/**
* This order is used for i18n instances where a new certification is released
* from beta but is not audited, so cannot be reordered (due to the way we
* split the map)
/*
* 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
*/
const superBlockNonAuditedOrder = {
'responsive-web-design': 0,
'javascript-algorithms-and-data-structures': 1,
'front-end-development-libraries': 2,
'data-visualization': 3,
'relational-database': 4,
'back-end-development-and-apis': 5,
'quality-assurance': 6,
'scientific-computing-with-python': 7,
'data-analysis-with-python': 8,
'information-security': 9,
'machine-learning-with-python': 10,
'coding-interview-prep': 11,
'2022/responsive-web-design': 12
};
const superBlockToNewOrder = {
...superBlockToOrder,
'2022/javascript-algorithms-and-data-structures': 13
};
function getSuperOrder(
superblock,
{ showNewCurriculum } = { showNewCurriculum: false }
) {
let orderMap = superBlockToOrder;
if (showNewCurriculum) {
orderMap = superBlockToNewOrder;
function createSuperOrder({
language = 'english',
showNewCurriculum = 'false',
showUpcomingChanges = 'false'
}) {
if (!Object.prototype.hasOwnProperty.call(superBlockOrder, language)) {
throw Error(`${language} not found in superblock-order.ts`);
}
if (
!languagesWithAuditedBetaReleases.includes(process.env.CURRICULUM_LOCALE)
!Object.prototype.hasOwnProperty.call(superBlockOrder[language], [
CurriculumMaps.Learn
])
) {
orderMap = superBlockNonAuditedOrder;
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
];
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);
return superOrder;
}
const superOrder = createSuperOrder({
language: process.env.CURRICULUM_LOCALE,
showNewCurriculum: process.env.SHOW_NEW_CURRICULUM,
showUpcomingChanges: process.env.SHOW_UPCOMING_CHANGES
});
// gets the superOrder of a superBlock from the object created above
function getSuperOrder(superblock) {
if (typeof superblock !== 'string')
throw Error('superblock must be a string');
const order = orderMap[superblock];
const order = superOrder[superblock];
if (typeof order === 'undefined')
throw Error(`${superblock} is not a valid superblock`);
return order;
@@ -112,5 +129,6 @@ function getSuperBlockFromDir(dir) {
return directoryToSuperblock[dir];
}
exports.createSuperOrder = createSuperOrder;
exports.getSuperOrder = getSuperOrder;
exports.getSuperBlockFromDir = getSuperBlockFromDir;
+136 -101
View File
@@ -5,11 +5,117 @@ import fs from 'fs';
import path from 'path';
import { config } from 'dotenv';
import { SuperBlocks } from '../config/certification-settings';
import { languagesWithAuditedBetaReleases } from '../config/i18n';
import { getSuperOrder, getSuperBlockFromDir } from './utils';
import { createSuperOrder, getSuperOrder, getSuperBlockFromDir } from './utils';
config({ path: path.resolve(__dirname, '../.env') });
const englishTest = {
[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.CodingInterviewPrep]: 11,
[SuperBlocks.RespWebDesign]: 12
};
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.CodingInterviewPrep]: 11,
[SuperBlocks.JsAlgoDataStructNew]: 12,
[SuperBlocks.RespWebDesign]: 13
};
const espanolTest = {
[SuperBlocks.RespWebDesign]: 0,
[SuperBlocks.JsAlgoDataStruct]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
[SuperBlocks.DataVis]: 3,
[SuperBlocks.BackEndDevApis]: 4,
[SuperBlocks.QualityAssurance]: 5,
[SuperBlocks.SciCompPy]: 6,
[SuperBlocks.DataAnalysisPy]: 7,
[SuperBlocks.RespWebDesignNew]: 8,
[SuperBlocks.RelationalDb]: 9,
[SuperBlocks.InfoSec]: 10,
[SuperBlocks.MachineLearningPy]: 11,
[SuperBlocks.CodingInterviewPrep]: 12
};
const chineseTest = {
[SuperBlocks.RespWebDesignNew]: 0,
[SuperBlocks.JsAlgoDataStruct]: 1,
[SuperBlocks.FrontEndDevLibs]: 2,
[SuperBlocks.DataVis]: 3,
[SuperBlocks.BackEndDevApis]: 4,
[SuperBlocks.QualityAssurance]: 5,
[SuperBlocks.SciCompPy]: 6,
[SuperBlocks.DataAnalysisPy]: 7,
[SuperBlocks.InfoSec]: 8,
[SuperBlocks.MachineLearningPy]: 9,
[SuperBlocks.RespWebDesign]: 10,
[SuperBlocks.RelationalDb]: 11,
[SuperBlocks.CodingInterviewPrep]: 12
};
describe('createSuperOrder', () => {
const englishSuperOrder = createSuperOrder({
language: 'english',
showNewCurriculum: 'false',
showUpcomingChanges: 'false'
});
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);
});
});
describe('getSuperOrder', () => {
it('returns a number for valid superblocks', () => {
expect.assertions(1);
@@ -29,108 +135,37 @@ describe('getSuperOrder', () => {
expect(() => getSuperOrder('certifications')).toThrow();
});
if (
languagesWithAuditedBetaReleases.includes(
process.env.CURRICULUM_LOCALE as string
)
) {
it('returns unique numbers for all current superblocks (audited beta)', () => {
expect.assertions(13);
expect(getSuperOrder('2022/responsive-web-design')).toBe(0);
expect(getSuperOrder('javascript-algorithms-and-data-structures')).toBe(
1
);
expect(getSuperOrder('front-end-development-libraries')).toBe(2);
expect(getSuperOrder('data-visualization')).toBe(3);
expect(getSuperOrder('relational-database')).toBe(4);
expect(getSuperOrder('back-end-development-and-apis')).toBe(5);
expect(getSuperOrder('quality-assurance')).toBe(6);
expect(getSuperOrder('scientific-computing-with-python')).toBe(7);
expect(getSuperOrder('data-analysis-with-python')).toBe(8);
expect(getSuperOrder('information-security')).toBe(9);
expect(getSuperOrder('machine-learning-with-python')).toBe(10);
expect(getSuperOrder('coding-interview-prep')).toBe(11);
expect(getSuperOrder('responsive-web-design')).toBe(12);
});
} else {
it('returns unique numbers for all current superblocks (not audited beta)', () => {
expect.assertions(13);
expect(getSuperOrder('responsive-web-design')).toBe(0);
expect(getSuperOrder('javascript-algorithms-and-data-structures')).toBe(
1
);
expect(getSuperOrder('front-end-development-libraries')).toBe(2);
expect(getSuperOrder('data-visualization')).toBe(3);
expect(getSuperOrder('relational-database')).toBe(4);
expect(getSuperOrder('back-end-development-and-apis')).toBe(5);
expect(getSuperOrder('quality-assurance')).toBe(6);
expect(getSuperOrder('scientific-computing-with-python')).toBe(7);
expect(getSuperOrder('data-analysis-with-python')).toBe(8);
expect(getSuperOrder('information-security')).toBe(9);
expect(getSuperOrder('machine-learning-with-python')).toBe(10);
expect(getSuperOrder('coding-interview-prep')).toBe(11);
expect(getSuperOrder('2022/responsive-web-design')).toBe(12);
});
}
it('returns a different order if passed the option showNewCurriculum: true', () => {
// Skip non-english tests while the RWD cert is still being translated.
it('returns unique numbers for all current superblocks', () => {
// Skip non-english tests
if (process.env.CURRICULUM_LOCALE !== 'english') {
return;
}
expect.assertions(14);
expect(
getSuperOrder('2022/responsive-web-design', { showNewCurriculum: true })
).toBe(0);
expect(
getSuperOrder('javascript-algorithms-and-data-structures', {
showNewCurriculum: true
})
).toBe(1);
expect(
getSuperOrder('front-end-development-libraries', {
showNewCurriculum: true
})
).toBe(2);
expect(
getSuperOrder('data-visualization', { showNewCurriculum: true })
).toBe(3);
expect(
getSuperOrder('relational-database', { showNewCurriculum: true })
).toBe(4);
expect(
getSuperOrder('back-end-development-and-apis', {
showNewCurriculum: true
})
).toBe(5);
expect(
getSuperOrder('quality-assurance', { showNewCurriculum: true })
).toBe(6);
expect(
getSuperOrder('scientific-computing-with-python', {
showNewCurriculum: true
})
).toBe(7);
expect(
getSuperOrder('data-analysis-with-python', { showNewCurriculum: true })
).toBe(8);
expect(
getSuperOrder('information-security', { showNewCurriculum: true })
).toBe(9);
expect(
getSuperOrder('machine-learning-with-python', { showNewCurriculum: true })
).toBe(10);
expect(
getSuperOrder('coding-interview-prep', { showNewCurriculum: true })
).toBe(11);
expect(
getSuperOrder('responsive-web-design', { showNewCurriculum: true })
).toBe(12);
expect(
getSuperOrder('2022/javascript-algorithms-and-data-structures', {
showNewCurriculum: true
})
).toBe(13);
if (process.env.SHOW_UPCOMING_CHANGES !== 'true') {
expect.assertions(13);
} else {
expect.assertions(14);
}
expect(getSuperOrder(SuperBlocks.RespWebDesignNew)).toBe(0);
expect(getSuperOrder(SuperBlocks.JsAlgoDataStruct)).toBe(1);
expect(getSuperOrder(SuperBlocks.FrontEndDevLibs)).toBe(2);
expect(getSuperOrder(SuperBlocks.DataVis)).toBe(3);
expect(getSuperOrder(SuperBlocks.RelationalDb)).toBe(4);
expect(getSuperOrder(SuperBlocks.BackEndDevApis)).toBe(5);
expect(getSuperOrder(SuperBlocks.QualityAssurance)).toBe(6);
expect(getSuperOrder(SuperBlocks.SciCompPy)).toBe(7);
expect(getSuperOrder(SuperBlocks.DataAnalysisPy)).toBe(8);
expect(getSuperOrder(SuperBlocks.InfoSec)).toBe(9);
expect(getSuperOrder(SuperBlocks.MachineLearningPy)).toBe(10);
expect(getSuperOrder(SuperBlocks.CodingInterviewPrep)).toBe(11);
if (process.env.SHOW_UPCOMING_CHANGES === 'true') {
expect(getSuperOrder(SuperBlocks.JsAlgoDataStructNew)).toBe(12);
expect(getSuperOrder(SuperBlocks.RespWebDesign)).toBe(13);
} else {
expect(getSuperOrder(SuperBlocks.RespWebDesign)).toBe(12);
}
});
});
+1 -2
View File
@@ -8,7 +8,6 @@ const selectors = {
const certifications = [
'(New) Responsive Web Design',
'Legacy Responsive Web Design',
'JavaScript Algorithms and Data Structures',
'Front End Development Libraries',
'Data Visualization',
@@ -59,7 +58,7 @@ describe('Landing page', () => {
});
it('Has links to all the certifications', function () {
cy.get(selectors.certifications).children().its('length').should('eq', 12);
cy.get(selectors.certifications).children().its('length').should('eq', 11);
cy.wrap(certifications).each(cert => {
cy.get(selectors.certifications).contains(cert);
});
+2 -2
View File
@@ -8,7 +8,6 @@ const locations = {
const superBlockNames = [
'(New) Responsive Web Design Certification',
'Legacy Responsive Web Design Certification',
'JavaScript Algorithms and Data Structures Certification',
'Front End Development Libraries Certification',
'Data Visualization Certification',
@@ -19,7 +18,8 @@ const superBlockNames = [
'Data Analysis with Python Certification',
'Information Security Certification',
'Machine Learning with Python Certification',
'Coding Interview Prep'
'Coding Interview Prep',
'Legacy Responsive Web Design Certification'
];
describe('Learn Landing page (not logged in)', () => {
+1
View File
@@ -101,6 +101,7 @@
"test:curriculum": "cd ./curriculum && npm test",
"test-curriculum-full-output": "cd ./curriculum && npm run test:full-output",
"test-client": "jest client",
"test-config": "jest config",
"test-curriculum-js": "jest curriculum",
"test-server": "jest api-server",
"test-tools": "jest tools",
+7 -2
View File
@@ -7,10 +7,11 @@ import { config } from 'dotenv';
const envPath = resolve(__dirname, '../../.env');
config({ path: envPath });
import { availableLangs, auditedCerts } from '../../config/i18n';
import { availableLangs } from '../../config/i18n';
import { getChallengesForLang } from '../../curriculum/getChallenges';
import { SuperBlocks } from '../../config/certification-settings';
import { ChallengeNode } from '../../client/src/redux/prop-types';
import { getAuditedSuperBlocks } from '../../config/superblock-order';
const superBlockFolderMap = {
'responsive-web-design': '01-responsive-web-design',
@@ -89,7 +90,11 @@ void (async () => {
);
for (const lang of langsToCheck) {
console.log(`\n=== ${lang} ===`);
const certs = auditedCerts[lang as keyof typeof auditedCerts];
const certs = getAuditedSuperBlocks({
language: lang,
showNewCurriculum: process.env.SHOW_NEW_CURRICULUM,
showUpcomingChanges: process.env.SHOW_UPCOMING_CHANGES
});
const langCurriculumDirectory = join(
process.cwd(),
'curriculum',
+7 -3
View File
@@ -2,7 +2,7 @@ import { spawn } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { availableLangs } from '../../../config/i18n';
import { availableLangs, Languages } from '../../../config/i18n';
import env from '../../../config/read-env';
const globalConfigPath = path.resolve(__dirname, '../../../config');
@@ -11,7 +11,7 @@ const { FREECODECAMP_NODE_ENV } = process.env;
function checkClientLocale() {
if (!process.env.CLIENT_LOCALE) throw Error('CLIENT_LOCALE is not set');
if (!availableLangs.client.includes(process.env.CLIENT_LOCALE)) {
if (!availableLangs.client.includes(process.env.CLIENT_LOCALE as Languages)) {
throw Error(`
CLIENT_LOCALE, ${process.env.CLIENT_LOCALE}, is not an available language in config/i18n.ts
@@ -23,7 +23,11 @@ function checkClientLocale() {
function checkCurriculumLocale() {
if (!process.env.CURRICULUM_LOCALE)
throw Error('CURRICULUM_LOCALE is not set');
if (!availableLangs.curriculum.includes(process.env.CURRICULUM_LOCALE)) {
if (
!availableLangs.curriculum.includes(
process.env.CURRICULUM_LOCALE as Languages
)
) {
throw Error(`
CURRICULUM_LOCALE, ${process.env.CURRICULUM_LOCALE}, is not an available language in config/i18n.ts
+6 -15
View File
@@ -1,20 +1,11 @@
// this can go once all certs have been audited.
const { getAuditedSuperBlocks } = require('../config/superblock-order');
// Currently the auditing is going through Crowdin, so once a cert has been 100%
// proofread, we can add it in here. That means that translations can come
// through from Crowdin whenever they are done, but we don't show them on the
// client until we decide the entire cert is ready.
// NOTE: certificates themselves (.yml files) are not currently being
// translated, but when they are they can be included by adding 'certificates'
// to the arrays below
const { auditedCerts } = require('../config/i18n');
function isAuditedCert(lang, cert) {
if (!lang || !cert)
function isAuditedCert(language, superblock) {
if (!language || !superblock)
throw Error('Both arguments must be provided for auditing');
return lang === 'english' || auditedCerts[lang].includes(cert);
const auditedSuperBlocks = getAuditedSuperBlocks(language);
return auditedSuperBlocks.includes(superblock);
}
exports.isAuditedCert = isAuditedCert;