Skip to content

Commit

Permalink
feat: Add Value Prop past expiration messaging for gated content (ope…
Browse files Browse the repository at this point in the history
…nedx#836)

REV-2500
  • Loading branch information
julianajlk authored Mar 11, 2022
1 parent 72d18dc commit 4f1a50e
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 20 deletions.
67 changes: 47 additions & 20 deletions src/courseware/course/sequence/lock-paywall/LockPaywall.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Alert, breakpoints, useWindowSize } from '@edx/paragon';
import {
Alert, Hyperlink, breakpoints, useWindowSize,
} from '@edx/paragon';
import { Locked } from '@edx/paragon/icons';
import SidebarContext from '../../sidebar/SidebarContext';
import messages from './messages';
Expand All @@ -24,6 +26,8 @@ function LockPaywall({
const { notificationTrayVisible } = useContext(SidebarContext);
const course = useModel('coursewareMeta', courseId);
const {
accessExpiration,
marketingUrl,
offer,
org,
verifiedMode,
Expand All @@ -40,6 +44,9 @@ function LockPaywall({
&& !notificationTrayVisible;
const shouldWrapTextOnButton = useWindowSize().width > breakpoints.extraSmall.minWidth;

const accessExpirationDate = accessExpiration ? new Date(accessExpiration.expirationDate) : null;
const pastExpirationDeadline = accessExpiration ? new Date(Date.now()) > accessExpirationDate : false;

if (!verifiedMode) {
return null;
}
Expand All @@ -59,19 +66,35 @@ function LockPaywall({
});
};

const logClickPastExpiration = () => {
sendTrackEvent('edx.bi.ecommerce.gated_content.past_expiration.link_clicked', {
...eventProperties,
linkCategory: 'gated_content',
linkName: 'course_details',
linkType: 'link',
pageName: 'in_course',
});
};

return (
<Alert variant="light" aria-live="off" icon={Locked} className="lock-paywall-container">
<div className="row">
<div className="col">
<h4 aria-level="3">
<span>{intl.formatMessage(messages['learn.lockPaywall.title'])}</span>
</h4>
{pastExpirationDeadline ? (
<div className="mb-2 upgrade-intro">
{intl.formatMessage(messages['learn.lockPaywall.content.pastExpiration'])}
<Hyperlink destination={marketingUrl} onClick={logClickPastExpiration} target="_blank">{intl.formatMessage(messages['learn.lockPaywall.courseDetails'])}</Hyperlink>
</div>
) : (
<div className="mb-2 upgrade-intro">
{intl.formatMessage(messages['learn.lockPaywall.content'])}
</div>
)}

<div className="mb-2 upgrade-intro">
{intl.formatMessage(messages['learn.lockPaywall.content'])}
</div>

<div className={classNames('d-flex flex-row', { 'flex-wrap': notificationTrayVisible || shouldDisplayBulletPointsBelowCertificate })}>
<div className={classNames('d-inline-flex flex-row', { 'flex-wrap': notificationTrayVisible || shouldDisplayBulletPointsBelowCertificate })}>
<div style={{ float: 'left' }} className="mr-3 mb-2">
<img
alt={intl.formatMessage(messages['learn.lockPaywall.example.alt'])}
Expand All @@ -95,20 +118,24 @@ function LockPaywall({
</div>
</div>

<div
className={
classNames('d-md-flex align-items-md-center text-right', {
'col-md-5 mx-md-0': notificationTrayVisible, 'col-md-4 mx-md-3 justify-content-center': !notificationTrayVisible && !shouldDisplayGatedContentTwoColumnsHalf, 'col-md-11 justify-content-end': shouldDisplayGatedContentOneColumn && !shouldDisplayGatedContentTwoColumns, 'col-md-6 justify-content-center': shouldDisplayGatedContentTwoColumnsHalf,
})
}
>
<UpgradeButton
offer={offer}
onClick={logClick}
verifiedMode={verifiedMode}
style={{ whiteSpace: shouldWrapTextOnButton ? 'nowrap' : null }}
/>
</div>
{pastExpirationDeadline
? null
: (
<div
className={
classNames('d-md-flex align-items-md-center text-right', {
'col-md-5 mx-md-0': notificationTrayVisible, 'col-md-4 mx-md-3 justify-content-center': !notificationTrayVisible && !shouldDisplayGatedContentTwoColumnsHalf, 'col-md-11 justify-content-end': shouldDisplayGatedContentOneColumn && !shouldDisplayGatedContentTwoColumns, 'col-md-6 justify-content-center': shouldDisplayGatedContentTwoColumnsHalf,
})
}
>
<UpgradeButton
offer={offer}
onClick={logClick}
verifiedMode={verifiedMode}
style={{ whiteSpace: shouldWrapTextOnButton ? 'nowrap' : null }}
/>
</div>
)}
</div>
</Alert>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.alert-content.lock-paywall-container {
display: inline-flex;
width: 100%;
}

.lock-paywall-container svg {
Expand Down
38 changes: 38 additions & 0 deletions src/courseware/course/sequence/lock-paywall/LockPaywall.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,42 @@ describe('Lock Paywall', () => {

expect(container).toBeEmptyDOMElement();
});

it('displays past expiration message if expiration date has expired', async () => {
const courseMetadata = Factory.build('courseMetadata', {
access_expiration: {
expiration_date: '1995-02-22T05:00:00Z',
},
marketing_url: 'https://example.com/course-details',
});
const testStore = await initializeTestStore({ courseMetadata }, false);
render(<LockPaywall {...mockData} courseId={courseMetadata.id} />, { store: testStore });
expect(screen.getByText('The upgrade deadline for this course passed. To upgrade, enroll in the next available session.')).toBeInTheDocument();
expect(screen.getByText('View Course Details'))
.toHaveAttribute('href', 'https://example.com/course-details');
});

it('sends analytics event onClick of past expiration course details link', async () => {
sendTrackEvent.mockClear();
const courseMetadata = Factory.build('courseMetadata', {
access_expiration: {
expiration_date: '1995-02-22T05:00:00Z',
},
marketing_url: 'https://example.com/course-details',
});
const testStore = await initializeTestStore({ courseMetadata }, false);
render(<LockPaywall {...mockData} courseId={courseMetadata.id} />, { store: testStore });
const courseDetailsLink = await screen.getByText('View Course Details');
fireEvent.click(courseDetailsLink);

expect(sendTrackEvent).toHaveBeenCalledTimes(1);
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.ecommerce.gated_content.past_expiration.link_clicked', {
org_key: 'edX',
courserun_key: mockData.courseId,
linkCategory: 'gated_content',
linkName: 'course_details',
linkType: 'link',
pageName: 'in_course',
});
});
});
10 changes: 10 additions & 0 deletions src/courseware/course/sequence/lock-paywall/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ const messages = defineMessages({
defaultMessage: 'Upgrade to gain access to locked features like this one and get the most out of your course.',
description: 'Message shown to indicate that a piece of content is unavailable to audit track users.',
},
'learn.lockPaywall.content.pastExpiration': {
id: 'learn.lockPaywall.content.pastExpiration',
defaultMessage: 'The upgrade deadline for this course passed. To upgrade, enroll in the next available session. ',
description: 'Message shown to indicate that a piece of content is unavailable to audit track users in a course where the expiration deadline has passed.',
},
'learn.lockPaywall.courseDetails': {
id: 'learn.lockPaywall.courseDetails',
defaultMessage: 'View Course Details',
description: 'Link to the course details page for this course with a past expiration date.',
},
'learn.lockPaywall.example.alt': {
id: 'learn.lockPaywall.example.alt',
defaultMessage: 'Example Certificate',
Expand Down
3 changes: 3 additions & 0 deletions src/courseware/data/__factories__/courseMetadata.factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Factory.define('courseMetadata')
.extend(courseMetadataBase)
.option('host', '')
.attrs({
access_expiration: {
expiration_date: '2032-02-22T05:00:00Z',
},
content_type_gating_enabled: false,
course_expired_message: null,
course_goals: {
Expand Down

0 comments on commit 4f1a50e

Please sign in to comment.