AA-390: Add Social Share icons to Course Exit (#259)
This commit is contained in:
@@ -7,7 +7,7 @@ import { layoutGenerator } from 'react-break';
|
||||
import ClapsMobile from './assets/claps_280x201.gif';
|
||||
import ClapsTablet from './assets/claps_456x328.gif';
|
||||
import messages from './messages';
|
||||
import SocialIcons from './SocialIcons';
|
||||
import SocialIcons from '../../social-share/SocialIcons';
|
||||
import { recordFirstSectionCelebration } from './utils';
|
||||
|
||||
function CelebrationModal({
|
||||
@@ -41,7 +41,12 @@ function CelebrationModal({
|
||||
<p className="mt-3">
|
||||
<strong>{intl.formatMessage(messages.earned)}</strong> {intl.formatMessage(messages.share)}
|
||||
</p>
|
||||
<SocialIcons courseId={courseId} />
|
||||
<SocialIcons
|
||||
analyticsId="edx.ui.lms.celebration.social_share.clicked"
|
||||
courseId={courseId}
|
||||
emailSubject={messages.emailSubject}
|
||||
socialMessage={messages.socialMessage}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
closeText={intl.formatMessage(messages.forward)}
|
||||
|
||||
@@ -13,11 +13,6 @@ const messages = defineMessages({
|
||||
id: 'learning.celebration.earned',
|
||||
defaultMessage: 'You earned it!',
|
||||
},
|
||||
emailBody: {
|
||||
id: 'learning.celebration.emailBody',
|
||||
defaultMessage: 'What are you spending your time learning?',
|
||||
description: 'Body when sharing course progress via email',
|
||||
},
|
||||
emailSubject: {
|
||||
id: 'learning.celebration.emailSubject',
|
||||
defaultMessage: "I'm on my way to completing {title} online with {platform}!",
|
||||
@@ -32,15 +27,7 @@ const messages = defineMessages({
|
||||
id: 'learning.celebration.share',
|
||||
defaultMessage: 'Take a moment to celebrate and share your progress.',
|
||||
},
|
||||
shareEmail: {
|
||||
id: 'learning.celebration.share.email',
|
||||
defaultMessage: 'Share your progress via email.',
|
||||
},
|
||||
shareService: {
|
||||
id: 'learning.celebration.share.service',
|
||||
defaultMessage: 'Share your progress on {service}.',
|
||||
},
|
||||
social: {
|
||||
socialMessage: {
|
||||
id: 'learning.celebration.social',
|
||||
defaultMessage: 'I’m on my way to completing {title} online with {platform}. What are you spending your time learning?',
|
||||
description: 'Shown when sharing course progress on a social network',
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useModel } from '../../../generic/model-store';
|
||||
import { requestCert } from '../../../course-home/data/thunks';
|
||||
import DashboardFootnote from './DashboardFootnote';
|
||||
import UpgradeFootnote from './UpgradeFootnote';
|
||||
import SocialIcons from '../../social-share/SocialIcons';
|
||||
|
||||
const LINKEDIN_BLUE = '#007fb1';
|
||||
|
||||
@@ -95,11 +96,11 @@ function CourseCelebration({ intl }) {
|
||||
let certificateImage = certificate;
|
||||
let footnote;
|
||||
let message;
|
||||
let title;
|
||||
let certHeader;
|
||||
// These cases are taken from the edx-platform `get_cert_data` function found in lms/courseware/views/views.py
|
||||
switch (certStatus) {
|
||||
case 'downloadable':
|
||||
title = intl.formatMessage(messages.certificateHeaderDownloadable);
|
||||
certHeader = intl.formatMessage(messages.certificateHeaderDownloadable);
|
||||
message = (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
@@ -123,7 +124,7 @@ function CourseCelebration({ intl }) {
|
||||
break;
|
||||
case 'earned_but_not_available': {
|
||||
const endDate = <FormattedDate value={end} day="numeric" month="long" year="numeric" />;
|
||||
title = intl.formatMessage(messages.certificateHeaderNotAvailable);
|
||||
certHeader = intl.formatMessage(messages.certificateHeaderNotAvailable);
|
||||
message = (
|
||||
<>
|
||||
<p>
|
||||
@@ -150,14 +151,14 @@ function CourseCelebration({ intl }) {
|
||||
}
|
||||
case 'requesting':
|
||||
buttonText = intl.formatMessage(messages.requestCertificateButton);
|
||||
title = intl.formatMessage(messages.certificateHeaderRequestable);
|
||||
certHeader = intl.formatMessage(messages.certificateHeaderRequestable);
|
||||
message = (<p>{intl.formatMessage(messages.requestCertificateBodyText)}</p>);
|
||||
footnote = <DashboardFootnote />;
|
||||
break;
|
||||
case 'unverified':
|
||||
buttonText = intl.formatMessage(messages.verifyIdentityButton);
|
||||
buttonLocation = verifyIdentityUrl;
|
||||
title = intl.formatMessage(messages.certificateHeaderUnverified);
|
||||
certHeader = intl.formatMessage(messages.certificateHeaderUnverified);
|
||||
// todo: check for idVerificationSupportLink null
|
||||
message = (
|
||||
<p>
|
||||
@@ -174,7 +175,7 @@ function CourseCelebration({ intl }) {
|
||||
case 'audit_passing':
|
||||
case 'honor_passing':
|
||||
if (verifiedMode) {
|
||||
title = intl.formatMessage(messages.certificateHeaderUpgradable);
|
||||
certHeader = intl.formatMessage(messages.certificateHeaderUpgradable);
|
||||
message = (
|
||||
<p>
|
||||
<FormattedMessage
|
||||
@@ -225,8 +226,15 @@ function CourseCelebration({ intl }) {
|
||||
</div>
|
||||
<div className="col-12 p-0 font-weight-normal lead text-center">
|
||||
{intl.formatMessage(messages.shareHeader)}
|
||||
<SocialIcons
|
||||
analyticsId="edx.ui.lms.course_exit.social_share.clicked"
|
||||
className="mt-2"
|
||||
courseId={courseId}
|
||||
emailSubject={messages.socialMessage}
|
||||
socialMessage={messages.socialMessage}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-12 my-4 px-0 px-md-5 text-center">
|
||||
<div className="col-12 mt-3 mb-4 px-0 px-md-5 text-center">
|
||||
<OnMobile>
|
||||
<img
|
||||
src={CelebrationMobile}
|
||||
@@ -244,10 +252,10 @@ function CourseCelebration({ intl }) {
|
||||
</OnAtLeastTablet>
|
||||
</div>
|
||||
<div className="col-12 px-0 px-md-5">
|
||||
{title && (
|
||||
{certHeader && (
|
||||
<Alert variant="primary" className="row w-100 m-0">
|
||||
<div className="col order-1 order-md-0 pl-0 pr-0 pr-md-5">
|
||||
<div className="h4">{title}</div>
|
||||
<div className="h4">{certHeader}</div>
|
||||
{message}
|
||||
{/* The requesting status needs a different button because it does a POST instead of a GET */}
|
||||
{certStatus === 'requesting' && (
|
||||
|
||||
@@ -118,6 +118,24 @@ describe('Course Exit Pages', () => {
|
||||
expect(screen.getByRole('button', { name: 'Request certificate' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Displays social share icons', async () => {
|
||||
setMetadata({ certificate_data: { cert_status: 'unverified' }, marketing_url: 'https://edx.org' });
|
||||
await fetchAndRender(<CourseCelebration />);
|
||||
expect(screen.getByRole('button', { name: 'linkedin' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'facebook' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'twitter' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'email' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not display social share icons if no marketing URL', async () => {
|
||||
setMetadata({ certificate_data: { cert_status: 'unverified' } });
|
||||
await fetchAndRender(<CourseCelebration />);
|
||||
expect(screen.queryByRole('button', { name: 'linkedin' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'facebook' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'twitter' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'email' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Displays verify identity link', async () => {
|
||||
setMetadata({
|
||||
certificate_data: { cert_status: 'unverified' },
|
||||
|
||||
@@ -97,6 +97,11 @@ const messages = defineMessages({
|
||||
id: 'courseCelebration.shareHeader',
|
||||
defaultMessage: 'You have completed your course. Share your success on social media or email.',
|
||||
},
|
||||
socialMessage: {
|
||||
id: 'courseExit.social.shareCompletionMessage',
|
||||
defaultMessage: 'I just completed {title} with {platform}!',
|
||||
description: 'Shown when sharing course progress on a social network',
|
||||
},
|
||||
upgradeButton: {
|
||||
id: 'courseExit.upgradeButton',
|
||||
defaultMessage: 'Upgrade now',
|
||||
|
||||
@@ -17,9 +17,18 @@ import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import messages from './messages';
|
||||
import { useModel } from '../../../generic/model-store';
|
||||
import { useModel } from '../../generic/model-store';
|
||||
|
||||
function SocialIcons({ courseId, intl }) {
|
||||
function SocialIcons({
|
||||
analyticsId,
|
||||
className,
|
||||
courseId,
|
||||
emailBody,
|
||||
emailSubject,
|
||||
hashtags,
|
||||
intl,
|
||||
socialMessage,
|
||||
}) {
|
||||
const {
|
||||
marketingUrl,
|
||||
title,
|
||||
@@ -33,8 +42,12 @@ function SocialIcons({ courseId, intl }) {
|
||||
const twitterAccount = twitterUrl && twitterUrl.substring(twitterUrl.lastIndexOf('/') + 1);
|
||||
|
||||
const logClick = (service) => {
|
||||
if (!analyticsId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { administrator } = getAuthenticatedUser();
|
||||
sendTrackEvent('edx.ui.lms.celebration.social_share.clicked', {
|
||||
sendTrackEvent(analyticsId, {
|
||||
course_id: courseId,
|
||||
is_staff: administrator,
|
||||
service,
|
||||
@@ -44,7 +57,7 @@ function SocialIcons({ courseId, intl }) {
|
||||
const socialUtmMarketingUrl = `${marketingUrl}?utm_campaign=edxmilestone&utm_medium=social`;
|
||||
|
||||
return (
|
||||
<div className="social-icons">
|
||||
<div className={`social-icons ${className}`}>
|
||||
<LinkedinShareButton
|
||||
beforeOnClick={() => logClick('linkedin')}
|
||||
url={`${socialUtmMarketingUrl}&utm_source=linkedin`}
|
||||
@@ -52,12 +65,12 @@ function SocialIcons({ courseId, intl }) {
|
||||
<LinkedinIcon round size={32} />
|
||||
<span className="sr-only">{intl.formatMessage(messages.shareService, { service: 'LinkedIn' })}</span>
|
||||
</LinkedinShareButton>
|
||||
{ twitterAccount && (
|
||||
{twitterAccount && (
|
||||
<TwitterShareButton
|
||||
beforeOnClick={() => logClick('twitter')}
|
||||
className="ml-2"
|
||||
hashtags={['myedxjourney']}
|
||||
title={intl.formatMessage(messages.social, { platform: `@${twitterAccount}`, title })}
|
||||
hashtags={hashtags}
|
||||
title={socialMessage ? intl.formatMessage(socialMessage, { platform: `@${twitterAccount}`, title }) : ''}
|
||||
url={`${socialUtmMarketingUrl}&utm_source=twitter`}
|
||||
>
|
||||
<TwitterIcon round size={32} />
|
||||
@@ -67,7 +80,7 @@ function SocialIcons({ courseId, intl }) {
|
||||
<FacebookShareButton
|
||||
beforeOnClick={() => logClick('facebook')}
|
||||
className="ml-2"
|
||||
quote={intl.formatMessage(messages.social, { platform: getConfig().SITE_NAME, title })}
|
||||
quote={socialMessage ? intl.formatMessage(socialMessage, { platform: getConfig().SITE_NAME, title }) : ''}
|
||||
url={`${socialUtmMarketingUrl}&utm_source=facebook`}
|
||||
>
|
||||
<FacebookIcon round size={32} />
|
||||
@@ -75,9 +88,9 @@ function SocialIcons({ courseId, intl }) {
|
||||
</FacebookShareButton>
|
||||
<EmailShareButton
|
||||
beforeOnClick={() => logClick('email')}
|
||||
body={`${intl.formatMessage(messages.emailBody)}\n\n`}
|
||||
body={emailBody ? `${intl.formatMessage(emailBody)}\n\n` : ''}
|
||||
className="ml-2"
|
||||
subject={intl.formatMessage(messages.emailSubject, { platform: getConfig().SITE_NAME, title })}
|
||||
subject={emailSubject ? intl.formatMessage(emailSubject, { platform: getConfig().SITE_NAME, title }) : ''}
|
||||
url={`${marketingUrl}?utm_campaign=edxmilestone&utm_medium=email&utm_source=email`}
|
||||
>
|
||||
<EmailIcon round size={32} />
|
||||
@@ -87,9 +100,24 @@ function SocialIcons({ courseId, intl }) {
|
||||
);
|
||||
}
|
||||
|
||||
SocialIcons.defaultProps = {
|
||||
analyticsId: '',
|
||||
className: '',
|
||||
emailBody: messages.defaultEmailBody,
|
||||
emailSubject: null,
|
||||
hashtags: ['myedxjourney'],
|
||||
socialMessage: null,
|
||||
};
|
||||
|
||||
SocialIcons.propTypes = {
|
||||
analyticsId: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
courseId: PropTypes.string.isRequired,
|
||||
emailBody: PropTypes.shape({}),
|
||||
emailSubject: PropTypes.shape({}),
|
||||
hashtags: PropTypes.arrayOf(PropTypes.string),
|
||||
intl: intlShape.isRequired,
|
||||
socialMessage: PropTypes.shape({}),
|
||||
};
|
||||
|
||||
export default injectIntl(SocialIcons);
|
||||
19
src/courseware/social-share/messages.js
Normal file
19
src/courseware/social-share/messages.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
defaultEmailBody: {
|
||||
id: 'learning.celebration.emailBody',
|
||||
defaultMessage: 'What are you spending your time learning?',
|
||||
description: 'Body when sharing course progress via email',
|
||||
},
|
||||
shareEmail: {
|
||||
id: 'learning.social.shareEmail',
|
||||
defaultMessage: 'Share your progress via email.',
|
||||
},
|
||||
shareService: {
|
||||
id: 'learning.social.shareService',
|
||||
defaultMessage: 'Share your progress on {service}.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -233,7 +233,6 @@ $primary: #1176B2;
|
||||
}
|
||||
|
||||
.previous-btn, .next-btn {
|
||||
min-width: 4rem;
|
||||
color: theme-color('gray', 700);
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -63,6 +63,7 @@ export function initializeMockApp() {
|
||||
mergeConfig({
|
||||
INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null,
|
||||
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null,
|
||||
TWITTER_URL: process.env.TWITTER_URL || null,
|
||||
authenticatedUser: {
|
||||
userId: 'abc123',
|
||||
username: 'Mock User',
|
||||
|
||||
Reference in New Issue
Block a user