From a430aa42ef770ec09d72d64cb6346f91d8e88812 Mon Sep 17 00:00:00 2001 From: Naomi Carrigan Date: Tue, 8 Feb 2022 17:49:00 -0800 Subject: [PATCH] feat(client): styling for project cards (#44771) * feat: project -> certification project * feat: add certification icon * feat: cert project banner * feat: combine new cert detection methods * feat: add a dynamic tag * feat: replace blocks with grid bloks for new RWD * feat: adjust individual progress bar * feat: add dropdown icon * feat: add conditional rendering * feat: add local and fix cert card link Co-authored-by: ahmad abdolsaheb --- client/i18n/locales/english/intro.json | 10 +- client/i18n/locales/english/translations.json | 3 +- client/src/assets/icons/dropdown.tsx | 26 ++ .../Introduction/components/block.tsx | 258 +++++++++++++----- .../Introduction/components/challenges.tsx | 3 +- .../Introduction/components/legacy-links.tsx | 3 +- client/src/templates/Introduction/intro.css | 126 ++++++++- .../Introduction/super-block-intro.tsx | 1 - .../is-new-responsive-web-design-cert.ts | 5 + 9 files changed, 347 insertions(+), 88 deletions(-) create mode 100644 client/src/assets/icons/dropdown.tsx create mode 100644 client/src/utils/is-new-responsive-web-design-cert.ts diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json index 7fe76f7061..5b131e071f 100644 --- a/client/i18n/locales/english/intro.json +++ b/client/i18n/locales/english/intro.json @@ -76,31 +76,31 @@ "note": "Note: Some browser extensions, such as ad-blockers and dark mode extensions can interfere with the tests. If you face issues, we recommend disabling extensions that modify the content or layout of pages, while taking the course.", "blocks": { "build-a-tribute-page-project": { - "title": "Build a Tribute Page Project", + "title": "Tribute Page", "intro": [ "This is one of the required projects to earn your certification.", "For this project, you will build a tribute page for a subject of your choosing, fictional or real." ] }, "build-a-personal-portfolio-webpage-project": { - "title": "Build a Personal Portfolio Webpage Project", + "title": "Personal Portfolio Webpage", "intro": [ "This is one of the required projects to earn your certification.", "For this project, you will build your own personal portfolio page." ] }, "build-a-product-landing-page-project": { - "title": "Build a Product Landing Page Project", + "title": "Product Landing Page", "intro": [ "This is one of the required projects to earn your certification.", "For this project, you will build a product landing page to market a product of your choice." ] }, "build-a-survey-form-project": { - "title": "Build a Survey Form Project", + "title": "Survey Form", "intro": [ "This is one of the required projects to earn your certification.", "For this project, you will build a survey form to collect data from your users." ] }, "build-a-technical-documentation-page-project": { - "title": "Build a Technical Documentation Page Project", + "title": "Technical Documentation Page", "intro": [ "This is one of the required projects to earn your certification.", "For this project, you will build a technical documentation page to serve as instruction or reference for a topic." diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 1892ef5b77..4807454721 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -418,7 +418,8 @@ "email": "Email", "and": "and", "change-theme": "Sign in to change theme.", - "translation-pending": "Help us translate" + "translation-pending": "Help us translate", + "certification-project": "Certification Project" }, "icons": { "gold-cup": "Gold Cup", diff --git a/client/src/assets/icons/dropdown.tsx b/client/src/assets/icons/dropdown.tsx new file mode 100644 index 0000000000..b7d12e96bb --- /dev/null +++ b/client/src/assets/icons/dropdown.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +function DropDown(): JSX.Element { + return ( + + + + ); +} + +DropDown.displayName = 'DropDown'; + +export default DropDown; diff --git a/client/src/templates/Introduction/components/block.tsx b/client/src/templates/Introduction/components/block.tsx index 863f47a982..8f67b1be0e 100644 --- a/client/src/templates/Introduction/components/block.tsx +++ b/client/src/templates/Introduction/components/block.tsx @@ -1,22 +1,25 @@ import React, { Component } from 'react'; import { withTranslation, TFunction } from 'react-i18next'; +import { ProgressBar } from '@freecodecamp/react-bootstrap'; import { connect } from 'react-redux'; import ScrollableAnchor from 'react-scrollable-anchor'; import { bindActionCreators, Dispatch } from 'redux'; import { createSelector } from 'reselect'; import { SuperBlocks } from '../../../../../config/certification-settings'; - import envData from '../../../../../config/env.json'; import { isAuditedCert } from '../../../../../utils/is-audited'; import Caret from '../../../assets/icons/caret'; +import DropDown from '../../../assets/icons/dropdown'; import GreenNotCompleted from '../../../assets/icons/green-not-completed'; import GreenPass from '../../../assets/icons/green-pass'; -import { Link } from '../../../components/helpers'; +import { Link, Spacer } from '../../../components/helpers'; import { completedChallengesSelector, executeGA } from '../../../redux'; import { ChallengeNode, CompletedChallenge } from '../../../redux/prop-types'; import { playTone } from '../../../utils/tone'; import { makeExpandedBlockSelector, toggleBlock } from '../redux'; +import IsNewRespCert from '../../../utils/is-new-responsive-web-design-cert'; import Challenges from './challenges'; +import '../intro.css'; const { curriculumLocale } = envData; @@ -101,6 +104,8 @@ export class Block extends Component { t } = this.props; + const isNewResponsiveWebDesign = IsNewRespCert(superBlock); + let completedCount = 0; const challengesWithCompleted = challenges.map(({ challenge }) => { const { id } = challenge; @@ -143,84 +148,191 @@ export class Block extends Component { collapse: string; } = t('intro:misc-text'); - return isProjectBlock ? ( - -
-
- -

- {blockTitle} - # -

-
- {!isAuditedCert(curriculumLocale, superBlock) && ( -
- - {t('misc.translation-pending')} - -
- )} -
- {this.renderBlockIntros(blockIntroArr)} - -
-
- ) : ( - -
-
- -

- {blockTitle} - # -

-
- {!isAuditedCert(curriculumLocale, superBlock) && ( -
- - {t('misc.translation-pending')} - -
- )} -
- {this.renderBlockIntros(blockIntroArr)} - - {isExpanded && ( + {this.renderBlockIntros(blockIntroArr)} + + {isExpanded && ( + + )} +
+
+ + ); + + const ProjectBlock = ( + <> + +
+
+ +

+ {blockTitle} + # +

+
+ {!isAuditedCert(curriculumLocale, superBlock) && ( +
+ + {t('misc.translation-pending')} + +
+ )} +
+ {this.renderBlockIntros(blockIntroArr)} - )} -
-
+ + + + ); + + const GridBlock = ( + <> + {' '} + + + + + ); + + const GridProjectBlock = ( +
+ { + this.handleBlockClick(); + }} + href={challengesWithCompleted[0].fields.slug} + > +
+ {t('misc.certification-project')} + {!isAuditedCert(curriculumLocale, superBlock) && ( + + {t('misc.translation-pending')} + + )} +
+
+ {this.renderCheckMark(isBlockCompleted)} +

