mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-19 04:00:56 -04:00
feat(client): add superblock intro ab test (#60685)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
This commit is contained in:
@@ -796,7 +796,15 @@
|
||||
"iframe-form-submit-alert": "Normally this form would be submitted! It works. This will be submitted to: {{externalLink}}",
|
||||
"document-notfound": "document not found",
|
||||
"slow-load-msg": "Looks like this is taking longer than usual, please try refreshing the page.",
|
||||
"navigation-warning": "If you leave this page, you will lose your progress. Are you sure?"
|
||||
"navigation-warning": "If you leave this page, you will lose your progress. Are you sure?",
|
||||
"fsd-b-description": "This comprehensive course prepares you to become a Certified Full Stack Developer. You'll learn to build complete web applications using HTML, CSS, JavaScript, React, TypeScript, Node.js, Python, and more.",
|
||||
"fsd-b-cta": "Start Learning",
|
||||
"fsd-b-benefit-1-title": "100k+ Students",
|
||||
"fsd-b-benefit-1-description": "Join more than 100k students taking this certification.",
|
||||
"fsd-b-benefit-2-title": "Professional Certification",
|
||||
"fsd-b-benefit-2-description": "Prove your skills with an official, verifiable certification.",
|
||||
"fsd-b-benefit-3-title": "500+ Exercises",
|
||||
"fsd-b-benefit-3-description": "Solidify your knowledge with plenty of practice."
|
||||
},
|
||||
"icons": {
|
||||
"gold-cup": "Gold Cup",
|
||||
|
||||
49
client/src/assets/icons/dumbbell.tsx
Normal file
49
client/src/assets/icons/dumbbell.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
function DumbbellIcon(
|
||||
props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>
|
||||
): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='62'
|
||||
height='55'
|
||||
viewBox='0 0 62 42'
|
||||
fill='none'
|
||||
{...props}
|
||||
>
|
||||
<rect y='17.2132' width='61.5396' height='7.69244' fill='#d0d0d5' />
|
||||
<rect
|
||||
x='12.8208'
|
||||
y='0.973633'
|
||||
width='8.54716'
|
||||
height='41.0264'
|
||||
fill='#d0d0d5'
|
||||
/>
|
||||
<rect
|
||||
x='39.3169'
|
||||
y='0.973633'
|
||||
width='8.54716'
|
||||
height='41.0264'
|
||||
fill='#d0d0d5'
|
||||
/>
|
||||
<rect
|
||||
x='4.14014'
|
||||
y='5.84572'
|
||||
width='8.77419'
|
||||
height='31.2822'
|
||||
fill='#d0d0d5'
|
||||
/>
|
||||
<rect
|
||||
x='47.0361'
|
||||
y='5.84572'
|
||||
width='8.77419'
|
||||
height='31.2822'
|
||||
fill='#d0d0d5'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
DumbbellIcon.displayName = 'DumbbellIcon';
|
||||
|
||||
export default DumbbellIcon;
|
||||
@@ -139,3 +139,62 @@
|
||||
.super-block-accordion .coming-soon-module {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.full-width-container {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
right: 50%;
|
||||
margin-left: -50vw;
|
||||
margin-right: -50vw;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.super-benefits-container {
|
||||
background-color: var(--gray-75);
|
||||
color: var(--gray-15);
|
||||
padding-inline: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.super-block-intro-page .btn-cta-big {
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.super-block-benefits > div {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.super-block-benefits p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.super-block-benefits {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.super-block-benefits svg path,
|
||||
.super-block-benefits p {
|
||||
fill: var(--gray-15);
|
||||
}
|
||||
|
||||
.super-block-benefits h3 {
|
||||
color: var(--gray05);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 700px) {
|
||||
.super-block-benefits {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 30px;
|
||||
}
|
||||
.super-block-benefits svg {
|
||||
margin-top: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,53 @@
|
||||
import React from 'react';
|
||||
import { graphql, useStaticQuery } from 'gatsby';
|
||||
import { useTranslation, Trans } from 'react-i18next';
|
||||
import { Alert, Spacer } from '@freecodecamp/ui';
|
||||
import { Alert, Spacer, Container, Row, Col } from '@freecodecamp/ui';
|
||||
import { ConnectedProps, connect } from 'react-redux';
|
||||
import { useFeatureIsOn } from '@growthbook/growthbook-react';
|
||||
import { SuperBlocks } from '../../../../../shared/config/curriculum';
|
||||
import { SuperBlockIcon } from '../../../assets/superblock-icon';
|
||||
import { Link } from '../../../components/helpers';
|
||||
import CapIcon from '../../../assets/icons/cap';
|
||||
import DumbbellIcon from '../../../assets/icons/dumbbell';
|
||||
import CommunityIcon from '../../../assets/icons/community';
|
||||
import { CompletedChallenge } from '../../../redux/prop-types';
|
||||
import { completedChallengesSelector } from '../../../redux/selectors';
|
||||
|
||||
interface SuperBlockIntroProps {
|
||||
interface SuperBlockIntroQueryData {
|
||||
challengeNode: {
|
||||
challenge: {
|
||||
fields: {
|
||||
slug: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
type ReduxProps = ConnectedProps<typeof connector>;
|
||||
|
||||
interface ConditionalDonationAlertProps {
|
||||
superBlock: SuperBlocks;
|
||||
onCertificationDonationAlertClick: () => void;
|
||||
isDonating: boolean;
|
||||
}
|
||||
|
||||
interface SuperBlockIntroProps
|
||||
extends ConditionalDonationAlertProps,
|
||||
ReduxProps {}
|
||||
|
||||
const mapStateToProps = (state: unknown) => ({
|
||||
completedChallenges: completedChallengesSelector(
|
||||
state
|
||||
) as CompletedChallenge[]
|
||||
});
|
||||
|
||||
const connector = connect(mapStateToProps);
|
||||
|
||||
export const ConditionalDonationAlert = ({
|
||||
superBlock,
|
||||
onCertificationDonationAlertClick,
|
||||
isDonating
|
||||
}: SuperBlockIntroProps): JSX.Element | null => {
|
||||
}: ConditionalDonationAlertProps): JSX.Element | null => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const betaCertifications: SuperBlocks[] = [];
|
||||
@@ -63,9 +95,13 @@ export const ConditionalDonationAlert = ({
|
||||
return null;
|
||||
};
|
||||
|
||||
function SuperBlockIntro(props: SuperBlockIntroProps): JSX.Element {
|
||||
function SuperBlockIntro({
|
||||
superBlock,
|
||||
onCertificationDonationAlertClick,
|
||||
isDonating,
|
||||
completedChallenges
|
||||
}: SuperBlockIntroProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const { superBlock, onCertificationDonationAlertClick, isDonating } = props;
|
||||
|
||||
const superBlockIntroObj: {
|
||||
title: string;
|
||||
@@ -77,13 +113,37 @@ function SuperBlockIntro(props: SuperBlockIntroProps): JSX.Element {
|
||||
note: string;
|
||||
};
|
||||
|
||||
const {
|
||||
challengeNode: {
|
||||
challenge: {
|
||||
fields: { slug: firstChallengeSlug }
|
||||
}
|
||||
}
|
||||
} = useStaticQuery<SuperBlockIntroQueryData>(graphql`
|
||||
query SuperBlockIntroQuery {
|
||||
challengeNode(
|
||||
challenge: {
|
||||
superOrder: { eq: 0 }
|
||||
order: { eq: 0 }
|
||||
challengeOrder: { eq: 0 }
|
||||
}
|
||||
) {
|
||||
challenge {
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const {
|
||||
title: i18nSuperBlock,
|
||||
intro: superBlockIntroText,
|
||||
note: superBlockNoteText
|
||||
} = superBlockIntroObj;
|
||||
|
||||
return (
|
||||
const introTopA = (
|
||||
<>
|
||||
<h1 id='content-start' className='text-center big-heading'>
|
||||
{i18nSuperBlock}
|
||||
@@ -99,6 +159,74 @@ function SuperBlockIntro(props: SuperBlockIntroProps): JSX.Element {
|
||||
{superBlockNoteText}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const introTopB = (
|
||||
<>
|
||||
<SuperBlockIcon className='cert-header-icon' superBlock={superBlock} />
|
||||
<Spacer size='m' />
|
||||
<h1 id='content-start' className='text-center big-heading'>
|
||||
{i18nSuperBlock}
|
||||
</h1>
|
||||
<Spacer size='m' />
|
||||
<p>{t('misc.fsd-b-description')}</p>
|
||||
{superBlockNoteText && (
|
||||
<div className='alert alert-info' style={{ marginTop: '2rem' }}>
|
||||
{superBlockNoteText}
|
||||
</div>
|
||||
)}
|
||||
<Spacer size='s' />
|
||||
<a
|
||||
className={'btn-cta-big btn-block signup-btn btn-cta'}
|
||||
href={firstChallengeSlug}
|
||||
>
|
||||
{t('misc.fsd-b-cta')}
|
||||
</a>
|
||||
<Spacer size='l' />
|
||||
<Container
|
||||
fluid={true}
|
||||
className='full-width-container super-benefits-container'
|
||||
>
|
||||
<Container>
|
||||
<Row>
|
||||
<Col xs={12} className='super-block-benefits'>
|
||||
<div>
|
||||
<CommunityIcon />
|
||||
<div className='benefit-text'>
|
||||
<h3>{t('misc.fsd-b-benefit-1-title')}</h3>
|
||||
<p>{t('misc.fsd-b-benefit-1-description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<CapIcon />
|
||||
<div className='benefit-text'>
|
||||
<h3>{t('misc.fsd-b-benefit-2-title')}</h3>
|
||||
<p>{t('misc.fsd-b-benefit-2-description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<DumbbellIcon />
|
||||
<div className='benefit-text'>
|
||||
<h3>{t('misc.fsd-b-benefit-3-title')}</h3>
|
||||
<p>{t('misc.fsd-b-benefit-3-description')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</Container>
|
||||
<Spacer size='l' />
|
||||
</>
|
||||
);
|
||||
|
||||
const showFSDnewIntro = useFeatureIsOn('fsd-new-intro');
|
||||
|
||||
const showIntroTopB = completedChallenges.length === 0 && showFSDnewIntro;
|
||||
|
||||
return (
|
||||
<>
|
||||
{showIntroTopB ? introTopB : introTopA}
|
||||
<ConditionalDonationAlert
|
||||
superBlock={superBlock}
|
||||
onCertificationDonationAlertClick={onCertificationDonationAlertClick}
|
||||
@@ -110,4 +238,4 @@ function SuperBlockIntro(props: SuperBlockIntroProps): JSX.Element {
|
||||
|
||||
SuperBlockIntro.displayName = 'SuperBlockIntro';
|
||||
|
||||
export default SuperBlockIntro;
|
||||
export default connector(SuperBlockIntro);
|
||||
|
||||
Reference in New Issue
Block a user