From af156fdfe4cc74f8631eaf636dc100bd7abbf1a2 Mon Sep 17 00:00:00 2001 From: Jagruti Tiwari Date: Thu, 27 Oct 2022 19:56:45 +0530 Subject: [PATCH] feat(client): add "completed x of y" in completion modal (#47022) * feat: wrote a new function and updated the test case * fix: lint errors * fix: Fixed the changes not being reflected in the code. And undid unwanted commits from the last time * fix: removing unwanted updates to the package-lock.json * fix: Removing unwanted changes from package-lock.json * fix: removing unwanted changes from package-lock.json * fix: updating snapshots to fix the test case * chore: update snapshot * Update client/src/templates/Challenges/components/completion-modal.tsx Co-authored-by: Shaun Hamilton * Update client/src/templates/Challenges/components/completion-modal.tsx Co-authored-by: Shaun Hamilton * Update client/src/templates/Challenges/components/completion-modal.tsx Co-authored-by: Shaun Hamilton * fix: removed hard coded english words from translation text * fix: only show output when id is cert project * fix: correctly count completed challenges * fix: update test and snapshot [skip ci] * fix: at least one challenge is always completed When the modal pops-up, there is always at least one challenge counted as "complete" * refactor: slightly improve readability Co-authored-by: Ilenia Co-authored-by: Mrugesh Mohapatra Co-authored-by: Shaun Hamilton Co-authored-by: Oliver Eyton-Williams --- client/i18n/locales/english/translations.json | 1 + .../components/completion-modal-body.test.tsx | 3 + .../components/completion-modal-body.tsx | 30 ++++++- .../components/completion-modal.test.tsx | 5 -- .../components/completion-modal.tsx | 81 ++++++++++++++----- 5 files changed, 93 insertions(+), 27 deletions(-) diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 21b5256bb36..b375e0452fe 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -284,6 +284,7 @@ "sign-in-save": "Sign in to save your progress", "download-solution": "Download my solution", "percent-complete": "{{percent}}% complete", + "project-complete": "Completed {{completedChallengesInBlock}} of {{totalChallengesInBlock}} certification projects", "tried-rsa": "If you've already tried the <0>Read-Search-Ask method, then you can ask for help on the freeCodeCamp forum.", "rsa": "Read, search, ask", "rsa-forum": "Before making a new post please see if your question has <0>already been answered on the forum.", diff --git a/client/src/templates/Challenges/components/completion-modal-body.test.tsx b/client/src/templates/Challenges/components/completion-modal-body.test.tsx index 04d9a4a40b1..c0aefbb5d89 100644 --- a/client/src/templates/Challenges/components/completion-modal-body.test.tsx +++ b/client/src/templates/Challenges/components/completion-modal-body.test.tsx @@ -8,6 +8,9 @@ import CompletionModalBody from './completion-modal-body'; const props = { block: 'basic-html-and-html5', completedPercent: Math.floor(Math.random() * 101), + completedChallengesInBlock: 2, + totalChallengesInBlock: 5, + currentChallengeId: '', superBlock: SuperBlocks.RespWebDesign }; diff --git a/client/src/templates/Challenges/components/completion-modal-body.tsx b/client/src/templates/Challenges/components/completion-modal-body.tsx index 1decdf4dbf0..e60dd54cee7 100644 --- a/client/src/templates/Challenges/components/completion-modal-body.tsx +++ b/client/src/templates/Challenges/components/completion-modal-body.tsx @@ -2,12 +2,16 @@ import BezierEasing from 'bezier-easing'; import React, { PureComponent } from 'react'; import { TFunction, withTranslation } from 'react-i18next'; import GreenPass from '../../../assets/icons/green-pass'; +import { certMap } from '../../../resources/cert-and-project-map'; interface CompletionModalBodyProps { block: string; + completedChallengesInBlock: number; completedPercent: number; + currentChallengeId: string; superBlock: string; t: TFunction; + totalChallengesInBlock: number; } interface CompletionModalBodyState { @@ -70,9 +74,23 @@ export class CompletionModalBody extends PureComponent< } render(): JSX.Element { - const { block, completedPercent, superBlock, t } = this.props; + const { + block, + completedPercent, + totalChallengesInBlock, + completedChallengesInBlock, + currentChallengeId, + superBlock, + t + } = this.props; const blockTitle = t(`intro:${superBlock}.blocks.${block}.title`); - + const isCertificationProject = certMap.some(cert => { + // @ts-expect-error If `projects` does not exist, no consequences + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + return cert.projects?.some( + (project: { id: string }) => project.id === currentChallengeId + ); + }); return ( <>
@@ -112,6 +130,14 @@ export class CompletionModalBody extends PureComponent<
+ {isCertificationProject && ( + + {t('learn.project-complete', { + completedChallengesInBlock, + totalChallengesInBlock + })} + + )} ); diff --git a/client/src/templates/Challenges/components/completion-modal.test.tsx b/client/src/templates/Challenges/components/completion-modal.test.tsx index 8e6c76a7607..19aeb5efdcc 100644 --- a/client/src/templates/Challenges/components/completion-modal.test.tsx +++ b/client/src/templates/Challenges/components/completion-modal.test.tsx @@ -5,15 +5,10 @@ jest.mock('../../../analytics'); const completedChallengesIds = ['1', '3', '5'], currentBlockIds = ['1', '3', '5', '7'], id = '7', - fakeId = '12345', fakeCompletedChallengesIds = ['1', '3', '5', '7', '8']; describe('', () => { describe('getCompletedPercent', () => { - it('returns 0 if no challenges have been completed', () => { - expect(getCompletedPercent([], currentBlockIds, fakeId)).toBe(0); - }); - it('returns 25 if one out of four challenges are complete', () => { expect(getCompletedPercent([], currentBlockIds, currentBlockIds[1])).toBe( 25 diff --git a/client/src/templates/Challenges/components/completion-modal.tsx b/client/src/templates/Challenges/components/completion-modal.tsx index 6437f034e43..64a13b47403 100644 --- a/client/src/templates/Challenges/components/completion-modal.tsx +++ b/client/src/templates/Challenges/components/completion-modal.tsx @@ -77,21 +77,33 @@ export function getCompletedPercent( currentBlockIds: string[] = [], currentChallengeId: string ): number { - completedChallengesIds = completedChallengesIds.includes(currentChallengeId) - ? completedChallengesIds - : [...completedChallengesIds, currentChallengeId]; - - const completedChallengesInBlock = completedChallengesIds.filter(id => { - return currentBlockIds.includes(id); - }); - + const completedChallengesInBlock = getCompletedChallengesInBlock( + completedChallengesIds, + currentBlockIds, + currentChallengeId + ); const completedPercent = Math.round( - (completedChallengesInBlock.length / currentBlockIds.length) * 100 + (completedChallengesInBlock / currentBlockIds.length) * 100 ); return completedPercent > 100 ? 100 : completedPercent; } +function getCompletedChallengesInBlock( + completedChallengesIds: string[], + currentBlockChallengeIds: string[], + currentChallengeId: string +) { + const oldCompletionCount = completedChallengesIds.filter(challengeId => + currentBlockChallengeIds.includes(challengeId) + ).length; + + const isAlreadyCompleted = + completedChallengesIds.includes(currentChallengeId); + + return isAlreadyCompleted ? oldCompletionCount : oldCompletionCount + 1; +} + interface CompletionModalsProps { allowBlockDonationRequests: (arg0: string) => void; block: string; @@ -116,6 +128,7 @@ interface CompletionModalsProps { interface CompletionModalInnerState { downloadURL: null | string; completedPercent: number; + completedChallengesInBlock: number; } export class CompletionModalInner extends Component< @@ -129,7 +142,8 @@ export class CompletionModalInner extends Component< this.state = { downloadURL: null, - completedPercent: 0 + completedPercent: 0, + completedChallengesInBlock: 0 }; } @@ -139,7 +153,11 @@ export class CompletionModalInner extends Component< ): CompletionModalInnerState { const { challengeFiles, isOpen } = props; if (!isOpen) { - return { downloadURL: null, completedPercent: 0 }; + return { + downloadURL: null, + completedPercent: 0, + completedChallengesInBlock: 0 + }; } const { downloadURL } = state; if (downloadURL) { @@ -168,7 +186,21 @@ export class CompletionModalInner extends Component< const completedPercent = isSignedIn ? getCompletedPercent(completedChallengesIds, currentBlockIds, id) : 0; - return { downloadURL: newURL, completedPercent: completedPercent }; + + let completedChallengesInBlock = 0; + if (currentBlockIds) { + completedChallengesInBlock = getCompletedChallengesInBlock( + completedChallengesIds, + currentBlockIds, + id + ); + } + + return { + downloadURL: newURL, + completedPercent, + completedChallengesInBlock + }; } handleKeypress(e: React.KeyboardEvent): void { @@ -207,15 +239,19 @@ export class CompletionModalInner extends Component< const { block, close, + currentBlockIds, + id, isOpen, - message, - t, - title, isSignedIn, - superBlock = '' + message, + superBlock = '', + t, + title } = this.props; - const { completedPercent } = this.state; + const { completedPercent, completedChallengesInBlock } = this.state; + + const totalChallengesInBlock = currentBlockIds?.length ?? 0; if (isOpen) { executeGA({ type: 'modal', data: '/completion-modal' }); @@ -243,9 +279,14 @@ export class CompletionModalInner extends Component<