{blockTitle}

+
+ {this.renderBlockIntros(blockIntroArr)} +
+
+ ); + + const blockrenderer = () => { + if (isProjectBlock) + return isNewResponsiveWebDesign ? GridProjectBlock : ProjectBlock; + return isNewResponsiveWebDesign ? GridBlock : Block; + }; + + return ( + <> + {blockrenderer()} + {isNewResponsiveWebDesign && !isProjectBlock ? null : } + ); } } diff --git a/client/src/templates/Introduction/components/challenges.tsx b/client/src/templates/Introduction/components/challenges.tsx index 6a5fb25d61..e60a9f1294 100644 --- a/client/src/templates/Introduction/components/challenges.tsx +++ b/client/src/templates/Introduction/components/challenges.tsx @@ -11,6 +11,7 @@ import { executeGA } from '../../../redux'; import { SuperBlocks } from '../../../../../config/certification-settings'; import { ExecuteGaArg } from '../../../pages/donate'; import { ChallengeWithCompletedNode } from '../../../redux/prop-types'; +import isNewRespCert from '../../../utils/is-new-responsive-web-design-cert'; const mapDispatchToProps = (dispatch: Dispatch) => bindActionCreators({ executeGA }, dispatch); @@ -46,7 +47,7 @@ function Challenges({ ); - const isGridMap = superBlock === SuperBlocks.RespWebDesignNew; + const isGridMap = isNewRespCert(superBlock); return isGridMap ? (
    diff --git a/client/src/templates/Introduction/components/legacy-links.tsx b/client/src/templates/Introduction/components/legacy-links.tsx index 4a4df993a7..dd8d28ee1b 100644 --- a/client/src/templates/Introduction/components/legacy-links.tsx +++ b/client/src/templates/Introduction/components/legacy-links.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Alert } from '@freecodecamp/react-bootstrap'; import { SuperBlocks } from '../../../../../config/certification-settings'; +import IsNewRespCert from '../../../utils/is-new-responsive-web-design-cert'; import { Link } from '../../../components/helpers'; interface LegacyLinksProps { @@ -12,7 +13,7 @@ function LegacyLinks({ superBlock }: LegacyLinksProps): JSX.Element { const { t } = useTranslation(); return ( <> - {superBlock === SuperBlocks.RespWebDesignNew && ( + {IsNewRespCert(superBlock) && (

    {t('intro:misc-text.viewing-upcoming-change')}{' '} diff --git a/client/src/templates/Introduction/intro.css b/client/src/templates/Introduction/intro.css index b53c0c3653..a5db556711 100644 --- a/client/src/templates/Introduction/intro.css +++ b/client/src/templates/Introduction/intro.css @@ -13,15 +13,16 @@ overflow-wrap: break-word; } -.block-title-wrapper { +.block-header { display: flex; justify-content: space-between; flex-direction: row; } -.block-title-wrapper .block-link { +.block-header .block-link { flex-grow: 3; flex-basis: 0; + padding: 25px 15px 10px; } .block-link:hover, @@ -30,6 +31,13 @@ background-color: var(--primary-background); } +a.cert-tag:hover, +a.cert-tag:focus, +a.cert-tag:active { + color: var(--highlight-background); + background-color: var(--highlight-color); +} + .block-link { cursor: pointer; } @@ -48,6 +56,12 @@ overflow-wrap: break-word; } +.block-grid-title { + font-size: 1.2rem; + margin: 0; + overflow-wrap: break-word; +} + .block-title-translation-cta { white-space: nowrap; padding: 0.2em 0.5em; @@ -162,9 +176,6 @@ button.map-title { background: var(--primary-background); } -.block-ui .block .big-block-title { - padding: 25px 15px 10px; -} .block-ui .block .block-description { padding: 0 15px 15px; border-bottom: 3px solid var(--secondary-background); @@ -212,6 +223,14 @@ button.map-title { transform: rotate(90deg); } +.block-grid .map-title > svg:last-child { + transform: rotate(180deg); +} + +.block-grid.open .map-title > svg:last-child { + transform: rotate(0deg); +} + .map-challenges-ul { padding-inline-start: 0; margin-bottom: 0; @@ -269,7 +288,7 @@ button.map-title { font-size: 1.17rem; } - .block-title-wrapper { + .block-header { flex-direction: column; } @@ -325,3 +344,98 @@ button.map-title { text-decoration-style: none; color: var(--secondary-color); } + +.cert-tag { + text-align: left; + width: fit-content; + font-size: 1rem; + display: block; + margin-bottom: 5px; + margin-right: 5px; + padding: 4px 10px; + color: var(--highlight-color); + background-color: var(--highlight-background); +} + +.block-grid { + border-bottom: 3px solid var(--secondary-background); +} + +.block-grid .block-header { + flex-direction: column; +} + +.block-grid .block-header { + display: flex; + background: transparent; + border: none; + text-align: left; + width: 100%; + padding: 0; + cursor: pointer; + padding: 18px 15px; +} + +.block-grid .block-header:hover { + color: var(--tertiary-color); + background-color: var(--tertiary-background); +} + +.block-grid .block-header .block-link { + flex-direction: row; +} + +.block-ui .block-grid .block-description { + border: none; + padding: 0 10px 10px; +} + +.block-grid .map-title > svg:last-child { + margin-left: auto; +} + +.block-grid .map-title > svg { + margin: 10px; +} + +.title-wrapper { + display: flex; + flex-direction: row; + align-items: center; + width: 100%; +} + +.block-grid .progress-wrapper { + width: 100%; + text-align: left; + display: flex; + align-items: center; + margin-top: 18px; + margin-bottom: 14px; +} + +.block-grid .progress-wrapper span { + color: var(--quaternary-color); +} + +.block-grid .progress { + height: 15px; + background-color: var(--secondary-background); + margin: 0 10px; + width: 80%; + border-radius: 0; +} + +.block-grid .progress-bar { + background-color: var(--blue-mid); +} + +.tags-wrapper { + display: flex; + width: 100%; + padding: 10px 10px 0px 10px; +} + +.grid-project-block { + margin-bottom: 50px; +} diff --git a/client/src/templates/Introduction/super-block-intro.tsx b/client/src/templates/Introduction/super-block-intro.tsx index 4ec567622e..c977512f45 100644 --- a/client/src/templates/Introduction/super-block-intro.tsx +++ b/client/src/templates/Introduction/super-block-intro.tsx @@ -217,7 +217,6 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => { )} superBlock={superBlock} /> - {blockDashedName !== 'project-euler' ? : null} ))} {superBlock !== SuperBlocks.CodingInterviewPrep && ( diff --git a/client/src/utils/is-new-responsive-web-design-cert.ts b/client/src/utils/is-new-responsive-web-design-cert.ts new file mode 100644 index 0000000000..b3b36e98e0 --- /dev/null +++ b/client/src/utils/is-new-responsive-web-design-cert.ts @@ -0,0 +1,5 @@ +import { SuperBlocks } from '../../../config/certification-settings'; + +export default function IsNewRespCert(superBlock: string): boolean { + return superBlock === SuperBlocks.RespWebDesignNew; +}