Updated frontend-build to v12 (#962)
* feat: rebase previous frontend-build upgrade * chore: make welcome message to default to empty
This commit is contained in:
@@ -12,13 +12,14 @@ import { LOADED, LOADING } from '../course-home/data/slice';
|
||||
import PageLoading from './PageLoading';
|
||||
import messages from '../tab-page/messages';
|
||||
|
||||
function CourseAccessErrorPage({ intl }) {
|
||||
const CourseAccessErrorPage = ({ intl }) => {
|
||||
const { courseId } = useParams();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const activeEnterpriseAlert = useActiveEnterpriseAlert(courseId);
|
||||
useEffect(() => {
|
||||
dispatch(fetchDiscussionTab(courseId));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [courseId]);
|
||||
|
||||
const {
|
||||
@@ -54,7 +55,7 @@ function CourseAccessErrorPage({ intl }) {
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
CourseAccessErrorPage.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
@@ -11,7 +11,9 @@ jest.mock('react-redux', () => ({
|
||||
useDispatch: () => mockDispatch,
|
||||
useSelector: () => ({ courseStatus: mockCourseStatus }),
|
||||
}));
|
||||
jest.mock('./PageLoading', () => () => <div data-testid="page-loading" />);
|
||||
jest.mock('./PageLoading', () => function () {
|
||||
return <div data-testid="page-loading" />;
|
||||
});
|
||||
|
||||
describe('CourseAccessErrorPage', () => {
|
||||
let courseId;
|
||||
|
||||
@@ -10,6 +10,7 @@ import { getNotices } from './api';
|
||||
*/
|
||||
const NoticesProvider = ({ children }) => {
|
||||
const [isRedirected, setIsRedirected] = useState();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(async () => {
|
||||
if (getConfig().ENABLE_NOTICES) {
|
||||
const data = await getNotices();
|
||||
|
||||
@@ -6,7 +6,7 @@ import classNames from 'classnames';
|
||||
|
||||
import useIndexOfLastVisibleChild from './useIndexOfLastVisibleChild';
|
||||
|
||||
export default function Tabs({ children, className, ...attrs }) {
|
||||
const Tabs = ({ children, className, ...attrs }) => {
|
||||
const [
|
||||
indexOfLastVisibleChild,
|
||||
containerElementRef,
|
||||
@@ -52,7 +52,7 @@ export default function Tabs({ children, className, ...attrs }) {
|
||||
),
|
||||
);
|
||||
return wrappedChildren;
|
||||
}, [children, indexOfLastVisibleChild]);
|
||||
}, [children, indexOfLastVisibleChild, invisibleStyle, overflowElementRef]);
|
||||
|
||||
return (
|
||||
<nav
|
||||
@@ -63,7 +63,7 @@ export default function Tabs({ children, className, ...attrs }) {
|
||||
{tabChildren}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Tabs.propTypes = {
|
||||
children: PropTypes.node,
|
||||
@@ -74,3 +74,5 @@ Tabs.defaultProps = {
|
||||
children: null,
|
||||
className: undefined,
|
||||
};
|
||||
|
||||
export default Tabs;
|
||||
|
||||
@@ -70,6 +70,7 @@ export default function useIndexOfLastVisibleChild() {
|
||||
});
|
||||
|
||||
setIndexOfLastVisibleChild(nextIndexOfLastVisibleChild);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [windowSize, containerElementRef.current]);
|
||||
|
||||
return [indexOfLastVisibleChild, containerElementRef, invisibleStyle, overflowElementRef];
|
||||
|
||||
@@ -4,7 +4,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
function FormattedPricing(props) {
|
||||
const FormattedPricing = (props) => {
|
||||
const {
|
||||
inline,
|
||||
intl,
|
||||
@@ -57,7 +57,7 @@ function FormattedPricing(props) {
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
FormattedPricing.defaultProps = {
|
||||
inline: false,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from '@edx/paragon';
|
||||
|
||||
import FormattedPricing from './FormattedPricing';
|
||||
|
||||
function UpgradeButton(props) {
|
||||
const UpgradeButton = (props) => {
|
||||
const {
|
||||
intl,
|
||||
offer,
|
||||
@@ -41,7 +41,7 @@ function UpgradeButton(props) {
|
||||
</div>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UpgradeButton.defaultProps = {
|
||||
offer: null,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button } from '@edx/paragon';
|
||||
|
||||
import FormattedPricing from './FormattedPricing';
|
||||
|
||||
function UpgradeNowButton(props) {
|
||||
const UpgradeNowButton = (props) => {
|
||||
const {
|
||||
intl,
|
||||
offer,
|
||||
@@ -39,7 +39,7 @@ function UpgradeNowButton(props) {
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UpgradeNowButton.defaultProps = {
|
||||
offer: null,
|
||||
|
||||
@@ -13,27 +13,23 @@ import {
|
||||
SupportMissionBullet,
|
||||
} from '../upsell-bullets/UpsellBullets';
|
||||
|
||||
function UpsellNoFBECardContent() {
|
||||
return (
|
||||
<ul className="fa-ul upgrade-notification-ul pt-0">
|
||||
<VerifiedCertBullet />
|
||||
<SupportMissionBullet />
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
const UpsellNoFBECardContent = () => (
|
||||
<ul className="fa-ul upgrade-notification-ul pt-0">
|
||||
<VerifiedCertBullet />
|
||||
<SupportMissionBullet />
|
||||
</ul>
|
||||
);
|
||||
|
||||
function UpsellFBEFarAwayCardContent() {
|
||||
return (
|
||||
<ul className="fa-ul upgrade-notification-ul">
|
||||
<VerifiedCertBullet />
|
||||
<UnlockGradedBullet />
|
||||
<FullAccessBullet />
|
||||
<SupportMissionBullet />
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
const UpsellFBEFarAwayCardContent = () => (
|
||||
<ul className="fa-ul upgrade-notification-ul">
|
||||
<VerifiedCertBullet />
|
||||
<UnlockGradedBullet />
|
||||
<FullAccessBullet />
|
||||
<SupportMissionBullet />
|
||||
</ul>
|
||||
);
|
||||
|
||||
function UpsellFBESoonCardContent({ accessExpirationDate, timezoneFormatArgs }) {
|
||||
const UpsellFBESoonCardContent = ({ accessExpirationDate, timezoneFormatArgs }) => {
|
||||
const includingAnyProgress = (
|
||||
<span className="font-weight-bold">
|
||||
<FormattedMessage
|
||||
@@ -83,7 +79,7 @@ function UpsellFBESoonCardContent({ accessExpirationDate, timezoneFormatArgs })
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UpsellFBESoonCardContent.propTypes = {
|
||||
accessExpirationDate: PropTypes.PropTypes.instanceOf(Date).isRequired,
|
||||
@@ -96,22 +92,20 @@ UpsellFBESoonCardContent.defaultProps = {
|
||||
timezoneFormatArgs: {},
|
||||
};
|
||||
|
||||
function PastExpirationCardContent() {
|
||||
return (
|
||||
<div className="upgrade-notification-text">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.pastExpiration.content"
|
||||
defaultMessage="The upgrade deadline for this course passed. To upgrade, enroll in the next available session."
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const PastExpirationCardContent = () => (
|
||||
<div className="upgrade-notification-text">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.pastExpiration.content"
|
||||
defaultMessage="The upgrade deadline for this course passed. To upgrade, enroll in the next available session."
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
function ExpirationCountdown({
|
||||
const ExpirationCountdown = ({
|
||||
courseId, hoursToExpiration, setupgradeNotificationCurrentState, type,
|
||||
}) {
|
||||
}) => {
|
||||
let expirationText;
|
||||
if (hoursToExpiration >= 24) { // More than 1 day left
|
||||
// setupgradeNotificationCurrentState is available in NotificationTray (not course home)
|
||||
@@ -179,7 +173,7 @@ function ExpirationCountdown({
|
||||
);
|
||||
}
|
||||
return (<div className="upsell-warning">{expirationText}</div>);
|
||||
}
|
||||
};
|
||||
|
||||
ExpirationCountdown.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
@@ -192,9 +186,9 @@ ExpirationCountdown.defaultProps = {
|
||||
type: null,
|
||||
};
|
||||
|
||||
function AccessExpirationDateBanner({
|
||||
const AccessExpirationDateBanner = ({
|
||||
courseId, accessExpirationDate, timezoneFormatArgs, setupgradeNotificationCurrentState,
|
||||
}) {
|
||||
}) => {
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
setupgradeNotificationCurrentState('accessDateView');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'accessDateView');
|
||||
@@ -218,7 +212,7 @@ function AccessExpirationDateBanner({
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
AccessExpirationDateBanner.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
@@ -234,9 +228,9 @@ AccessExpirationDateBanner.defaultProps = {
|
||||
setupgradeNotificationCurrentState: null,
|
||||
};
|
||||
|
||||
function PastExpirationDateBanner({
|
||||
const PastExpirationDateBanner = ({
|
||||
courseId, accessExpirationDate, timezoneFormatArgs, setupgradeNotificationCurrentState,
|
||||
}) {
|
||||
}) => {
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
setupgradeNotificationCurrentState('PastExpirationDate');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'PastExpirationDate');
|
||||
@@ -260,7 +254,7 @@ function PastExpirationDateBanner({
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
PastExpirationDateBanner.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
@@ -276,7 +270,7 @@ PastExpirationDateBanner.defaultProps = {
|
||||
setupgradeNotificationCurrentState: null,
|
||||
};
|
||||
|
||||
function UpgradeNotification({
|
||||
const UpgradeNotification = ({
|
||||
accessExpiration,
|
||||
contentTypeGatingEnabled,
|
||||
marketingUrl,
|
||||
@@ -289,17 +283,13 @@ function UpgradeNotification({
|
||||
upsellPageName,
|
||||
userTimezone,
|
||||
verifiedMode,
|
||||
}) {
|
||||
}) => {
|
||||
const dateNow = Date.now();
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
const correctedTime = new Date(dateNow + timeOffsetMillis);
|
||||
const accessExpirationDate = accessExpiration ? new Date(accessExpiration.expirationDate) : null;
|
||||
const pastExpirationDeadline = accessExpiration ? new Date(dateNow) > accessExpirationDate : false;
|
||||
|
||||
if (!verifiedMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const eventProperties = {
|
||||
org_key: org,
|
||||
courserun_key: courseId,
|
||||
@@ -316,8 +306,13 @@ function UpgradeNotification({
|
||||
useEffect(() => {
|
||||
sendTrackingLogEvent('edx.bi.course.upgrade.sidebarupsell.displayed', eventProperties);
|
||||
sendTrackEvent('Promotion Viewed', promotionEventProperties);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!verifiedMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const logClick = () => {
|
||||
sendTrackingLogEvent('edx.bi.course.upgrade.sidebarupsell.clicked', eventProperties);
|
||||
sendTrackingLogEvent('edx.course.enrollment.upgrade.clicked', {
|
||||
@@ -502,7 +497,7 @@ function UpgradeNotification({
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UpgradeNotification.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
|
||||
@@ -4,14 +4,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
function CheckmarkBullet() {
|
||||
return (
|
||||
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
|
||||
);
|
||||
}
|
||||
const CheckmarkBullet = () => (
|
||||
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
|
||||
);
|
||||
|
||||
// Must be child of a <ul className="fa-ul">
|
||||
export function VerifiedCertBullet() {
|
||||
export const VerifiedCertBullet = () => {
|
||||
const verifiedCertLink = (
|
||||
<a className="inline-link-underline font-weight-bold" rel="noopener noreferrer" target="_blank" href={`${getConfig().MARKETING_SITE_BASE_URL}/verified-certificate`}>
|
||||
<FormattedMessage
|
||||
@@ -32,10 +30,10 @@ export function VerifiedCertBullet() {
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Must be child of a <ul className="fa-ul">
|
||||
export function UnlockGradedBullet() {
|
||||
export const UnlockGradedBullet = () => {
|
||||
const gradedAssignmentsInBoldText = (
|
||||
<span className="font-weight-bold">
|
||||
<FormattedMessage
|
||||
@@ -56,10 +54,10 @@ export function UnlockGradedBullet() {
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Must be child of a <ul className="fa-ul">
|
||||
export function FullAccessBullet() {
|
||||
export const FullAccessBullet = () => {
|
||||
const fullAccessInBoldText = (
|
||||
<span className="font-weight-bold">
|
||||
<FormattedMessage
|
||||
@@ -80,10 +78,10 @@ export function FullAccessBullet() {
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Must be child of a <ul className="fa-ul">
|
||||
export function SupportMissionBullet() {
|
||||
export const SupportMissionBullet = () => {
|
||||
const missionInBoldText = (
|
||||
<span className="font-weight-bold">
|
||||
<FormattedMessage
|
||||
@@ -104,4 +102,4 @@ export function SupportMissionBullet() {
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -29,22 +29,20 @@ function getAlertIcon(type) {
|
||||
}
|
||||
}
|
||||
|
||||
function Alert({
|
||||
const Alert = ({
|
||||
type, dismissible, children, onDismiss, stacked,
|
||||
}) {
|
||||
return (
|
||||
<ParagonAlert
|
||||
data-testid={`alert-container-${type}`}
|
||||
variant={getAlertVariant(type)}
|
||||
icon={getAlertIcon(type)}
|
||||
dismissible={dismissible}
|
||||
onClose={onDismiss}
|
||||
stacked={stacked}
|
||||
>
|
||||
{children}
|
||||
</ParagonAlert>
|
||||
);
|
||||
}
|
||||
}) => (
|
||||
<ParagonAlert
|
||||
data-testid={`alert-container-${type}`}
|
||||
variant={getAlertVariant(type)}
|
||||
icon={getAlertIcon(type)}
|
||||
dismissible={dismissible}
|
||||
onClose={onDismiss}
|
||||
stacked={stacked}
|
||||
>
|
||||
{children}
|
||||
</ParagonAlert>
|
||||
);
|
||||
|
||||
Alert.propTypes = {
|
||||
type: PropTypes.oneOf([
|
||||
|
||||
@@ -4,9 +4,9 @@ import PropTypes from 'prop-types';
|
||||
import UserMessagesContext from './UserMessagesContext';
|
||||
import Alert from './Alert';
|
||||
|
||||
export default function AlertList({
|
||||
const AlertList = ({
|
||||
topic, className, customAlerts, customProps,
|
||||
}) {
|
||||
}) => {
|
||||
const { remove, messages } = useContext(UserMessagesContext);
|
||||
const getAlertComponent = useCallback(
|
||||
(code) => (customAlerts[code] !== undefined ? customAlerts[code] : Alert),
|
||||
@@ -38,7 +38,7 @@ export default function AlertList({
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
AlertList.propTypes = {
|
||||
className: PropTypes.string,
|
||||
@@ -60,3 +60,5 @@ AlertList.defaultProps = {
|
||||
customAlerts: {},
|
||||
customProps: {},
|
||||
};
|
||||
|
||||
export default AlertList;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import React, {
|
||||
useState, useRef, useEffect, useMemo,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import UserMessagesContext from './UserMessagesContext';
|
||||
@@ -27,7 +29,7 @@ function popFlashMessages() {
|
||||
return popLocalStorage(FLASH_MESSAGES_LOCAL_STORAGE_KEY) || [];
|
||||
}
|
||||
|
||||
export default function UserMessagesProvider({ children }) {
|
||||
const UserMessagesProvider = ({ children }) => {
|
||||
// Note: The callbacks (add, remove, clear) below interact with useState in very subtle ways.
|
||||
// When we call setMessages, we always do so with the function-based form of the handler, making
|
||||
// use of the "current" state and not relying on lexical scoping to access the state exposed
|
||||
@@ -81,20 +83,20 @@ export default function UserMessagesProvider({ children }) {
|
||||
flashMessages.forEach(flashMessage => add(flashMessage));
|
||||
}, []);
|
||||
|
||||
const value = {
|
||||
const value = useMemo(() => ({
|
||||
add,
|
||||
addFlash,
|
||||
remove,
|
||||
clear,
|
||||
messages,
|
||||
};
|
||||
}), [messages]);
|
||||
|
||||
return (
|
||||
<UserMessagesContext.Provider value={value}>
|
||||
{children}
|
||||
</UserMessagesContext.Provider>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
UserMessagesProvider.propTypes = {
|
||||
children: PropTypes.node,
|
||||
@@ -103,3 +105,5 @@ UserMessagesProvider.propTypes = {
|
||||
UserMessagesProvider.defaultProps = {
|
||||
children: null,
|
||||
};
|
||||
|
||||
export default UserMessagesProvider;
|
||||
|
||||
@@ -31,5 +31,6 @@ export function useAlert(isVisible, {
|
||||
return () => {
|
||||
remove(cleanupId);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isVisible, code, text, topic, type, payload, dismissible]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user