From b223cdd2553342a08297840f52722d1afd974dd3 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Wed, 16 Feb 2022 22:48:22 +0100 Subject: [PATCH] refactor: display solutions (#45139) * refactor: re-organise show-project-links * refactor: update ChallengeFile's declared shape * fix: handle missing challenge solution * refactor: use display function for Certification * refactor: use display function for TimeLine * refactor: use common component for timeline + cert * fix: handle legacy solutions * refactor: use widget for certifications * refactor: reorganise ShowDisplayWidget * refactor: remove unused ids * test: pass dataCy, not projectTitle, to widget * chore: kebabify * revert: add id back for dropdown Co-authored-by: Shaun Hamilton * revert: add the ids back Co-authored-by: Shaun Hamilton --- client/i18n/locales/english/translations.json | 1 + .../client-only-routes/show-project-links.tsx | 51 ++----- .../src/client-only-routes/show-settings.tsx | 2 +- .../profile/components/time-line.test.tsx | 2 +- .../{TimeLine.tsx => time-line.tsx} | 86 ++--------- client/src/components/profile/profile.tsx | 2 +- .../{Certification.js => certification.js} | 90 ++--------- ...fication.test.js => certification.test.js} | 2 +- .../solution-display-widget/index.tsx | 141 ++++++++++++++++++ client/src/redux/prop-types.ts | 2 +- .../utils/__fixtures/completed-challenges.ts | 39 +++++ .../src/utils/solution-display-type.test.ts | 29 ++++ client/src/utils/solution-display-type.ts | 16 ++ curriculum/schema/challengeSchema.js | 2 +- .../{challenges.js => challenges.ts} | 30 ++-- 15 files changed, 285 insertions(+), 210 deletions(-) rename client/src/components/profile/components/{TimeLine.tsx => time-line.tsx} (79%) rename client/src/components/settings/{Certification.js => certification.js} (84%) rename client/src/components/settings/{Certification.test.js => certification.test.js} (99%) create mode 100644 client/src/components/solution-display-widget/index.tsx create mode 100644 client/src/utils/__fixtures/completed-challenges.ts create mode 100644 client/src/utils/solution-display-type.test.ts create mode 100644 client/src/utils/solution-display-type.ts rename utils/__fixtures__/{challenges.js => challenges.ts} (59%) diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index f71db454a7..f1a02b875b 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -551,6 +551,7 @@ "heading-legacy-full-stack": "As part of this Legacy Full Stack certification, {{user}} completed the following certifications:", "heading": "As part of this certification, {{user}} built the following projects and got all automated test suites to pass:", "solution": "solution", + "no-solution": "error displaying solution, email support@freeCodeCamp.org to get help.", "source": "source", "footnote": "If you suspect that any of these projects violate the <2>academic honesty policy, please <5>report this to our team.", "title": { diff --git a/client/src/client-only-routes/show-project-links.tsx b/client/src/client-only-routes/show-project-links.tsx index 39e775e36a..3ca5a39dcb 100644 --- a/client/src/client-only-routes/show-project-links.tsx +++ b/client/src/client-only-routes/show-project-links.tsx @@ -10,7 +10,7 @@ import { legacyProjectMap } from '../resources/cert-and-project-map'; -import { maybeUrlRE } from '../utils'; +import { SolutionDisplayWidget } from '../components/solution-display-widget'; interface ShowProjectLinksProps { certName: string; @@ -52,8 +52,8 @@ const ShowProjectLinks = (props: ShowProjectLinksProps): JSX.Element => { return null; } - const { solution, githubLink, challengeFiles } = completedProject; - const onClickHandler = () => + const { solution, challengeFiles } = completedProject; + const showFilesSolution = () => setSolutionState({ projectTitle, challengeFiles, @@ -61,46 +61,13 @@ const ShowProjectLinks = (props: ShowProjectLinksProps): JSX.Element => { isOpen: true }); - if (challengeFiles?.length) { - return ( - - ); - } - if (githubLink) { - return ( - <> - - {t('certification.project.solution')} - - ,{' '} - - {t('certification.project.source')} - - - ); - } - if (maybeUrlRE.test(solution ?? '')) { - return ( - - {t('certification.project.solution')} - - ); - } return ( - + ); }; diff --git a/client/src/client-only-routes/show-settings.tsx b/client/src/client-only-routes/show-settings.tsx index b58f60452b..ced9d1fea1 100644 --- a/client/src/client-only-routes/show-settings.tsx +++ b/client/src/client-only-routes/show-settings.tsx @@ -8,7 +8,7 @@ import { createSelector } from 'reselect'; import envData from '../../../config/env.json'; import { createFlashMessage } from '../components/Flash/redux'; import { Loader, Spacer } from '../components/helpers'; -import Certification from '../components/settings/Certification'; +import Certification from '../components/settings/certification'; import About from '../components/settings/about'; import DangerZone from '../components/settings/danger-zone'; import Email from '../components/settings/email'; diff --git a/client/src/components/profile/components/time-line.test.tsx b/client/src/components/profile/components/time-line.test.tsx index b5044188eb..d08b0654d3 100644 --- a/client/src/components/profile/components/time-line.test.tsx +++ b/client/src/components/profile/components/time-line.test.tsx @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'; import { useStaticQuery } from 'gatsby'; import React from 'react'; -import TimeLine from './TimeLine'; +import TimeLine from './time-line'; beforeEach(() => { // @ts-ignore diff --git a/client/src/components/profile/components/TimeLine.tsx b/client/src/components/profile/components/time-line.tsx similarity index 79% rename from client/src/components/profile/components/TimeLine.tsx rename to client/src/components/profile/components/time-line.tsx index 0558289d16..df3ee76518 100644 --- a/client/src/components/profile/components/TimeLine.tsx +++ b/client/src/components/profile/components/time-line.tsx @@ -1,11 +1,5 @@ /* eslint-disable @typescript-eslint/unbound-method */ -import { - Button, - Modal, - Table, - DropdownButton, - MenuItem -} from '@freecodecamp/react-bootstrap'; +import { Button, Modal, Table } from '@freecodecamp/react-bootstrap'; import Loadable from '@loadable/component'; import { useStaticQuery, graphql } from 'gatsby'; import { reverse, sortBy } from 'lodash-es'; @@ -21,8 +15,8 @@ import { } from '../../../../../utils'; import CertificationIcon from '../../../assets/icons/certification-icon'; import { ChallengeFiles, CompletedChallenge } from '../../../redux/prop-types'; -import { maybeUrlRE } from '../../../utils'; import { FullWidthRow, Link } from '../../helpers'; +import { SolutionDisplayWidget } from '../../solution-display-widget'; import TimelinePagination from './timeline-pagination'; import './timeline.css'; @@ -53,7 +47,6 @@ function TimelineInner({ idToNameMap, sortedTimeline, totalPages, - completedMap, t, username @@ -96,73 +89,20 @@ function TimelineInner({ } function renderViewButton( - id: string, - challengeFiles: ChallengeFiles, - githubLink?: string, - solution?: string | null + completedChallenge: CompletedChallenge ): React.ReactNode { - if (challengeFiles?.length) { - return ( - - ); - } else if (githubLink) { - return ( -
- - - {t('buttons.frontend')} - - - {t('buttons.backend')} - - -
- ); - } else if (solution && maybeUrlRE.test(solution)) { - return ( - - ); - } else { - return null; - } + const { id, solution, challengeFiles } = completedChallenge; + return ( + viewSolution(id, solution, challengeFiles)} + displayContext={'timeline'} + > + ); } function renderCompletion(completed: CompletedChallenge): JSX.Element { - const { id, challengeFiles, githubLink, solution } = completed; + const { id } = completed; const completedDate = new Date(completed.completedDate); // @ts-expect-error idToNameMap is not a Map... const { challengeTitle, challengePath, certPath } = idToNameMap.get(id); @@ -181,7 +121,7 @@ function TimelineInner({ {challengeTitle} )} - {renderViewButton(id, challengeFiles, githubLink, solution)} + {renderViewButton(completed)}