Skip to content

Commit

Permalink
feat: Set up streak celebration discount experiment (openedx#431)
Browse files Browse the repository at this point in the history
As part of this work, the streak celebration has been migrated from a Paragon Modal to a Modal Dialog
AA-759
  • Loading branch information
MatthewPiatetsky authored May 11, 2021
1 parent 0f69ed5 commit d0bcb19
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 65 deletions.
10 changes: 10 additions & 0 deletions src/course-home/data/__snapshots__/redux.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ Object {
},
],
"title": "Demonstration Course",
"verifiedMode": Object {
"currencySymbol": "$",
"price": 10,
"upgradeUrl": "test",
},
},
},
"dates": Object {
Expand Down Expand Up @@ -347,6 +352,11 @@ Object {
},
],
"title": "Demonstration Course",
"verifiedMode": Object {
"currencySymbol": "$",
"price": 10,
"upgradeUrl": "test",
},
},
},
"outline": Object {
Expand Down
8 changes: 4 additions & 4 deletions src/course-home/outline-tab/widgets/UpgradeCard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ describe('Upgrade Card', () => {
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our non-profit mission at edX');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('126.65 (149)');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
});

Expand Down Expand Up @@ -174,7 +174,7 @@ describe('Upgrade Card', () => {
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our non-profit mission at edX');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('126.65 (149)');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
});

Expand Down Expand Up @@ -203,7 +203,7 @@ describe('Upgrade Card', () => {
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our non-profit mission at edX');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('126.65 (149)');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
});

Expand All @@ -230,7 +230,7 @@ describe('Upgrade Card', () => {
expect(screen.getByText('5 days left')).toBeInTheDocument(); // setting the time to 12 will mean that it's slightly less than 12
expect(screen.getByText(/You will lose all access to this course.*?on/s).textContent).toMatch('You will lose all access to this course, including any progress, on April 18.');
expect(screen.getByText(/Upgrading your course enables you/s).textContent).toMatch('Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the benefits of upgrading.');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('126.65 (149)');
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
});
});
8 changes: 6 additions & 2 deletions src/generic/upgrade-button/FormattedPricing.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ function FormattedPricing(props) {
verifiedMode,
} = props;

let currencySymbol;
if (verifiedMode) {
currencySymbol = verifiedMode.currencySymbol;
}

if (!offer) {
const {
currencySymbol,
price,
} = verifiedMode;
return `${currencySymbol}${price}`;
Expand Down Expand Up @@ -49,7 +53,7 @@ function FormattedPricing(props) {
{intl.formatMessage(messages.srPrices, { discountedPrice, originalPrice })}
</span>
<span aria-hidden="true">
<span>{discountedPrice}</span> (<del>{originalPrice}</del>)
<span>{currencySymbol}{discountedPrice}</span> (<del>{currencySymbol}{originalPrice}</del>)
</span>
</>
);
Expand Down
5 changes: 4 additions & 1 deletion src/generic/upgrade-button/UpgradeButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function UpgradeButton(props) {
const {
intl,
offer,
variant,
onClick,
verifiedMode,
...rest
Expand All @@ -19,7 +20,7 @@ function UpgradeButton(props) {

return (
<Button
variant="primary"
variant={variant}
href={url}
onClick={onClick}
{...rest}
Expand All @@ -43,6 +44,7 @@ function UpgradeButton(props) {
UpgradeButton.defaultProps = {
offer: null,
onClick: null,
variant: 'primary',
};

UpgradeButton.propTypes = {
Expand All @@ -54,6 +56,7 @@ UpgradeButton.propTypes = {
verifiedMode: PropTypes.shape({
upgradeUrl: PropTypes.string.isRequired,
}).isRequired,
variant: PropTypes.string,
};

export default injectIntl(UpgradeButton);
62 changes: 62 additions & 0 deletions src/generic/upgrade-button/UpgradeNowButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';

import FormattedPricing from './FormattedPricing';

function UpgradeNowButton(props) {
const {
intl,
offer,
variant,
onClick,
verifiedMode,
...rest
} = props;

// Prefer offer's url in case it is ever different (though it is not at time of this writing)
const url = offer ? offer.upgradeUrl : verifiedMode.upgradeUrl;

return (
<Button
variant={variant}
href={url}
onClick={onClick}
{...rest}
>
<FormattedMessage
id="learning.upgradeNowButton.buttonText"
defaultMessage="Upgrade now for {pricing}"
values={{
pricing: (
<FormattedPricing
offer={offer}
verifiedMode={verifiedMode}
/>
),
}}
/>
</Button>
);
}

UpgradeNowButton.defaultProps = {
offer: null,
onClick: null,
variant: 'primary',
};

UpgradeNowButton.propTypes = {
intl: intlShape.isRequired,
offer: PropTypes.shape({
upgradeUrl: PropTypes.string.isRequired,
}),
onClick: PropTypes.func,
verifiedMode: PropTypes.shape({
upgradeUrl: PropTypes.string.isRequired,
}).isRequired,
variant: PropTypes.string,
};

export default injectIntl(UpgradeNowButton);
2 changes: 2 additions & 0 deletions src/generic/upgrade-button/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import FormattedPricing from './FormattedPricing';
import UpgradeButton from './UpgradeButton';
import UpgradeNowButton from './UpgradeNowButton';

export {
FormattedPricing,
UpgradeButton,
UpgradeNowButton,
};
6 changes: 6 additions & 0 deletions src/setupTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ window.getComputedStyle = jest.fn(() => ({
getPropertyValue: jest.fn(),
}));

// Mock Intersection Observer which is unavailable in the context of a test.
global.IntersectionObserver = jest.fn(function mockIntersectionObserver() {
this.observe = jest.fn();
this.disconnect = jest.fn();
});

// Mock media queries because any component that uses `react-break` for responsive breakpoints will
// run into `TypeError: window.matchMedia is not a function`. This avoids that for all of our tests now.
Object.defineProperty(window, 'matchMedia', {
Expand Down
5 changes: 5 additions & 0 deletions src/shared/data/__factories__/courseMetadataBase.factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export default new Factory()
original_user_is_staff: false,
number: 'DemoX',
org: 'edX',
verifiedMode: {
upgradeUrl: 'test',
price: 10,
currencySymbol: '$',
},
})
.attr(
'tabs', ['id', 'host'], (id, host) => {
Expand Down
Loading

0 comments on commit d0bcb19

Please sign in to comment.