feat: add AB test landing page top (#55659)

Co-authored-by: Naomi the Technomancer <accounts+github@nhcarrigan.com>
Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2024-08-16 12:33:48 +03:00
committed by GitHub
parent fa818eb8bb
commit b34291fc41
10 changed files with 1525 additions and 154 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -699,6 +699,30 @@ a.patreon-button:hover {
);
}
.light-palette .gradient-foreground {
background-image: linear-gradient(
-10deg,
rgb(0, 51, 133) 35%,
rgba(237, 202, 216, 0) 75%,
rgb(150, 15, 46) 100%
),
radial-gradient(circle, rgb(157, 1, 69) 0%, rgb(0, 89, 189) 100%);
color: transparent;
background-clip: text;
}
.dark-palette .gradient-foreground {
background-image: linear-gradient(
-10deg,
rgb(223 243 255) 35%,
rgba(237, 202, 216, 0) 75%,
rgb(255 215 224) 100%
),
radial-gradient(circle, rgb(255 139 189) 0%, rgb(187 219 255) 100%);
color: transparent;
background-clip: text;
}
.supporters-background {
background-repeat: repeat;
}

View File

@@ -12,6 +12,7 @@ import {
userFetchStateSelector
} from '../../redux/selectors';
import envData from '../../../config/env.json';
import defaultGrowthBookFeatures from '../../../config/growthbook-features-default.json';
import { User, UserFetchState } from '../../redux/prop-types';
import { getUUID } from '../../utils/growthbook-cookie';
import callGA from '../../analytics/call-ga';
@@ -77,17 +78,20 @@ const GrowthBookWrapper = ({
useEffect(() => {
async function setGrowthBookFeatures() {
if (!growthbookUri) return;
try {
const res = await fetch(growthbookUri);
const data = (await res.json()) as {
features: Record<string, FeatureDefinition>;
};
growthbook.setFeatures(data.features);
} catch (e) {
// TODO: report to sentry when it's enabled
console.error(e);
if (!growthbookUri) {
// Defaults are added to facilitate testing, and avoid passing the related env
growthbook.setFeatures(defaultGrowthBookFeatures);
} else {
try {
const res = await fetch(growthbookUri);
const data = (await res.json()) as {
features: Record<string, FeatureDefinition>;
};
growthbook.setFeatures(data.features);
} catch (e) {
// TODO: report to sentry when it's enabled
console.error(e);
}
}
}

View File

@@ -0,0 +1,110 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Container, Col, Row } from '@freecodecamp/ui';
import { clientLocale } from '../../../../config/env.json';
import {
AmazonLogo,
AppleLogo,
MicrosoftLogo,
SpotifyLogo,
GoogleLogo,
TencentLogo,
AlibabaLogo
} from '../../../assets/images/components';
import { Spacer } from '../../helpers';
import BigCallToAction from './big-call-to-action';
import UIImages from './ui-images';
const LogoRow = (): JSX.Element => {
const { t } = useTranslation();
const showChineseLogos = ['chinese', 'chinese-tradition'].includes(
clientLocale
);
return (
<>
<p
className='logo-row-title'
data-playwright-test-label='landing-h2-heading-b'
>
{t('landing.h2-heading-b')}
</p>
<Spacer size='small' />
<ul
className='logo-row'
data-playwright-test-label='brand-logo-container'
>
<AppleLogo />
<GoogleLogo />
<MicrosoftLogo />
{showChineseLogos ? (
<>
<TencentLogo />
<AlibabaLogo />
</>
) : (
<>
<SpotifyLogo />
<AmazonLogo />
</>
)}
</ul>
</>
);
};
function LandingTop(): JSX.Element {
const { t } = useTranslation();
return (
<Container fluid={true} className='gradient-container'>
<Container className='landing-top'>
<Row>
<Spacer size='medium' />
<Col lg={6} sm={12} xs={12}>
<h1
id='content-start'
className='mega-heading'
data-test-label='landing-header'
>
{t('landing.big-heading-1-b')}
</h1>
<p
className='mega-heading'
data-playwright-test-label='landing-big-heading-2'
>
{t('landing.big-heading-2')}
</p>
<p
className='mega-heading'
data-playwright-test-label='landing-big-heading-3'
>
{t('landing.big-heading-3')}
</p>
<p
className='mega-heading gradient-foreground'
data-playwright-test-label='landing-big-heading-4'
>
{t('landing.big-heading-4')}
</p>
<Spacer size='medium' />
<BigCallToAction text={t('buttons.get-started')} />
</Col>
<Col lg={6}>
<UIImages />
</Col>
</Row>
<Row>
<Spacer size='large' />
<Col xs={12}>
<LogoRow />
</Col>
</Row>
</Container>
</Container>
);
}
LandingTop.displayName = 'LandingTop';
export default LandingTop;

View File

@@ -0,0 +1,27 @@
// eslint-disable-next-line filenames-simple/naming-convention
import React from 'react';
import { useTranslation } from 'react-i18next';
import Media from 'react-responsive';
import landingPageb from '../../../assets/images/landing/landing-page-b.svg';
import { LazyImage, Spacer } from '../../helpers';
const LARGE_SCREEN_SIZE = 1200;
function UIImages(): JSX.Element {
const { t } = useTranslation();
return (
<Media minWidth={LARGE_SCREEN_SIZE}>
<figure
className='ui-images'
data-playwright-test-label='landing-page-figure'
>
<LazyImage alt={t('landing.hero-img-uis')} src={landingPageb} />
</figure>
<Spacer size='exLarge' />
</Media>
);
}
UIImages.displayName = 'UIImages';
export default UIImages;

View File

@@ -1,30 +1,58 @@
import React, { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
import { useGrowthBook } from '@growthbook/growthbook-react';
import SEO from '../seo';
import { Loader } from '../helpers';
import AsSeenIn from './components/as-seen-in';
import Certifications from './components/certifications';
import LandingTop from './components/landing-top';
import LandingTopB from './components/landing-top-b';
import Testimonials from './components/testimonials';
import Faq from './components/faq';
import './landing.css';
const LandingA = () => (
<main className='landing-page'>
<LandingTop />
<AsSeenIn />
<Testimonials />
<Certifications />
<Faq />
</main>
);
const LandingB = () => (
<main className='landing-page landing-page-b'>
<LandingTopB />
<Testimonials />
<Certifications />
<Faq />
</main>
);
function Landing(): ReactElement {
const { t } = useTranslation();
return (
<>
<SEO title={t('metaTags:title')} />
<main className='landing-page'>
<LandingTop />
<AsSeenIn />
<Testimonials />
<Certifications />
<Faq />
</main>
</>
);
const growthbook = useGrowthBook();
if (growthbook && growthbook.ready) {
const showLandingPageRedesign = growthbook.getFeatureValue(
'landing-page-redesign',
false
);
return (
<>
<SEO title={t('metaTags:title')} />
{showLandingPageRedesign === true ? <LandingB /> : <LandingA />}
</>
);
} else {
return (
<>
<SEO title={t('metaTags:title')} />
<Loader fullScreen={true} />
</>
);
}
}
Landing.displayName = 'Landing';

View File

@@ -214,3 +214,54 @@ figcaption.caption {
padding: 40px;
}
}
/* AB testing styles */
.landing-page-b .mega-heading {
font-size: 2rem;
margin: 0px 0px 1rem;
font-weight: 700;
line-height: 3rem;
}
@media (min-width: 500px) {
.landing-page-b .mega-heading {
font-size: 3rem;
}
}
.landing-page-b .landing-top .btn-cta-big {
margin: 0px;
min-width: 300px;
max-width: fit-content;
background-image: none;
color: var(--primary-background) !important;
background-color: var(--primary-color);
border-color: var(--primary-color);
padding: 12px;
}
.landing-page-b .landing-top .btn-cta-big:hover,
.landing-page-b .landing-top .btn-cta-big:focus {
background-color: var(--quaternary-color) !important;
}
.landing-page-b .logo-row-title {
font-weight: normal;
}
.landing-page-b {
overflow-x: hidden;
}
figure.ui-images {
position: absolute;
left: 50%;
height: auto;
width: 750px;
top: 3vw;
}
figure.ui-images img {
width: 100%;
height: auto;
}