diff --git a/src/courseware/course/sequence/Unit.jsx b/src/courseware/course/sequence/Unit.jsx
index 17bce39e..7a2e785a 100644
--- a/src/courseware/course/sequence/Unit.jsx
+++ b/src/courseware/course/sequence/Unit.jsx
@@ -18,6 +18,7 @@ import { processEvent } from '../../../course-home/data/thunks';
import { fetchCourse } from '../../data/thunks';
const LockPaywall = React.lazy(() => import('./lock-paywall'));
+const LockPaywallValuePropExperiment = React.lazy(() => import('./lock-paywall-value-prop'));
/**
* We discovered an error in Firefox where - upon iframe load - React would cease to call any
@@ -66,6 +67,14 @@ function Unit({
const [iframeHeight, setIframeHeight] = useState(0);
const [hasLoaded, setHasLoaded] = useState(false);
const [modalOptions, setModalOptions] = useState({ open: false });
+ const [rev1512ValuePropExperimentLock, setRev1512ValuePropExperimentLock] = useState(
+ window.rev1512ValuePropExperimentLock,
+ );
+ /* TODO: The code block below + code referencing it should be deleted after REV1512 value prop experiment */
+ window.rev1512ToggleValuePropPaywallLock = () => {
+ window.rev1512ValuePropExperimentLock = !rev1512ValuePropExperimentLock;
+ setRev1512ValuePropExperimentLock(!rev1512ValuePropExperimentLock);
+ };
const unit = useModel('units', id);
const course = useModel('courses', courseId);
@@ -125,9 +134,9 @@ function Unit({
/>
)}
>
-
+ {(rev1512ValuePropExperimentLock)
+ ?
+ : }
)}
{!hasLoaded && (
diff --git a/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.jsx b/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.jsx
new file mode 100644
index 00000000..19893d99
--- /dev/null
+++ b/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.jsx
@@ -0,0 +1,235 @@
+/* TODO: This file should be deleted after REV1512 value prop experiment */
+import React from 'react';
+import PropTypes from 'prop-types';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faLock } from '@fortawesome/free-solid-svg-icons';
+import { faCheckCircle } from '@fortawesome/free-regular-svg-icons';
+import {
+ injectIntl, getLocale,
+} from '@edx/frontend-platform/i18n';
+import { Button } from '@edx/paragon';
+import classNames from 'classnames';
+
+import VerifiedCert from '../../../../generic/assets/edX_verified_certificate.png';
+import { useModel } from '../../../../generic/model-store';
+import './LockPaywall.scss';
+
+function LockPaywall({
+ courseId,
+}) {
+ const course = useModel('courses', courseId);
+ const {
+ verifiedMode,
+ } = course;
+ if (!verifiedMode) {
+ return null;
+ }
+ const {
+ currencySymbol,
+ price,
+ upgradeUrl,
+ } = verifiedMode;
+
+ const isSpanish = getLocale() === 'es-419';
+
+ let upgradeButtonText;
+
+ if (document.querySelector('.price.discount') !== null) {
+ let discountPrice = document.querySelector('.price.discount').textContent;
+ if (discountPrice !== null) {
+ discountPrice = discountPrice.replace(/[^0-9.]/g, '');
+ }
+
+ if (isSpanish) {
+ upgradeButtonText = (
+ <>
+
+ Cómpralo por {currencySymbol}{discountPrice}
+
+
+ ({currencySymbol}{price})
+
+ >
+ );
+ } else {
+ upgradeButtonText = (
+ <>
+
+ Upgrade for {currencySymbol}{discountPrice}
+
+
+ ({currencySymbol}{price})
+
+ >
+ );
+ }
+ } else if (isSpanish) {
+ upgradeButtonText = (
+ <>
+
+ Cómpralo por {currencySymbol}{price}
+
+ >
+ );
+ } else {
+ upgradeButtonText = (
+ <>
+
+ Upgrade for {currencySymbol}{price}
+
+ >
+ );
+ }
+
+ const circleCheckIcon = (
+
+ );
+ const verifiedCertificateLink = (
+
+
+ { (isSpanish) ? 'certificado verificado' : 'verified certificate' }
+
+
+ );
+
+ const userAgent = typeof window.navigator === 'undefined' ? '' : navigator.userAgent;
+ const isMobile = Boolean(
+ userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WPDesktop/i),
+ );
+
+ return (
+
+
+
+
+
+ {
+ isSpanish
+ ? 'Las tareas calificadas están bloqueadas'
+ : 'Graded assignments are locked'
+ }
+
+
+
+ {
+ isSpanish
+ ? 'Cámbiate a la opción verificada para obtener acceso a funciones bloqueadas como esta y aprovechar al máximo tu curso.'
+ : 'Upgrade to gain access to locked features like this one and get the most out of your course.'
+ }
+
+
+
+
+
+
+
+
+ {
+ isSpanish
+ ? 'Cuando te cambias a la opción verificada, tú:'
+ : 'When you upgrade, you:'
+ }
+
+
+
+
{circleCheckIcon}
+
+
+ {
+ isSpanish
+ ? <>Obtén un {verifiedCertificateLink} de finalización para compartirlo en tu currículum>
+ : <>Earn a {verifiedCertificateLink} of completion to showcase on your resume>
+ }
+
+
+
+
+
+
{circleCheckIcon}
+
+ {
+ isSpanish
+ ? 'Desbloquea el acceso a todas las actividades del curso, incluidas las '
+ : 'Unlock access to all course activities, including '
+ }
+
+ {
+ isSpanish
+ ? 'tareas calificadas'
+ : 'graded assignments'
+ }
+
+
+
+
+
+
{circleCheckIcon}
+
+
+ {
+ isSpanish
+ ? 'Acceso completo'
+ : 'Full access'
+ }
+
+ {
+ isSpanish
+ ? ' al contenido y los materiales del curso, incluso después de que finalice el curso'
+ : ' to course content and materials, even after the course ends'
+ }
+
+
+
+
+
{circleCheckIcon}
+
+ {
+ isSpanish
+ ? 'Apoya nuestra '
+ : 'Support our '
+ }
+
+ {
+ isSpanish
+ ? 'misión sin fines de lucro'
+ : 'non-profit mission'
+ }
+
+ {
+ isSpanish
+ ? ' en edX'
+ : ' at edX'
+ }
+
+
+
+
+
+
+
+ {upgradeButtonText}
+
+
+
+
+ );
+}
+LockPaywall.propTypes = {
+ courseId: PropTypes.string.isRequired,
+};
+export default injectIntl(LockPaywall);
diff --git a/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.scss b/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.scss
new file mode 100644
index 00000000..336914b6
--- /dev/null
+++ b/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.scss
@@ -0,0 +1,69 @@
+/* TODO: This file should be deleted after REV1512 value prop experiment */
+.value-prop-lock-paywall-banner {
+ background-color: #f8f9fa;
+
+ .list-item-row {
+ display: flex;
+ margin-left: -7px;
+ }
+ .certificate-image-banner {
+ width: 166px;
+ height: 119px;
+ margin-top: 3px;
+ }
+ .certificate-image-banner-container {
+ display: inline-block;
+ float: left;
+ padding-left: 20px;
+ flex-shrink: 0;
+ }
+ .top-banner-text {
+ margin-bottom: 16px;
+ padding-left: 20px;
+ }
+ .top-banner-text-header {
+ padding-left: 20px;
+ }
+ .value-prop-upgrade-button-container {
+ padding-left: 20px;
+ }
+ .is-mobile {
+ padding-top: 10px;
+
+ .certificate-image-banner-container {
+ text-align: center;
+ display: block;
+ float: none;
+ padding-left: 0px;
+ }
+ .certificate-image-banner {
+ width: 180px;
+ height: 128px;
+ margin-top: 3px;
+ margin-bottom: 5px;
+ }
+ .top-banner-text-header {
+ margin-left:36px;
+ position: relative;
+ padding-left: 10px;
+ }
+ .top-banner-text {
+ margin-left:36px;
+ margin-bottom: 4px;
+ position: relative;
+ padding-left: 10px;
+ }
+ .lock-icon {
+ position: absolute;
+ left: -28px;
+ top: 4px;
+ }
+ .value-prop-upgrade-button-container {
+ text-align: center;
+ padding-bottom: 20px;
+ padding-top: 20px;
+ padding-left: 0px;
+ }
+ }
+}
+
diff --git a/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.test.jsx b/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.test.jsx
new file mode 100644
index 00000000..2666f96c
--- /dev/null
+++ b/src/courseware/course/sequence/lock-paywall-value-prop/LockPaywall.test.jsx
@@ -0,0 +1,40 @@
+/* TODO: This file should be deleted after REV1512 value prop experiment */
+import React from 'react';
+import { Factory } from 'rosie';
+import { initializeTestStore, render, screen } from '../../../../setupTest';
+import LockPaywall from './LockPaywall';
+
+describe('Lock Paywall', () => {
+ let store;
+ const mockData = {};
+
+ beforeAll(async () => {
+ store = await initializeTestStore();
+ const { courseware } = store.getState();
+ mockData.courseId = courseware.courseId;
+ });
+
+ it('displays message along with lock icon', () => {
+ const { container } = render( );
+
+ const lockIcon = container.querySelector('svg');
+ expect(lockIcon).toHaveClass('fa-lock');
+ expect(lockIcon.parentElement).toHaveTextContent('Graded assignments are locked');
+ });
+
+ it('displays unlock link with price', () => {
+ const { currencySymbol, price, upgradeUrl } = store.getState().models.courses[mockData.courseId].verifiedMode;
+ render( );
+
+ const upgradeLink = screen.getByRole('link', { name: `Upgrade for ${currencySymbol}${price}` });
+ expect(upgradeLink).toHaveAttribute('href', `${upgradeUrl}`);
+ });
+
+ it('does not display anything if course does not have verified mode', async () => {
+ const courseMetadata = Factory.build('courseMetadata', { verified_mode: null });
+ const testStore = await initializeTestStore({ courseMetadata, excludeFetchSequence: true }, false);
+ const { container } = render( , { store: testStore });
+
+ expect(container).toBeEmptyDOMElement();
+ });
+});
diff --git a/src/courseware/course/sequence/lock-paywall-value-prop/index.js b/src/courseware/course/sequence/lock-paywall-value-prop/index.js
new file mode 100644
index 00000000..5fd12427
--- /dev/null
+++ b/src/courseware/course/sequence/lock-paywall-value-prop/index.js
@@ -0,0 +1,2 @@
+/* TODO: This file + folder should be deleted after REV1512 value prop experiment */
+export { default } from './LockPaywall';
diff --git a/src/courseware/course/sequence/lock-paywall/LockPaywall.jsx b/src/courseware/course/sequence/lock-paywall/LockPaywall.jsx
index 212a100c..68175d5f 100644
--- a/src/courseware/course/sequence/lock-paywall/LockPaywall.jsx
+++ b/src/courseware/course/sequence/lock-paywall/LockPaywall.jsx
@@ -35,7 +35,7 @@ function LockPaywall({
{intl.formatMessage(messages['learn.lockPaywall.content'])}
-
+
{intl.formatMessage(messages['learn.lockPaywall.upgrade.link'], {
currencySymbol,
price,