feat: Remove upgrade sock from course pages (#556)
REV-2220: The upgrade sock is being removed from the remaining course pages in favor of the new Value Prop work.
This commit is contained in:
@@ -13,7 +13,6 @@ import ContentTools from './content-tools';
|
||||
import CourseBreadcrumbs from './CourseBreadcrumbs';
|
||||
import NotificationTrigger from './NotificationTrigger';
|
||||
|
||||
import CourseSock from '../../generic/course-sock';
|
||||
import { useModel } from '../../generic/model-store';
|
||||
import useWindowSize, { responsiveBreakpoints } from '../../generic/tabs/useWindowSize';
|
||||
|
||||
@@ -39,10 +38,7 @@ function Course({
|
||||
].filter(element => element != null).map(element => element.title);
|
||||
|
||||
const {
|
||||
canShowUpgradeSock,
|
||||
celebrations,
|
||||
offer,
|
||||
org,
|
||||
verifiedMode,
|
||||
} = course;
|
||||
|
||||
@@ -109,15 +105,6 @@ function Course({
|
||||
open
|
||||
/>
|
||||
)}
|
||||
{canShowUpgradeSock && (
|
||||
<CourseSock
|
||||
courseId={courseId}
|
||||
offer={offer}
|
||||
orgKey={org}
|
||||
pageLocation="Course Content Page"
|
||||
verifiedMode={verifiedMode}
|
||||
/>
|
||||
)}
|
||||
<ContentTools course={course} />
|
||||
{ /** [MM-P2P] Experiment */ }
|
||||
{ MMP2P.meta.modalLock && <MMP2PBlockModal options={MMP2P} /> }
|
||||
|
||||
@@ -79,14 +79,6 @@ describe('Course', () => {
|
||||
expect(getByRole(celebrationModal, 'heading', { name: 'Congratulations!' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays upgrade sock', async () => {
|
||||
const courseMetadata = Factory.build('courseMetadata', { can_show_upgrade_sock: true });
|
||||
const testStore = await initializeTestStore({ courseMetadata, excludeFetchSequence: true }, false);
|
||||
|
||||
render(<Course {...mockData} courseId={courseMetadata.id} />, { store: testStore });
|
||||
expect(screen.getByRole('button', { name: 'Learn About Verified Certificates' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays notification trigger and toggles active class on click', async () => {
|
||||
useWindowSize.mockReturnValue({ width: 1200 });
|
||||
render(<Course {...mockData} />);
|
||||
|
||||
@@ -6,7 +6,6 @@ Factory.define('courseMetadata')
|
||||
.extend(courseMetadataBase)
|
||||
.option('host', '')
|
||||
.attrs({
|
||||
can_show_upgrade_sock: false,
|
||||
content_type_gating_enabled: false,
|
||||
course_expired_message: null,
|
||||
end: null,
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import LearnerQuote1 from './assets/learner-quote.png';
|
||||
import LearnerQuote2 from './assets/learner-quote2.png';
|
||||
import { UpgradeButton } from '../upgrade-button';
|
||||
import VerifiedCert from '../assets/edX_certificate.png';
|
||||
|
||||
export default class CourseSock extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { showUpsell: false };
|
||||
this.sockElement = React.createRef();
|
||||
this.commonEventProperties = {
|
||||
courserun_key: this.props.courseId,
|
||||
org_key: this.props.orgKey,
|
||||
};
|
||||
this.promotionEventProperties = {
|
||||
creative: 'original_sock',
|
||||
name: 'In-Course Verification Prompt',
|
||||
position: 'sock',
|
||||
promotion_id: 'courseware_verified_certificate_upsell',
|
||||
...this.commonEventProperties,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
sendTrackEvent('Promotion Viewed', this.promotionEventProperties);
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.setState(state => ({
|
||||
showUpsell: !state.showUpsell,
|
||||
}));
|
||||
|
||||
const toggleLogEvent = this.state.showUpsell ? 'edx.bi.course.sock.toggle_opened'
|
||||
: 'edx.bi.course.sock.toggle_closed';
|
||||
sendTrackEvent(toggleLogEvent, {
|
||||
from_page: this.props.pageLocation,
|
||||
...this.commonEventProperties,
|
||||
});
|
||||
sendTrackEvent('Promotion Clicked', this.promotionEventProperties);
|
||||
}
|
||||
|
||||
logClick = () => {
|
||||
sendTrackingLogEvent('edx.course.enrollment.upgrade.clicked', {
|
||||
location: 'sock',
|
||||
...this.commonEventProperties,
|
||||
});
|
||||
const onCourseHome = this.props.pageLocation === 'Home Page';
|
||||
sendTrackEvent('edx.bi.ecommerce.upsell_links_clicked', {
|
||||
...this.commonEventProperties,
|
||||
linkCategory: 'green_upgrade',
|
||||
linkName: onCourseHome ? 'course_home_sock' : 'in_course_sock',
|
||||
linkType: 'button',
|
||||
pageName: onCourseHome ? 'course_home' : 'in_course',
|
||||
});
|
||||
}
|
||||
|
||||
showToUser = () => {
|
||||
this.setState({
|
||||
showUpsell: true,
|
||||
}, () => {
|
||||
if (this.sockElement.current) {
|
||||
this.sockElement.current.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.verifiedMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const buttonClass = this.state.showUpsell ? 'btn-primary' : 'btn-outline-primary';
|
||||
return (
|
||||
<div ref={this.sockElement} className="verification-sock container py-5">
|
||||
<div className="d-flex justify-content-center">
|
||||
<button type="button" aria-expanded="false" className={`btn ${buttonClass}`} onClick={this.handleClick}>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.heading"
|
||||
defaultMessage="Learn About Verified Certificates"
|
||||
description="The heading for the upsell dialog"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{this.state.showUpsell && (
|
||||
<>
|
||||
<h2 className="mt-3 mb-4">
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.verifiedcert"
|
||||
defaultMessage="{siteName} Verified Certificate"
|
||||
values={{
|
||||
siteName: getConfig().SITE_NAME,
|
||||
}}
|
||||
/>
|
||||
</h2>
|
||||
<div className="row flex-row-reverse">
|
||||
<div className="col-md-4 col-lg-6 d-flex flex-column">
|
||||
<div>
|
||||
<img alt="Example Certificate" src={VerifiedCert} className="d-block img-thumbnail mb-3 ml-md-auto" />
|
||||
</div>
|
||||
<div className="position-relative flex-grow-1 d-flex flex-column justify-content-end align-items-md-end">
|
||||
<div style={{ position: 'sticky', bottom: '4rem' }}>
|
||||
<UpgradeButton
|
||||
size="lg"
|
||||
offer={this.props.offer}
|
||||
onClick={this.logClick}
|
||||
verifiedMode={this.props.verifiedMode}
|
||||
className="mb-3"
|
||||
data-creative="original_sock"
|
||||
data-position="sock"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-8 col-lg-6">
|
||||
<h3 className="h5">
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.why"
|
||||
defaultMessage="Why upgrade?"
|
||||
/>
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.reason1"
|
||||
defaultMessage="Official proof of completion"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.reason2"
|
||||
defaultMessage="Easily shareable certificate"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.reason3"
|
||||
defaultMessage="Proven motivator to complete the course"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.reason4"
|
||||
defaultMessage="Certificate purchases help {siteName} continue to offer free courses"
|
||||
values={{
|
||||
siteName: getConfig().SITE_NAME,
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 className="h5">
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.howtitle"
|
||||
defaultMessage="How it works"
|
||||
/>
|
||||
</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.how1"
|
||||
defaultMessage="Pay the Verified Certificate upgrade fee"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.how2"
|
||||
defaultMessage="Verify your identity with a webcam and government-issued ID"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.how3"
|
||||
defaultMessage="Study hard and pass the course"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.how4"
|
||||
defaultMessage="Share your certificate with friends, employers, and others"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="h5">
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.storytitle"
|
||||
defaultMessage="{siteName} Learner Stories"
|
||||
values={{
|
||||
siteName: getConfig().SITE_NAME,
|
||||
}}
|
||||
/>
|
||||
</h3>
|
||||
<div className="media my-3">
|
||||
<img className="mr-3" style={{ maxWidth: '4rem' }} alt="Christina Fong" src={LearnerQuote1} />
|
||||
<div className="media-body">
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.story1"
|
||||
defaultMessage="My certificate has helped me showcase my knowledge on my
|
||||
resume - I feel like this certificate could really help me land
|
||||
my dream job!"
|
||||
/>
|
||||
<p className="font-weight-bold">
|
||||
— <FormattedMessage
|
||||
id="coursesock.upsell.learner"
|
||||
description="Name of learner"
|
||||
defaultMessage="{name}, {siteName} Learner"
|
||||
values={{
|
||||
name: 'Christina Fong',
|
||||
siteName: getConfig().SITE_NAME,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="media my-3">
|
||||
<img className="mr-3" style={{ maxWidth: '4rem' }} alt="Chery Troell" src={LearnerQuote2} />
|
||||
<div className="media-body">
|
||||
<FormattedMessage
|
||||
id="coursesock.upsell.story2"
|
||||
defaultMessage="I wanted to include a verified certificate on my resume and my profile to
|
||||
illustrate that I am working towards this goal I have and that I have
|
||||
achieved something while I was unemployed."
|
||||
/>
|
||||
<p className="font-weight-bold">
|
||||
— <FormattedMessage
|
||||
id="coursesock.upsell.learner"
|
||||
description="Name of learner"
|
||||
defaultMessage="{name}, {siteName} Learner"
|
||||
values={{
|
||||
name: 'Cheryl Troell',
|
||||
siteName: getConfig().SITE_NAME,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CourseSock.defaultProps = {
|
||||
courseId: null,
|
||||
offer: null,
|
||||
orgKey: null,
|
||||
};
|
||||
|
||||
CourseSock.propTypes = {
|
||||
courseId: PropTypes.string,
|
||||
offer: PropTypes.shape({}),
|
||||
orgKey: PropTypes.string,
|
||||
pageLocation: PropTypes.string.isRequired,
|
||||
verifiedMode: PropTypes.shape({}).isRequired,
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
import React from 'react';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import {
|
||||
render, screen, fireEvent, initializeMockApp, initializeTestStore,
|
||||
} from '../../setupTest';
|
||||
import CourseSock from './CourseSock';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
describe('Course Sock', () => {
|
||||
let store;
|
||||
const mockData = {
|
||||
verifiedMode: {
|
||||
upgradeUrl: 'test-url',
|
||||
price: 1234,
|
||||
currency: 'dollars',
|
||||
currencySymbol: '$',
|
||||
},
|
||||
pageLocation: 'Course Content Page',
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
|
||||
await initializeMockApp();
|
||||
store = await initializeTestStore();
|
||||
const { courseware } = store.getState();
|
||||
mockData.courseId = courseware.courseId;
|
||||
});
|
||||
|
||||
it('hides upsell information on load', () => {
|
||||
render(<CourseSock {...mockData} />);
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Learn About Verified Certificates' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('edX Verified Certificate')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.only('handles click', () => {
|
||||
sendTrackEvent.mockClear();
|
||||
render(<CourseSock {...mockData} />);
|
||||
const learnMoreButton = screen.getByRole('button', { name: 'Learn About Verified Certificates' });
|
||||
fireEvent.click(learnMoreButton);
|
||||
|
||||
expect(screen.getByText('edX Verified Certificate')).toBeInTheDocument();
|
||||
const { currencySymbol, price } = mockData.verifiedMode;
|
||||
const upsellButton = screen.getByText(`Upgrade for ${currencySymbol}${price}`);
|
||||
expect(upsellButton).toBeInTheDocument();
|
||||
fireEvent.click(upsellButton);
|
||||
expect(sendTrackEvent).toHaveBeenCalledTimes(4);
|
||||
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('Promotion Viewed', {
|
||||
courserun_key: 'course-v1:edX+DemoX+Demo_Course_1',
|
||||
creative: 'original_sock',
|
||||
name: 'In-Course Verification Prompt',
|
||||
org_key: null,
|
||||
position: 'sock',
|
||||
promotion_id: 'courseware_verified_certificate_upsell',
|
||||
});
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.course.sock.toggle_closed', {
|
||||
courserun_key: 'course-v1:edX+DemoX+Demo_Course_1',
|
||||
from_page: 'Course Content Page',
|
||||
org_key: null,
|
||||
});
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('Promotion Clicked', {
|
||||
courserun_key: 'course-v1:edX+DemoX+Demo_Course_1',
|
||||
creative: 'original_sock',
|
||||
name: 'In-Course Verification Prompt',
|
||||
org_key: null,
|
||||
position: 'sock',
|
||||
promotion_id: 'courseware_verified_certificate_upsell',
|
||||
});
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.ecommerce.upsell_links_clicked', {
|
||||
org_key: null,
|
||||
courserun_key: mockData.courseId,
|
||||
linkCategory: 'green_upgrade',
|
||||
linkName: 'in_course_sock',
|
||||
linkType: 'button',
|
||||
pageName: 'in_course',
|
||||
});
|
||||
fireEvent.click(learnMoreButton);
|
||||
expect(screen.queryByText('edX Verified Certificate')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 108 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,3 +0,0 @@
|
||||
import CourseSock from './CourseSock';
|
||||
|
||||
export default CourseSock;
|
||||
Reference in New Issue
Block a user