fix(client): compute step number from challenge order (#57209)

This commit is contained in:
Oliver Eyton-Williams 2024-11-18 22:07:12 +01:00 committed by GitHub
parent d7a6951918
commit 594ee9af58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 36 deletions

View File

@ -152,7 +152,7 @@ export interface PrerequisiteChallenge {
slug?: string;
}
export type ChallengeWithCompletedNode = {
export type ExtendedChallenge = {
block: string;
challengeType: number;
dashedName: string;
@ -163,6 +163,7 @@ export type ChallengeWithCompletedNode = {
isCompleted: boolean;
order: number;
superBlock: SuperBlocks;
stepNumber: number;
title: string;
};

View File

@ -7,6 +7,7 @@ import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect';
import { Spacer } from '@freecodecamp/ui';
import { challengeTypes } from '../../../../../shared/config/challenge-types';
import { SuperBlocks } from '../../../../../shared/config/curriculum';
import envData from '../../../../config/env.json';
import { isAuditedSuperBlock } from '../../../../../shared/utils/is-audited';
@ -83,8 +84,9 @@ class Block extends Component<BlockProps> {
} = this.props;
let completedCount = 0;
let stepNumber = 0;
const challengesWithCompleted = challenges.map(challenge => {
const extendedChallenges = challenges.map(challenge => {
const { id } = challenge;
const isCompleted = completedChallengeIds.some(
(completedChallengeId: string) => completedChallengeId === id
@ -92,7 +94,13 @@ class Block extends Component<BlockProps> {
if (isCompleted) {
completedCount++;
}
return { ...challenge, isCompleted };
// Dialogues are interwoven with other challenges in the curriculum, but
// are not considered to be steps.
if (challenge.challengeType !== challengeTypes.dialogue) {
stepNumber++;
}
return { ...challenge, isCompleted, stepNumber };
});
const isProjectBlock = challenges.some(challenge => {
@ -118,17 +126,17 @@ class Block extends Component<BlockProps> {
const expandText = t('intro:misc-text.expand');
const collapseText = t('intro:misc-text.collapse');
const isBlockCompleted = completedCount === challengesWithCompleted.length;
const isBlockCompleted = completedCount === extendedChallenges.length;
const percentageCompleted = Math.floor(
(completedCount / challengesWithCompleted.length) * 100
(completedCount / extendedChallenges.length) * 100
);
const courseCompletionStatus = () => {
if (completedCount === 0) {
return t('learn.not-started');
}
if (completedCount === challengesWithCompleted.length) {
if (completedCount === extendedChallenges.length) {
return t('learn.completed');
}
return `${percentageCompleted}% ${t('learn.completed')}`;
@ -174,19 +182,19 @@ class Block extends Component<BlockProps> {
<span
aria-hidden='true'
className='map-completed-count'
>{`${completedCount}/${challengesWithCompleted.length}`}</span>
>{`${completedCount}/${extendedChallenges.length}`}</span>
<span className='sr-only'>
,{' '}
{t('learn.challenges-completed', {
completedCount,
totalChallenges: challengesWithCompleted.length
totalChallenges: extendedChallenges.length
})}
</span>
</div>
</button>
{isExpanded && (
<Challenges
challengesWithCompleted={challengesWithCompleted}
challenges={extendedChallenges}
isProjectBlock={isProjectBlock}
/>
)}
@ -218,7 +226,7 @@ class Block extends Component<BlockProps> {
</div>
<BlockIntros intros={blockIntroArr} />
<Challenges
challengesWithCompleted={challengesWithCompleted}
challenges={extendedChallenges}
isProjectBlock={isProjectBlock}
/>
</div>
@ -258,7 +266,7 @@ class Block extends Component<BlockProps> {
<div id={`${block}-panel`}>
<BlockIntros intros={blockIntroArr} />
<Challenges
challengesWithCompleted={challengesWithCompleted}
challenges={extendedChallenges}
isProjectBlock={isProjectBlock}
isGridMap={true}
blockTitle={blockTitle}
@ -301,7 +309,7 @@ class Block extends Component<BlockProps> {
onClick={() => {
this.handleBlockClick();
}}
to={challengesWithCompleted[0].fields.slug}
to={extendedChallenges[0].fields.slug}
>
<CheckMark isCompleted={isBlockCompleted} />
{blockTitle}{' '}
@ -351,7 +359,7 @@ class Block extends Component<BlockProps> {
{isExpanded && (
<div id={`${block}-panel`}>
<Challenges
challengesWithCompleted={challengesWithCompleted}
challenges={extendedChallenges}
isProjectBlock={isProjectBlock}
/>
</div>
@ -386,7 +394,7 @@ class Block extends Component<BlockProps> {
onClick={() => {
this.handleBlockClick();
}}
to={challengesWithCompleted[0].fields.slug}
to={extendedChallenges[0].fields.slug}
>
<CheckMark isCompleted={isBlockCompleted} />
{blockType && <BlockLabel blockType={blockType} />}
@ -433,7 +441,7 @@ class Block extends Component<BlockProps> {
{isExpanded && (
<div id={`${block}-panel`} className='challenge-grid-block-panel'>
<Challenges
challengesWithCompleted={challengesWithCompleted}
challenges={extendedChallenges}
isProjectBlock={isProjectBlock}
isGridMap={true}
blockTitle={blockTitle}

View File

@ -3,20 +3,14 @@ import { withTranslation, useTranslation } from 'react-i18next';
import GreenNotCompleted from '../../../assets/icons/green-not-completed';
import GreenPass from '../../../assets/icons/green-pass';
import { ChallengeWithCompletedNode } from '../../../redux/prop-types';
import { ExtendedChallenge } from '../../../redux/prop-types';
import { SuperBlocks } from '../../../../../shared/config/curriculum';
import { challengeTypes } from '../../../../../shared/config/challenge-types';
import { Link } from '../../../components/helpers';
import { ButtonLink } from '../../../components/helpers/button-link';
const getStepNumber = (dashedName: string) => {
// dashedName should be in the format 'step-1' or 'task-1'
const match = dashedName.match(/-(\d+)/);
return match ? match[1] : '';
};
interface Challenges {
challengesWithCompleted: ChallengeWithCompletedNode[];
challenges: ExtendedChallenge[];
isProjectBlock: boolean;
isGridMap?: boolean;
blockTitle?: string | null;
@ -25,11 +19,7 @@ interface Challenges {
const CheckMark = ({ isCompleted }: { isCompleted: boolean }) =>
isCompleted ? <GreenPass /> : <GreenNotCompleted />;
const Challenge = ({
challenge
}: {
challenge: ChallengeWithCompletedNode;
}) => (
const Challenge = ({ challenge }: { challenge: ExtendedChallenge }) => (
<Link to={challenge.fields.slug}>
<span className='map-badge'>
<CheckMark isCompleted={challenge.isCompleted} />
@ -38,7 +28,7 @@ const Challenge = ({
</Link>
);
const Project = ({ challenge }: { challenge: ChallengeWithCompletedNode }) => (
const Project = ({ challenge }: { challenge: ExtendedChallenge }) => (
<Link to={challenge.fields.slug}>
{challenge.title}
<span className='map-badge map-project-checkmark'>
@ -48,18 +38,18 @@ const Project = ({ challenge }: { challenge: ChallengeWithCompletedNode }) => (
);
function Challenges({
challengesWithCompleted,
challenges,
isProjectBlock,
isGridMap = false,
blockTitle
}: Challenges): JSX.Element {
const { t } = useTranslation();
const firstIncompleteChallenge = challengesWithCompleted.find(
const firstIncompleteChallenge = challenges.find(
challenge => !challenge.isCompleted
);
const isChallengeStarted = !!challengesWithCompleted.find(
const isChallengeStarted = !!challenges.find(
challenge => challenge.isCompleted
);
@ -78,14 +68,14 @@ function Challenges({
<nav
aria-label={
blockTitle
? challengesWithCompleted[0].superBlock === SuperBlocks.A2English
? challenges[0].superBlock === SuperBlocks.A2English
? t('aria.dialogues-and-tasks-for', { blockTitle })
: t('aria.steps-for', { blockTitle })
: t('aria.steps')
}
>
<ul className={`map-challenges-ul map-challenges-grid `}>
{challengesWithCompleted.map(challenge => (
{challenges.map(challenge => (
<li
className={`map-challenge-title map-challenge-title-grid ${
isProjectBlock
@ -111,7 +101,7 @@ function Challenges({
? t('aria.task')
: t('aria.step')}
</span>
<span>{getStepNumber(challenge.dashedName)}</span>
<span>{challenge.stepNumber}</span>
<span className='sr-only'>
{challenge.isCompleted
? t('icons.passed')
@ -130,7 +120,7 @@ function Challenges({
</>
) : (
<ul className={`map-challenges-ul`}>
{challengesWithCompleted.map(challenge => (
{challenges.map(challenge => (
<li
className={`map-challenge-title ${
isProjectBlock ? 'map-project-wrap' : 'map-challenge-wrap'