feat(client, curriculum): add support for blockLayout property (#56101)

Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
Huyen Nguyen
2024-10-15 20:47:21 +05:30
committed by GitHub
co-authored by Tom
parent b3d4241bd6
commit 815f0291b6
9 changed files with 99 additions and 10 deletions
+2
View File
@@ -74,6 +74,7 @@ exports.createPages = async function createPages({
challenge {
block
blockType
blockLayout
certification
challengeType
dashedName
@@ -260,6 +261,7 @@ exports.createSchemaCustomization = ({ actions }) => {
}
type Challenge {
blockType: String
blockLayout: String
challengeFiles: [FileContents]
explanation: String
notes: String
+2 -1
View File
@@ -1,6 +1,6 @@
import { HandlerProps } from 'react-reflex';
import { SuperBlocks } from '../../../shared/config/curriculum';
import { BlockTypes } from '../../../shared/config/blocks';
import { BlockLayouts, BlockTypes } from '../../../shared/config/blocks';
import { Themes } from '../components/settings/theme';
import { type CertTitle } from '../../config/cert-and-project-map';
@@ -167,6 +167,7 @@ export type ChallengeNode = {
challenge: {
block: string;
blockType: BlockTypes;
blockLayout: BlockLayouts;
certification: string;
challengeOrder: number;
challengeType: number;
@@ -20,7 +20,7 @@ import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types';
import { playTone } from '../../../utils/tone';
import { makeExpandedBlockSelector, toggleBlock } from '../redux';
import { isGridBased, isProjectBased } from '../../../utils/curriculum-layout';
import { BlockTypes } from '../../../../../shared/config/blocks';
import { BlockLayouts, BlockTypes } from '../../../../../shared/config/blocks';
import Challenges from './challenges';
import BlockLabel from './block-label';
@@ -151,7 +151,12 @@ class Block extends Component<BlockProps> {
</div>
);
const Block = (
/**
* ChallengeListBlock displays challenges in a list.
* This layout is used in backend blocks, The Odin Project blocks, and blocks in legacy certification.
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#basic-javascript
*/
const ChallengeListBlock = (
<>
{' '}
<ScrollableAnchor id={block}>
@@ -209,7 +214,12 @@ class Block extends Component<BlockProps> {
</>
);
const ProjectBlock = (
/**
* ProjectListBlock displays a list of certification projects.
* This layout is used in legacy certifications.
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#javascript-algorithms-and-data-structures-projects
*/
const ProjectListBlock = (
<>
<ScrollableAnchor id={block}>
<div className='block'>
@@ -247,7 +257,12 @@ class Block extends Component<BlockProps> {
return `${percentageCompleted}% ${t('learn.completed')}`;
};
const GridBlock = (
/**
* ChallengeGridBlock displays challenges in a grid.
* This layout is used for step-based blocks.
* Example: https://www.freecodecamp.org/learn/2022/responsive-web-design/#learn-html-by-building-a-cat-photo-app
*/
const ChallengeGridBlock = (
<>
{' '}
<ScrollableAnchor id={block}>
@@ -304,7 +319,12 @@ class Block extends Component<BlockProps> {
</>
);
const GridProjectBlock = (
/**
* LinkBlock displays the block as a single link.
* This layout is used if the block has a single challenge.
* Example: https://www.freecodecamp.org/learn/2022/responsive-web-design/#build-a-survey-form-project
*/
const LinkBlock = (
<ScrollableAnchor id={block}>
<div className='block block-grid grid-project-block'>
<div className='tags-wrapper'>
@@ -346,14 +366,24 @@ class Block extends Component<BlockProps> {
</ScrollableAnchor>
);
const blockrenderer = () => {
if (isProjectBlock) return isGridBlock ? GridProjectBlock : ProjectBlock;
return isGridBlock ? GridBlock : Block;
const blockRenderer = () => {
const blockLayout = challenges[0].blockLayout;
// `blockLayout` property isn't available in all challenges
if (!blockLayout) {
if (isProjectBlock) return isGridBlock ? LinkBlock : ProjectListBlock;
return isGridBlock ? ChallengeGridBlock : ChallengeListBlock;
}
if (blockLayout === BlockLayouts.ChallengeGrid) return ChallengeGridBlock;
if (blockLayout === BlockLayouts.ChallengeList) return ChallengeListBlock;
if (blockLayout === BlockLayouts.Link) return LinkBlock;
if (blockLayout === BlockLayouts.ProjectList) return ProjectListBlock;
};
return (
<>
{blockrenderer()}
{blockRenderer()}
{isGridBlock && !isProjectBlock ? null : <Spacer size='medium' />}
</>
);
@@ -305,6 +305,7 @@ export const query = graphql`
order
superBlock
dashedName
blockLayout
}
}
}
+1
View File
@@ -287,6 +287,7 @@ function generateChallengeCreator(lang, englishPath, i18nPath) {
challenge.block = meta.dashedName;
challenge.blockType = meta.blockType;
challenge.blockLayout = meta.blockLayout;
challenge.hasEditableBoundaries = !!meta.hasEditableBoundaries;
challenge.order = meta.order;
// const superOrder = getSuperOrder(meta.superBlock);
@@ -132,6 +132,15 @@ const schema = Joi.object()
).required(),
otherwise: Joi.valid(null)
}),
blockLayout: Joi.when('superBlock', {
is: [SuperBlocks.FrontEndDevelopment],
then: Joi.valid(
'challenge-list',
'challenge-grid',
'link',
'project-list'
)
}),
challengeOrder: Joi.number(),
certification: Joi.string().regex(slugWithSlashRE),
challengeType: Joi.number().min(0).max(23).required(),
+9
View File
@@ -129,6 +129,15 @@ const schema = Joi.object()
).required(),
otherwise: Joi.valid(null)
}),
blockLayout: Joi.when('superBlock', {
is: [SuperBlocks.FrontEndDevelopment],
then: Joi.valid(
'challenge-list',
'challenge-grid',
'link',
'project-list'
)
}),
challengeOrder: Joi.number(),
certification: Joi.string().regex(slugWithSlashRE),
challengeType: Joi.number().min(0).max(23).required(),
+6
View File
@@ -7,6 +7,12 @@ const schema = Joi.object()
.keys({
name: Joi.string().required(),
blockType: Joi.valid('workshop', 'lab', 'lecture', 'quiz', 'exam'),
blockLayout: Joi.valid(
'challenge-list',
'challenge-grid',
'link',
'project-list'
),
isUpcomingChange: Joi.boolean().required(),
dashedName: Joi.string().regex(slugRE).required(),
superBlock: Joi.string().regex(slugWithSlashRE).required(),
+30
View File
@@ -6,3 +6,33 @@ export enum BlockTypes {
quiz = 'quiz',
exam = 'exam'
}
export enum BlockLayouts {
/**
* ChallengeList displays challenges in a list.
* This layout is used in backend blocks, The Odin Project blocks, and blocks in legacy certification.
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#basic-javascript
*/
ChallengeList = 'challenge-list',
/**
* ChallengeGrid displays challenges in a grid.
* This layout is used for step-based blocks.
* Example: https://www.freecodecamp.org/learn/2022/responsive-web-design/#learn-html-by-building-a-cat-photo-app
*/
ChallengeGrid = 'challenge-grid',
/**
* Link displays the block as a single link.
* This layout is used if the block has a single challenge.
* Example: https://www.freecodecamp.org/learn/2022/responsive-web-design/#build-a-survey-form-project
*/
Link = 'link',
/**
* ProjectList displays a list of certification projects.
* This layout is used in legacy certifications.
* Example: https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#javascript-algorithms-and-data-structures-projects
*/
ProjectList = 'project-list'
}