chore: add 15% discount incentive to increase registrations. (#426)
This commit is contained in:
39
package-lock.json
generated
39
package-lock.json
generated
@@ -3711,9 +3711,9 @@
|
||||
}
|
||||
},
|
||||
"@edx/paragon": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-16.6.1.tgz",
|
||||
"integrity": "sha512-OpfcWhNsgcTfJgxuHm5yZ35FLOPddinENRVd/SzkmUuUYjqsvxlJW5T53yN2OZctP770nvim1qmFAHm+Zyzxhg==",
|
||||
"version": "16.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-16.10.0.tgz",
|
||||
"integrity": "sha512-D8QTEf1K/9KNvxguPTpCqErbSAglTCIVsqvHa5T25+345E0o6gexX/FsJVSnT8ZUy3YAXdaPA6FFxhwEXjxPMQ==",
|
||||
"requires": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||
@@ -8449,6 +8449,16 @@
|
||||
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||
"dev": true
|
||||
},
|
||||
"clipboard": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
|
||||
"integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
|
||||
"requires": {
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
@@ -9710,6 +9720,11 @@
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"dev": true
|
||||
},
|
||||
"delegate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
@@ -13363,6 +13378,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"good-listener": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||
"requires": {
|
||||
"delegate": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz",
|
||||
@@ -22340,6 +22363,11 @@
|
||||
"commander": "^2.8.1"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
|
||||
},
|
||||
"select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@@ -24296,6 +24324,11 @@
|
||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
|
||||
"dev": true
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||
},
|
||||
"tiny-invariant": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
|
||||
|
||||
@@ -37,13 +37,14 @@
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/frontend-component-cookie-policy-banner": "2.1.12",
|
||||
"@edx/frontend-platform": "1.12.4",
|
||||
"@edx/paragon": "16.6.1",
|
||||
"@edx/paragon": "16.10.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.32",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.1",
|
||||
"@fortawesome/react-fontawesome": "0.1.13",
|
||||
"classnames": "2.2.6",
|
||||
"clipboard": "^2.0.8",
|
||||
"core-js": "3.9.1",
|
||||
"extract-react-intl-messages": "4.1.1",
|
||||
"fastest-levenshtein": "1.0.12",
|
||||
|
||||
@@ -351,6 +351,10 @@ select.form-control {
|
||||
height: 282px;
|
||||
}
|
||||
|
||||
.variation1-medium-screen {
|
||||
height: 300px !important;
|
||||
}
|
||||
|
||||
.medium-screen-svg-light,
|
||||
.medium-screen-svg-primary {
|
||||
fill: $light-200;
|
||||
@@ -486,6 +490,14 @@ select.form-control {
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
.variation1-bar-color {
|
||||
stroke: $brand !important;
|
||||
}
|
||||
|
||||
.variation2-bar-color {
|
||||
stroke: $accent-a !important;
|
||||
}
|
||||
|
||||
.medium-screen-svg-line {
|
||||
padding-top: 0.5rem;
|
||||
stroke: $accent-b;
|
||||
@@ -670,6 +682,15 @@ select.form-control {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.discount-banner {
|
||||
background-color: #03C7E8;
|
||||
}
|
||||
|
||||
.dashed-border {
|
||||
border-style: dashed;
|
||||
border-width: thin;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.mw-500 {
|
||||
@@ -706,6 +727,12 @@ select.form-control {
|
||||
flex-direction:column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dashed-border {
|
||||
border-style: dashed;
|
||||
border-width: thin;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1199px) and (min-width: 768px) {
|
||||
@@ -759,6 +786,12 @@ select.form-control {
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 550px) {
|
||||
.variation2-text-alignment {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
// Smaller than Extra Small (Mobile Screens)
|
||||
@media (max-width: 464px) {
|
||||
.btn-social {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -16,12 +16,23 @@ import SmallLayout from './SmallLayout';
|
||||
import AuthExtraLargeLayout from './AuthExtraLargeLayout';
|
||||
import AuthMediumLayout from './AuthMediumLayout';
|
||||
import AuthSmallLayout from './AuthSmallLayout';
|
||||
import DiscountExperimentBanner from './DiscountBanner';
|
||||
|
||||
const BaseComponent = ({ children, showWelcomeBanner }) => {
|
||||
const BaseComponent = ({ children, isRegistrationPage, showWelcomeBanner }) => {
|
||||
const authenticatedUser = showWelcomeBanner ? getAuthenticatedUser() : null;
|
||||
const [optimizelyExperimentName, setOptimizelyExperimentName] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const { experimentName } = window;
|
||||
|
||||
if (experimentName) {
|
||||
setOptimizelyExperimentName(experimentName);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{isRegistrationPage && optimizelyExperimentName === 'variation2' ? <DiscountExperimentBanner /> : null}
|
||||
<CookiePolicyBanner languageCode={getLocale()} />
|
||||
<ExtraLarge>
|
||||
<div className="col-md-12 extra-large-screen-top-stripe" />
|
||||
@@ -33,25 +44,37 @@ const BaseComponent = ({ children, showWelcomeBanner }) => {
|
||||
<div className={classNames('layout', { authenticated: authenticatedUser })}>
|
||||
<ExtraSmall>
|
||||
<div className="col-md-12 small-screen-top-stripe" />
|
||||
{authenticatedUser ? <AuthSmallLayout variant="xs" username={authenticatedUser.username} /> : <SmallLayout />}
|
||||
{authenticatedUser ? <AuthSmallLayout variant="xs" username={authenticatedUser.username} /> : (
|
||||
<SmallLayout experimentName={optimizelyExperimentName} isRegistrationPage={isRegistrationPage} />
|
||||
)}
|
||||
</ExtraSmall>
|
||||
<Small>
|
||||
<div className="col-md-12 small-screen-top-stripe" />
|
||||
{authenticatedUser ? <AuthSmallLayout username={authenticatedUser.username} /> : <SmallLayout />}
|
||||
{authenticatedUser ? <AuthSmallLayout username={authenticatedUser.username} /> : (
|
||||
<SmallLayout experimentName={optimizelyExperimentName} isRegistrationPage={isRegistrationPage} />
|
||||
)}
|
||||
</Small>
|
||||
<Medium>
|
||||
<div className="w-100 medium-screen-top-stripe" />
|
||||
{authenticatedUser ? <AuthMediumLayout username={authenticatedUser.username} /> : <MediumLayout />}
|
||||
{authenticatedUser ? <AuthMediumLayout username={authenticatedUser.username} /> : (
|
||||
<MediumLayout experimentName={optimizelyExperimentName} isRegistrationPage={isRegistrationPage} />
|
||||
)}
|
||||
</Medium>
|
||||
<Large>
|
||||
<div className="w-100 large-screen-top-stripe" />
|
||||
{authenticatedUser ? <AuthMediumLayout username={authenticatedUser.username} /> : <MediumLayout />}
|
||||
{authenticatedUser ? <AuthMediumLayout username={authenticatedUser.username} /> : (
|
||||
<MediumLayout experimentName={optimizelyExperimentName} isRegistrationPage={isRegistrationPage} />
|
||||
)}
|
||||
</Large>
|
||||
<ExtraLarge>
|
||||
{authenticatedUser ? <AuthExtraLargeLayout username={authenticatedUser.username} /> : <LargeLayout />}
|
||||
{authenticatedUser ? <AuthExtraLargeLayout username={authenticatedUser.username} /> : (
|
||||
<LargeLayout experimentName={optimizelyExperimentName} isRegistrationPage={isRegistrationPage} />
|
||||
)}
|
||||
</ExtraLarge>
|
||||
<ExtraExtraLarge>
|
||||
{authenticatedUser ? <AuthExtraLargeLayout variant="xxl" username={authenticatedUser.username} /> : <LargeLayout />}
|
||||
{authenticatedUser ? <AuthExtraLargeLayout variant="xxl" username={authenticatedUser.username} /> : (
|
||||
<LargeLayout experimentName={optimizelyExperimentName} isRegistrationPage={isRegistrationPage} />
|
||||
)}
|
||||
</ExtraExtraLarge>
|
||||
|
||||
<div className={classNames('content', { 'align-items-center mt-0': authenticatedUser })}>
|
||||
@@ -63,11 +86,13 @@ const BaseComponent = ({ children, showWelcomeBanner }) => {
|
||||
};
|
||||
|
||||
BaseComponent.defaultProps = {
|
||||
isRegistrationPage: false,
|
||||
showWelcomeBanner: false,
|
||||
};
|
||||
|
||||
BaseComponent.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
isRegistrationPage: PropTypes.bool,
|
||||
showWelcomeBanner: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
||||
46
src/base-component/DiscountBanner.jsx
Normal file
46
src/base-component/DiscountBanner.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import ClipboardJS from 'clipboard';
|
||||
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { faCut } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import { PageBanner } from '@edx/paragon';
|
||||
|
||||
export default function DiscountExperimentBanner() {
|
||||
const [show, setShow] = useState(true);
|
||||
new ClipboardJS('.copyIcon'); // eslint-disable-line no-new
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageBanner
|
||||
show={show}
|
||||
dismissible
|
||||
onDismiss={() => { setShow(false); }}
|
||||
>
|
||||
<span className="text-primary-700 small variation2-text-alignment">
|
||||
<span className="mr-1">
|
||||
<FormattedMessage
|
||||
id="discount.message.body"
|
||||
defaultMessage="Get {discount} your first verified certificate* with code"
|
||||
description="Message body for edX discount"
|
||||
values={{
|
||||
discount: <strong> 15% off </strong>,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<span className="dashed-border p-1 d-inline-flex flex-wrap align-items-center">
|
||||
<span id="edx-welcome" className="font-weight-bold">EDXWELCOME</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-dark-200 copyIcon ml-2"
|
||||
icon={faCut}
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#edx-welcome"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</PageBanner>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { Hyperlink, Image } from '@edx/paragon';
|
||||
|
||||
import LargeScreenLeftLayout from './LargeLeftLayout';
|
||||
|
||||
const LargeLayout = () => (
|
||||
const LargeLayout = ({ experimentName, isRegistrationPage }) => (
|
||||
<div className="container row p-0 m-0 large-screen-container">
|
||||
<div className="col-md-9 p-0 screen-header-primary">
|
||||
<Hyperlink destination={getConfig().MARKETING_SITE_BASE_URL}>
|
||||
<Image alt="edx" className="logo position-absolute" src={getConfig().LOGO_WHITE_URL} />
|
||||
</Hyperlink>
|
||||
<LargeScreenLeftLayout />
|
||||
<LargeScreenLeftLayout experimentName={experimentName} isRegistrationPage={isRegistrationPage} />
|
||||
</div>
|
||||
<div className="col-md-3 p-0 screen-polygon">
|
||||
<svg
|
||||
@@ -28,4 +29,14 @@ const LargeLayout = () => (
|
||||
</div>
|
||||
);
|
||||
|
||||
LargeLayout.defaultProps = {
|
||||
experimentName: '',
|
||||
isRegistrationPage: false,
|
||||
};
|
||||
|
||||
LargeLayout.propTypes = {
|
||||
experimentName: PropTypes.string,
|
||||
isRegistrationPage: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default LargeLayout;
|
||||
|
||||
@@ -1,30 +1,71 @@
|
||||
import React from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import PropTypes from 'prop-types';
|
||||
import ClipboardJS from 'clipboard';
|
||||
|
||||
import { faCut } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
const LargeLeftLayout = (props) => {
|
||||
const { intl } = props;
|
||||
|
||||
const { intl, isRegistrationPage, experimentName } = props;
|
||||
new ClipboardJS('.copyIcon'); // eslint-disable-line no-new
|
||||
return (
|
||||
<div className="min-vh-100 pr-0 mt-lg-n2 d-flex align-items-center">
|
||||
<svg className="large-screen-svg-line ml-5">
|
||||
<svg className={classNames(
|
||||
'large-screen-svg-line',
|
||||
{
|
||||
'variation1-bar-color mt-n5 pt-0': experimentName === 'variation1' && isRegistrationPage,
|
||||
'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage,
|
||||
'ml-5': experimentName !== 'variation1' || !isRegistrationPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<line x1="50" y1="0" x2="10" y2="215" />
|
||||
</svg>
|
||||
<h1 className="large-heading">
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<span className="text-accent-a"><br />
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
<div className={classNames({ 'pl-4': experimentName === 'variation1' && isRegistrationPage })}>
|
||||
<h1 className="large-heading">
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<span
|
||||
className={((experimentName === 'variation1' || experimentName === 'variation2') && isRegistrationPage) ? 'text-accent-b' : 'text-accent-a'}
|
||||
>
|
||||
<br />
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
{experimentName === 'variation1' && isRegistrationPage ? (
|
||||
<span className="text-light-300">
|
||||
<span className="lead mr-2.5">Get <span className="text-accent-a h2">15% off</span> your first verified <br /> certificate* with code</span>
|
||||
<span className="dashed-border d-inline-flex flex-wrap align-items-center">
|
||||
<span id="edx-welcome" className="text-white edx-welcome font-weight-bold mr-1">EDXWELCOME</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-light-700 copyIcon ml-1.5"
|
||||
icon={faCut}
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#edx-welcome"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
LargeLeftLayout.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
experimentName: PropTypes.string,
|
||||
isRegistrationPage: PropTypes.bool,
|
||||
};
|
||||
|
||||
LargeLeftLayout.defaultProps = {
|
||||
experimentName: '',
|
||||
isRegistrationPage: false,
|
||||
};
|
||||
|
||||
export default injectIntl(LargeLeftLayout);
|
||||
|
||||
@@ -1,31 +1,74 @@
|
||||
import React from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { Hyperlink, Image } from '@edx/paragon';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import ClipboardJS from 'clipboard';
|
||||
|
||||
import { faCut } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import messages from './messages';
|
||||
|
||||
const MediumLayout = (props) => {
|
||||
const { intl } = props;
|
||||
const { intl, isRegistrationPage, experimentName } = props;
|
||||
new ClipboardJS('.copyIcon'); // eslint-disable-line no-new
|
||||
|
||||
return (
|
||||
<div className="container row p-0 mb-3 medium-screen-container">
|
||||
<div className={classNames(
|
||||
'container row p-0 mb-3 medium-screen-container',
|
||||
{
|
||||
'variation1-medium-screen': experimentName === 'variation1' && isRegistrationPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div className="col-md-10 p-0 screen-header-primary">
|
||||
<Hyperlink destination={getConfig().MARKETING_SITE_BASE_URL}>
|
||||
<Image alt="edx" className="logo" src={getConfig().LOGO_WHITE_URL} />
|
||||
</Hyperlink>
|
||||
<div className="row mt-4 justify-content-center">
|
||||
<svg className="medium-screen-svg-line pl-5">
|
||||
<svg className={classNames(
|
||||
'medium-screen-svg-line pl-5',
|
||||
{
|
||||
'variation1-bar-color': experimentName === 'variation1' && isRegistrationPage,
|
||||
'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<line x1="50" y1="0" x2="10" y2="215" />
|
||||
</svg>
|
||||
<h1 className="medium-heading pb-4">
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<span className="text-accent-a"><br />
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
<div className="pb-4">
|
||||
<h1 className="medium-heading">
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<span
|
||||
className={((experimentName === 'variation1' || experimentName === 'variation2') && isRegistrationPage) ? 'text-accent-b' : 'text-accent-a'}
|
||||
>
|
||||
<br />
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
{experimentName === 'variation1' && isRegistrationPage ? (
|
||||
<div className="text-light-300 pl-3">
|
||||
<span className="mr-1.5">
|
||||
Get <span className="text-accent-a h3">15% off</span> your first verified <br />certificate* with code
|
||||
</span>
|
||||
<span className="dashed-border h5 text-white">
|
||||
<span id="edx-welcome" className="edx-welcome">EDXWELCOME </span>
|
||||
<FontAwesomeIcon
|
||||
className="text-light-700 copyIcon ml-1"
|
||||
icon={faCut}
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#edx-welcome"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
</div>
|
||||
<div className="col-md-2 p-0 screen-polygon">
|
||||
<svg width="100%" height="100%" className="medium-screen-svg-primary" preserveAspectRatio="xMaxYMin meet">
|
||||
@@ -40,6 +83,13 @@ const MediumLayout = (props) => {
|
||||
|
||||
MediumLayout.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
experimentName: PropTypes.string,
|
||||
isRegistrationPage: PropTypes.bool,
|
||||
};
|
||||
|
||||
MediumLayout.defaultProps = {
|
||||
experimentName: '',
|
||||
isRegistrationPage: false,
|
||||
};
|
||||
|
||||
export default injectIntl(MediumLayout);
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { Hyperlink, Image } from '@edx/paragon';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import ClipboardJS from 'clipboard';
|
||||
|
||||
import { faCut } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import messages from './messages';
|
||||
|
||||
const SmallLayout = (props) => {
|
||||
const { intl } = props;
|
||||
|
||||
const { intl, isRegistrationPage, experimentName } = props;
|
||||
new ClipboardJS('.copyIcon'); // eslint-disable-line no-new
|
||||
return (
|
||||
<>
|
||||
<div className="small-screen-header-primary">
|
||||
@@ -16,16 +24,41 @@ const SmallLayout = (props) => {
|
||||
<Image alt="edx" className="logo" src={getConfig().LOGO_WHITE_URL} />
|
||||
</Hyperlink>
|
||||
<div className="d-flex mt-3">
|
||||
<svg className="small-screen-svg-line">
|
||||
<svg className={classNames(
|
||||
'small-screen-svg-line',
|
||||
{
|
||||
'variation1-bar-color': experimentName === 'variation1' && isRegistrationPage,
|
||||
'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<line x1="55" y1="0" x2="40" y2="65" />
|
||||
</svg>
|
||||
<h1 className="small-heading pb-3">
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<br />
|
||||
<span className="text-accent-a">
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
<div className="pb-3">
|
||||
<h1 className="small-heading">
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<br />
|
||||
<span
|
||||
className={((experimentName === 'variation1' || experimentName === 'variation2') && isRegistrationPage) ? 'text-accent-b' : 'text-accent-a'}
|
||||
>
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
{(experimentName === 'variation1' && isRegistrationPage) ? (
|
||||
<div className="small text-light-300 pl-2">
|
||||
<span className="mr-1.5">Get <span className="text-accent-a h3">15% off</span> your first verified <br />certificate* with code</span>
|
||||
<span className="dashed-border h6 text-white d-inline-flex flex-wrap align-items-center">
|
||||
<span id="edx-welcome" className="edx-welcome mr-1">EDXWELCOME</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-light-700 copyIcon ml-1"
|
||||
icon={faCut}
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#edx-welcome"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -34,6 +67,14 @@ const SmallLayout = (props) => {
|
||||
|
||||
SmallLayout.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
experimentName: PropTypes.string,
|
||||
isRegistrationPage: PropTypes.bool,
|
||||
};
|
||||
|
||||
SmallLayout.defaultProps = {
|
||||
experimentName: '',
|
||||
isRegistrationPage: false,
|
||||
|
||||
};
|
||||
|
||||
export default injectIntl(SmallLayout);
|
||||
|
||||
@@ -52,7 +52,7 @@ const Logistration = (props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseComponent>
|
||||
<BaseComponent isRegistrationPage={selectedPage === REGISTER_PAGE}>
|
||||
<div>
|
||||
{institutionLogin
|
||||
? (
|
||||
|
||||
@@ -145,7 +145,7 @@ describe('LoginPage', () => {
|
||||
|
||||
it('should match default button state', () => {
|
||||
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||
expect(loginPage.find('button[type="submit"] span').first().text()).toEqual('Sign in');
|
||||
expect(loginPage.find('button[type="submit"] span').first().text()).toEqual(' Sign in ');
|
||||
});
|
||||
|
||||
it('should match pending button state', () => {
|
||||
@@ -160,7 +160,7 @@ describe('LoginPage', () => {
|
||||
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||
const button = loginPage.find('button[type="submit"] span').first();
|
||||
|
||||
expect(button.find('.sr-only').text()).toEqual('pending');
|
||||
expect(button.find('.sr-only').text()).toEqual(' pending ');
|
||||
});
|
||||
|
||||
it('should show forgot password link', () => {
|
||||
|
||||
@@ -657,6 +657,13 @@ class RegistrationPage extends React.Component {
|
||||
thirdPartyAuthApiStatus,
|
||||
intl)}
|
||||
</Form>
|
||||
{(this.state.optimizelyExperimentName === 'variation1' || this.state.optimizelyExperimentName === 'variation2')
|
||||
? (
|
||||
<div id="certificate-msg" className="mt-4 micro text-gray-500">
|
||||
{intl.formatMessage(messages['certificate.msg'])}
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -270,6 +270,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Did you mean',
|
||||
description: 'Did you mean alert suggestion',
|
||||
},
|
||||
'certificate.msg': {
|
||||
id: 'certificate.msg',
|
||||
defaultMessage: '*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.',
|
||||
description: 'Text for the 15% discount experiment',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -405,7 +405,7 @@ describe('RegistrationPage', () => {
|
||||
|
||||
it('should match default button state', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual('Create an account');
|
||||
expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual(' Create an account ');
|
||||
});
|
||||
|
||||
it('should match pending button state', () => {
|
||||
@@ -420,7 +420,7 @@ describe('RegistrationPage', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
const button = registrationPage.find('button[type="submit"] span').first();
|
||||
|
||||
expect(button.find('.sr-only').text()).toEqual('pending');
|
||||
expect(button.find('.sr-only').text()).toEqual(' pending ');
|
||||
});
|
||||
|
||||
it('should show single sign on provider button', () => {
|
||||
|
||||
Reference in New Issue
Block a user