From e5e73e40bad9667b8bf14317d53bd577015fc915 Mon Sep 17 00:00:00 2001 From: Kshitij Sobti Date: Wed, 12 Oct 2022 12:57:42 +0000 Subject: [PATCH 01/92] feat: update discussion sidebar url to allow grouping by subsection (#968) To enable grouping by subsection in the discussions MFE, this PR updates the embed URL to the one that supports grouping. ref: https://github.com/openedx/frontend-app-discussions/pull/281 --- .../course/sidebar/sidebars/discussions/DiscussionsSidebar.jsx | 2 +- .../sidebar/sidebars/discussions/DiscussionsSidebar.test.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/discussions/DiscussionsSidebar.jsx b/src/courseware/course/sidebar/sidebars/discussions/DiscussionsSidebar.jsx index 13f4e972..ef463a74 100644 --- a/src/courseware/course/sidebar/sidebars/discussions/DiscussionsSidebar.jsx +++ b/src/courseware/course/sidebar/sidebars/discussions/DiscussionsSidebar.jsx @@ -19,7 +19,7 @@ function DiscussionsSidebar({ intl }) { if (!topic?.id) { return null; } - const discussionsUrl = `${getConfig().DISCUSSIONS_MFE_BASE_URL}/${courseId}/topics/${topic.id}`; + const discussionsUrl = `${getConfig().DISCUSSIONS_MFE_BASE_URL}/${courseId}/category/${unitId}`; return ( { renderWithProvider(); expect(screen.queryByTitle('Discussions')).toBeInTheDocument(); expect(screen.queryByTitle('Discussions')) - .toHaveAttribute('src', `http://localhost:2002/${courseId}/topics/topic-1?inContext`); + .toHaveAttribute('src', `http://localhost:2002/${courseId}/category/${unitId}?inContext`); }); it('should show nothing if unit has no discussions associated with it', async () => { From 7c4200e9d36adc5b778fff23cee999789462f566 Mon Sep 17 00:00:00 2001 From: Abderraouf Mehdi Bouhali Date: Fri, 14 Oct 2022 17:49:37 +0100 Subject: [PATCH 02/92] fix(rtl): mirror position of grade rectangles in grade bar (#980) Translates the rectangles for current and passing grades when to appear on the right when in RTL. --- .../progress-tab/grades/course-grade/GradeBar.jsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/course-home/progress-tab/grades/course-grade/GradeBar.jsx b/src/course-home/progress-tab/grades/course-grade/GradeBar.jsx index fb8b707c..a0f72eb3 100644 --- a/src/course-home/progress-tab/grades/course-grade/GradeBar.jsx +++ b/src/course-home/progress-tab/grades/course-grade/GradeBar.jsx @@ -2,7 +2,9 @@ import React from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { + getLocale, injectIntl, intlShape, isRtl, +} from '@edx/frontend-platform/i18n'; import { useModel } from '../../../../generic/model-store'; import CurrentGradeTooltip from './CurrentGradeTooltip'; import PassingGradeTooltip from './PassingGradeTooltip'; @@ -26,14 +28,16 @@ function GradeBar({ intl, passingGrade }) { const lockedTooltipClassName = gradesFeatureIsFullyLocked ? 'locked-overlay' : ''; + const adjustedRtlStyle = (percentOffest) => (isRtl(getLocale()) ? { transform: `translateX(${100 - percentOffest}%)` } : {}); + return (
{intl.formatMessage(messages.courseGradeBarAltText, { currentGrade, passingGrade })}
{intl.formatMessage(messages.weightedGradeSummary)}
-
{totalGrade}%
+
{totalGrade}{isLocaleRtl && '\u200f'}%
); From be72e36a3a34597e9f93309c0e2afe1950244037 Mon Sep 17 00:00:00 2001 From: Diana Olarte Date: Thu, 27 Oct 2022 09:01:43 -0500 Subject: [PATCH 05/92] feat: allow runtime configuration (#955) Allows frontend-app-learning to be configured at runtime using the LMS's new MFE Configuration API. Part of openedx/frontend-wg#103 --- package-lock.json | 682 +++++++++++++++--- package.json | 8 +- .../ActiveEnterpriseAlert.test.jsx | 2 +- .../course-start-alert/CourseStartAlert.jsx | 4 +- .../outline-tab/OutlineTab.test.jsx | 2 +- .../course-end-alert/CourseEndAlert.jsx | 4 +- .../progress-tab/ProgressTab.test.jsx | 9 +- .../CoursewareRedirectLandingPage.test.jsx | 3 +- .../course/CourseBreadcrumbs.test.jsx | 19 +- .../course/course-exit/CourseExit.test.jsx | 1 + .../course/sequence/SequenceContent.test.jsx | 2 +- .../course/sequence/content-lock/messages.js | 2 +- src/index.jsx | 5 + .../StreakCelebrationModal.test.jsx | 4 +- 14 files changed, 612 insertions(+), 135 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1240f899..4ecdd7cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "license": "AGPL-3.0", "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@1.1.0", - "@edx/frontend-component-footer": "10.2.4", - "@edx/frontend-component-header": "2.4.6", - "@edx/frontend-lib-special-exams": "1.16.3", - "@edx/frontend-platform": "1.15.6", + "@edx/frontend-component-footer": "11.1.0", + "@edx/frontend-component-header": "3.1.0", + "@edx/frontend-lib-special-exams": "2.0.0", + "@edx/frontend-platform": "2.5.1", "@edx/paragon": "19.18.3", "@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -3056,9 +3056,9 @@ } }, "node_modules/@edx/frontend-component-footer": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-10.2.4.tgz", - "integrity": "sha512-kmNHL+D4ieB2VGhJ+Hf/Oa07NvmHJqyuSRWHMIph83GXhGh1EfZkmQPw6w7Z1TxLWaKQMr1HfZnQ1+Tz/nNAjQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-11.1.0.tgz", + "integrity": "sha512-cbcUj3we2whEDQl1GkVcSrfVmD7xFyVCKO9MwCVZyXiSGbymHxEc0A+LuCkZAY22kmOkv5SG9H3rmG1qG7DIeQ==", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -3067,7 +3067,7 @@ "@fortawesome/react-fontawesome": "0.1.18" }, "peerDependencies": { - "@edx/frontend-platform": "^1.8.0", + "@edx/frontend-platform": "^2.3.0", "prop-types": "^15.5.10", "react": "^16.9.0", "react-dom": "^16.9.0" @@ -3086,9 +3086,9 @@ } }, "node_modules/@edx/frontend-component-header": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-2.4.6.tgz", - "integrity": "sha512-bwEP3B37N4lIDPM4cz/Dg1egQHWf14qj+3VU44BOf5DC/bNhASKeeEJEvDn6XTZdFCpGioHRBtqlEJQJsp0WqA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-3.1.0.tgz", + "integrity": "sha512-HeeEzgH7BAeVldAoyNMri+02c4fQdx6FY16S4pinJYVmHoK+xcUf4mw2ESUk3XyAhFUmC77tjLF+EHE20WsReA==", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -3100,7 +3100,7 @@ "react-transition-group": "4.4.2" }, "peerDependencies": { - "@edx/frontend-platform": "^1.8.0", + "@edx/frontend-platform": "^2.0.0", "@edx/paragon": ">= 7.0.0 < 20.0.0", "prop-types": "^15.5.10", "react": "^16.9.0", @@ -3120,9 +3120,9 @@ } }, "node_modules/@edx/frontend-lib-special-exams": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-1.16.3.tgz", - "integrity": "sha512-i88xa/msZgh1vgcgTSihG19C+gQcG9AUsxzuamtKKA8swBtK5cDyknsE7XY4RB5z1IE1Wj1x4Bpi4DS6bxBkiQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.0.0.tgz", + "integrity": "sha512-GjwxsEt9QW4miAg3U94o70AvckKopQ9Na1eMQMVHXBzD+QHU/hJDHDV+fRbUhenCPld+3tZIdCUT+zkMX5PwDA==", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.34", "@fortawesome/free-brands-svg-icons": "5.11.2", @@ -3133,7 +3133,7 @@ "eventemitter3": "^4.0.7" }, "peerDependencies": { - "@edx/frontend-platform": "^1.8.4", + "@edx/frontend-platform": "^2.3.0", "@edx/paragon": "^19.14.1", "@reduxjs/toolkit": "^1.5.1", "prop-types": "^15.7.2", @@ -3203,11 +3203,13 @@ } }, "node_modules/@edx/frontend-platform": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.15.6.tgz", - "integrity": "sha512-hvcJwRLy4JBdyBjHgu11nrqmMTWI901q6Ax83pf+yQpz68PpsJ0KdFjerxnkNJjU//XrWUuhSLesOPY2ntIjjg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.5.1.tgz", + "integrity": "sha512-iBnfo502VTnQnFM7G2PK6S9FTUQEExqIIw5Y6cFC/BbEzeMaD2rFW68IBrH8FMUZ1MeUCw4l0gV7WqydUtFoYA==", "dependencies": { "@cospired/i18n-iso-languages": "2.2.0", + "@formatjs/intl-pluralrules": "4.3.3", + "@formatjs/intl-relativetimeformat": "10.0.1", "axios": "0.26.1", "axios-cache-adapter": "2.7.3", "form-urlencoded": "4.1.4", @@ -3222,15 +3224,14 @@ "lodash.merge": "4.6.2", "lodash.snakecase": "4.1.1", "pubsub-js": "1.9.4", - "react-intl": "2.9.0", + "react-intl": "^5.25.0", "universal-cookie": "4.0.4" }, "bin": { - "transifex-Makefile": "i18n/scripts/Makefile", "transifex-utils.js": "i18n/scripts/transifex-utils.js" }, "peerDependencies": { - "@edx/paragon": ">= 10.0.0 < 20.0.0", + "@edx/paragon": ">= 10.0.0 < 21.0.0", "prop-types": "^15.7.2", "react": "^16.9.0", "react-dom": "^16.9.0", @@ -3260,22 +3261,6 @@ "value-equal": "^1.0.1" } }, - "node_modules/@edx/frontend-platform/node_modules/react-intl": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz", - "integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==", - "dependencies": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" - }, - "peerDependencies": { - "prop-types": "^15.5.4", - "react": "^0.14.9 || ^15.0.0 || ^16.0.0" - } - }, "node_modules/@edx/new-relic-source-map-webpack-plugin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-1.0.1.tgz", @@ -3372,6 +3357,149 @@ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, + "node_modules/@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/fast-memoize/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-displaynames/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-localematcher/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/@formatjs/intl-numberformat": { "version": "5.7.6", "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz", @@ -3397,6 +3525,68 @@ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, + "node_modules/@formatjs/intl-pluralrules": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz", + "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-pluralrules/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-pluralrules/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/intl-relativetimeformat": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz", + "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-relativetimeformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-relativetimeformat/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/@formatjs/intl/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/@formatjs/ts-transformer": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz", @@ -14191,34 +14381,31 @@ "node": ">= 0.10" } }, - "node_modules/intl-format-cache": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.9.tgz", - "integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ==" - }, "node_modules/intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", "dependencies": { - "intl-messageformat-parser": "1.4.0" + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" } }, - "node_modules/intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=", - "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser" - }, - "node_modules/intl-relativeformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz", - "integrity": "sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==", - "deprecated": "This package has been deprecated, please see migration guide at 'https://github.com/formatjs/formatjs/tree/master/packages/intl-relativeformat#migration-guide'", + "node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", "dependencies": { - "intl-messageformat": "^2.0.0" + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" } }, + "node_modules/intl-messageformat/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/into-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", @@ -22426,6 +22613,46 @@ "react": ">=16.3.0" } }, + "node_modules/react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "^16.3.0 || 17 || 18", + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-intl/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/react-intl/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -26060,7 +26287,7 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -29944,9 +30171,9 @@ } }, "@edx/frontend-component-footer": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-10.2.4.tgz", - "integrity": "sha512-kmNHL+D4ieB2VGhJ+Hf/Oa07NvmHJqyuSRWHMIph83GXhGh1EfZkmQPw6w7Z1TxLWaKQMr1HfZnQ1+Tz/nNAjQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-11.1.0.tgz", + "integrity": "sha512-cbcUj3we2whEDQl1GkVcSrfVmD7xFyVCKO9MwCVZyXiSGbymHxEc0A+LuCkZAY22kmOkv5SG9H3rmG1qG7DIeQ==", "requires": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -29966,9 +30193,9 @@ } }, "@edx/frontend-component-header": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-2.4.6.tgz", - "integrity": "sha512-bwEP3B37N4lIDPM4cz/Dg1egQHWf14qj+3VU44BOf5DC/bNhASKeeEJEvDn6XTZdFCpGioHRBtqlEJQJsp0WqA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-3.1.0.tgz", + "integrity": "sha512-HeeEzgH7BAeVldAoyNMri+02c4fQdx6FY16S4pinJYVmHoK+xcUf4mw2ESUk3XyAhFUmC77tjLF+EHE20WsReA==", "requires": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -29991,9 +30218,9 @@ } }, "@edx/frontend-lib-special-exams": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-1.16.3.tgz", - "integrity": "sha512-i88xa/msZgh1vgcgTSihG19C+gQcG9AUsxzuamtKKA8swBtK5cDyknsE7XY4RB5z1IE1Wj1x4Bpi4DS6bxBkiQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.0.0.tgz", + "integrity": "sha512-GjwxsEt9QW4miAg3U94o70AvckKopQ9Na1eMQMVHXBzD+QHU/hJDHDV+fRbUhenCPld+3tZIdCUT+zkMX5PwDA==", "requires": { "@fortawesome/fontawesome-svg-core": "1.2.34", "@fortawesome/free-brands-svg-icons": "5.11.2", @@ -30047,11 +30274,13 @@ } }, "@edx/frontend-platform": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.15.6.tgz", - "integrity": "sha512-hvcJwRLy4JBdyBjHgu11nrqmMTWI901q6Ax83pf+yQpz68PpsJ0KdFjerxnkNJjU//XrWUuhSLesOPY2ntIjjg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.5.1.tgz", + "integrity": "sha512-iBnfo502VTnQnFM7G2PK6S9FTUQEExqIIw5Y6cFC/BbEzeMaD2rFW68IBrH8FMUZ1MeUCw4l0gV7WqydUtFoYA==", "requires": { "@cospired/i18n-iso-languages": "2.2.0", + "@formatjs/intl-pluralrules": "4.3.3", + "@formatjs/intl-relativetimeformat": "10.0.1", "axios": "0.26.1", "axios-cache-adapter": "2.7.3", "form-urlencoded": "4.1.4", @@ -30066,7 +30295,7 @@ "lodash.merge": "4.6.2", "lodash.snakecase": "4.1.1", "pubsub-js": "1.9.4", - "react-intl": "2.9.0", + "react-intl": "^5.25.0", "universal-cookie": "4.0.4" }, "dependencies": { @@ -30090,18 +30319,6 @@ "tiny-warning": "^1.0.0", "value-equal": "^1.0.1" } - }, - "react-intl": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz", - "integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==", - "requires": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" - } } } }, @@ -30191,6 +30408,169 @@ } } }, + "@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, "@formatjs/intl-numberformat": { "version": "5.7.6", "resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz", @@ -30218,6 +30598,58 @@ } } }, + "@formatjs/intl-pluralrules": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz", + "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "@formatjs/intl-relativetimeformat": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz", + "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, "@formatjs/ts-transformer": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz", @@ -38701,30 +39133,31 @@ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, - "intl-format-cache": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.9.tgz", - "integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ==" - }, "intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", "requires": { - "intl-messageformat-parser": "1.4.0" - } - }, - "intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" - }, - "intl-relativeformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz", - "integrity": "sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==", - "requires": { - "intl-messageformat": "^2.0.0" + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } } }, "into-stream": { @@ -44974,6 +45407,39 @@ "react-side-effect": "^2.1.0" } }, + "react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -47831,7 +48297,7 @@ "version": "4.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", - "dev": true + "devOptional": true }, "unbox-primitive": { "version": "1.0.1", diff --git a/package.json b/package.json index f736b71f..c1c206ce 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,10 @@ }, "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@1.1.0", - "@edx/frontend-component-footer": "10.2.4", - "@edx/frontend-component-header": "2.4.6", - "@edx/frontend-lib-special-exams": "1.16.3", - "@edx/frontend-platform": "1.15.6", + "@edx/frontend-component-footer": "11.1.0", + "@edx/frontend-component-header": "3.1.0", + "@edx/frontend-lib-special-exams": "2.0.0", + "@edx/frontend-platform": "2.5.1", "@edx/paragon": "19.18.3", "@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/free-brands-svg-icons": "5.15.4", diff --git a/src/alerts/active-enteprise-alert/ActiveEnterpriseAlert.test.jsx b/src/alerts/active-enteprise-alert/ActiveEnterpriseAlert.test.jsx index b631133b..f2b5422b 100644 --- a/src/alerts/active-enteprise-alert/ActiveEnterpriseAlert.test.jsx +++ b/src/alerts/active-enteprise-alert/ActiveEnterpriseAlert.test.jsx @@ -19,7 +19,7 @@ describe('ActiveEnterpriseAlert', () => { it('Shows alert message and links', () => { render(); expect(screen.getByRole('alert')).toBeInTheDocument(); - expect(screen.getByText('test message')).toBeInTheDocument(); + expect(screen.getByText('test message', { exact: false })).toBeInTheDocument(); expect(screen.getByRole('link', { name: 'change enterprise now' })).toHaveAttribute( 'href', `${getConfig().LMS_BASE_URL}/enterprise/select/active/?success_url=http%3A%2F%2Flocalhost%2Fcourse%2Ftest-course-id%2Fhome`, ); diff --git a/src/alerts/course-start-alert/CourseStartAlert.jsx b/src/alerts/course-start-alert/CourseStartAlert.jsx index 884fb624..abe666fa 100644 --- a/src/alerts/course-start-alert/CourseStartAlert.jsx +++ b/src/alerts/course-start-alert/CourseStartAlert.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { FormattedDate, FormattedMessage, - FormattedRelative, + FormattedRelativeTime, FormattedTime, } from '@edx/frontend-platform/i18n'; import { Alert } from '@edx/paragon'; @@ -26,7 +26,7 @@ function CourseStartAlert({ payload }) { const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {}; const timeRemaining = ( - { const instructorToolbar = await screen.getByTestId('instructor-toolbar'); expect(instructorToolbar).toBeInTheDocument(); expect(screen.getByText('This learner no longer has access to this course. Their access expired on', { exact: false })).toBeInTheDocument(); - expect(screen.getByText('1/1/2020')).toBeInTheDocument(); + expect(screen.getByText('1/1/2020', { exact: false })).toBeInTheDocument(); }); it('does not render banner when not masquerading', async () => { diff --git a/src/course-home/outline-tab/alerts/course-end-alert/CourseEndAlert.jsx b/src/course-home/outline-tab/alerts/course-end-alert/CourseEndAlert.jsx index 3e5d06c5..97b02519 100644 --- a/src/course-home/outline-tab/alerts/course-end-alert/CourseEndAlert.jsx +++ b/src/course-home/outline-tab/alerts/course-end-alert/CourseEndAlert.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { FormattedDate, FormattedMessage, - FormattedRelative, + FormattedRelativeTime, FormattedTime, } from '@edx/frontend-platform/i18n'; import { Alert } from '@edx/paragon'; @@ -21,7 +21,7 @@ function CourseEndAlert({ payload }) { const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {}; const timeRemaining = ( - { month: 'long', day: 'numeric', }), + { exact: false }, )).toBeInTheDocument(); }); }); @@ -1302,7 +1303,7 @@ describe('Progress Tab', () => { await act(async () => render(..., { store })); expect(screen.getByTestId('instructor-toolbar')).toBeInTheDocument(); expect(screen.getByText('This learner no longer has access to this course. Their access expired on', { exact: false })).toBeInTheDocument(); - expect(screen.getByText('1/1/2020')).toBeInTheDocument(); + expect(screen.getByText('1/1/2020', { exact: false })).toBeInTheDocument(); }); it('does not render banner when not masquerading', async () => { setMetadata({ is_enrolled: true, original_user_is_staff: true }); @@ -1315,7 +1316,7 @@ describe('Progress Tab', () => { await executeThunk(thunks.fetchProgressTab(courseId), store.dispatch); await act(async () => render(..., { store })); expect(screen.queryByText('This learner no longer has access to this course. Their access expired on', { exact: false })).not.toBeInTheDocument(); - expect(screen.queryByText('1/1/2020')).not.toBeInTheDocument(); + expect(screen.queryByText('1/1/2020', { exact: false })).not.toBeInTheDocument(); }); }); @@ -1331,7 +1332,7 @@ describe('Progress Tab', () => { await act(async () => render(..., { store })); expect(screen.getByTestId('instructor-toolbar')).toBeInTheDocument(); expect(screen.getByText('This learner does not yet have access to this course. The course starts on', { exact: false })).toBeInTheDocument(); - expect(screen.getByText('1/1/2999')).toBeInTheDocument(); + expect(screen.getByText('1/1/2999', { exact: false })).toBeInTheDocument(); }); it('does not render banner when not masquerading', async () => { setMetadata({ @@ -1343,7 +1344,7 @@ describe('Progress Tab', () => { await executeThunk(thunks.fetchProgressTab(courseId), store.dispatch); await act(async () => render(..., { store })); expect(screen.queryByText('This learner does not yet have access to this course. The course starts on', { exact: false })).not.toBeInTheDocument(); - expect(screen.queryByText('1/1/2999')).not.toBeInTheDocument(); + expect(screen.queryByText('1/1/2999', { exact: false })).not.toBeInTheDocument(); }); }); diff --git a/src/courseware/CoursewareRedirectLandingPage.test.jsx b/src/courseware/CoursewareRedirectLandingPage.test.jsx index d9c1ce41..08409380 100644 --- a/src/courseware/CoursewareRedirectLandingPage.test.jsx +++ b/src/courseware/CoursewareRedirectLandingPage.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; -import { render } from '@testing-library/react'; import { Router } from 'react-router'; import { createMemoryHistory } from 'history'; +import { render, initializeMockApp } from '../setupTest'; import CoursewareRedirectLandingPage from './CoursewareRedirectLandingPage'; const redirectUrl = jest.fn(); @@ -17,6 +17,7 @@ jest.mock('react-router', () => ({ describe('CoursewareRedirectLandingPage', () => { beforeEach(async () => { + await initializeMockApp(); delete global.location; global.location = { assign: redirectUrl }; }); diff --git a/src/courseware/course/CourseBreadcrumbs.test.jsx b/src/courseware/course/CourseBreadcrumbs.test.jsx index d4412710..23285f14 100644 --- a/src/courseware/course/CourseBreadcrumbs.test.jsx +++ b/src/courseware/course/CourseBreadcrumbs.test.jsx @@ -3,6 +3,7 @@ import { screen, render } from '@testing-library/react'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { getConfig } from '@edx/frontend-platform'; import { BrowserRouter } from 'react-router-dom'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import { useModel, useModels } from '../../generic/model-store'; import CourseBreadcrumbs from './CourseBreadcrumbs'; @@ -106,14 +107,16 @@ describe('CourseBreadcrumbs', () => { ], ]); render( - - - , + + + + , + , ); it('renders course breadcrumbs as expected', async () => { expect(screen.queryAllByRole('link')).toHaveLength(1); diff --git a/src/courseware/course/course-exit/CourseExit.test.jsx b/src/courseware/course/course-exit/CourseExit.test.jsx index 48dc0376..55eee504 100644 --- a/src/courseware/course/course-exit/CourseExit.test.jsx +++ b/src/courseware/course/course-exit/CourseExit.test.jsx @@ -411,6 +411,7 @@ describe('Course Exit Pages', () => { month: 'long', day: 'numeric', }), + { exact: false }, )).toBeInTheDocument(); }); }); diff --git a/src/courseware/course/sequence/SequenceContent.test.jsx b/src/courseware/course/sequence/SequenceContent.test.jsx index bd2d9bbe..99a3b078 100644 --- a/src/courseware/course/sequence/SequenceContent.test.jsx +++ b/src/courseware/course/sequence/SequenceContent.test.jsx @@ -14,7 +14,7 @@ describe('Sequence Content', () => { courseId: courseware.courseId, sequenceId: courseware.sequenceId, unitId: models.sequences[courseware.sequenceId].unitIds[0], - unitLoadedHandler: () => {}, + unitLoadedHandler: () => { }, }; }); diff --git a/src/courseware/course/sequence/content-lock/messages.js b/src/courseware/course/sequence/content-lock/messages.js index 872009b1..553210d3 100644 --- a/src/courseware/course/sequence/content-lock/messages.js +++ b/src/courseware/course/sequence/content-lock/messages.js @@ -8,7 +8,7 @@ const messages = defineMessages({ }, 'learn.contentLock.complete.prerequisite': { id: 'learn.contentLock.complete.prerequisite', - defaultMessage: "You must complete the prerequisite: '{prereqSectionName}' to access this content.", + defaultMessage: "You must complete the prerequisite: ''{prereqSectionName}'' to access this content.", description: 'Message shown to indicate which prerequisite the student must complete prior to accessing the locked content. {prereqSectionName} is the name of the prerequisite.', }, 'learn.contentLock.goToSection': { diff --git a/src/index.jsx b/src/index.jsx index 425ba0a1..04c0a57f 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -4,6 +4,7 @@ import 'regenerator-runtime/runtime'; import { APP_INIT_ERROR, APP_READY, subscribe, initialize, mergeConfig, + getConfig, } from '@edx/frontend-platform'; import { AppProvider, ErrorPage, PageRoute } from '@edx/frontend-platform/react'; import React from 'react'; @@ -12,6 +13,7 @@ import { Switch } from 'react-router-dom'; import { messages as footerMessages } from '@edx/frontend-component-footer'; import { messages as headerMessages } from '@edx/frontend-component-header'; +import { Helmet } from 'react-helmet'; import { fetchDiscussionTab, fetchLiveTab } from './course-home/data/thunks'; import DiscussionTab from './course-home/discussion-tab/DiscussionTab'; @@ -39,6 +41,9 @@ import CourseAccessErrorPage from './generic/CourseAccessErrorPage'; subscribe(APP_READY, () => { ReactDOM.render( + + + diff --git a/src/shared/streak-celebration/StreakCelebrationModal.test.jsx b/src/shared/streak-celebration/StreakCelebrationModal.test.jsx index eb5d744e..77c7af5e 100644 --- a/src/shared/streak-celebration/StreakCelebrationModal.test.jsx +++ b/src/shared/streak-celebration/StreakCelebrationModal.test.jsx @@ -96,8 +96,8 @@ describe('Loaded Tab Page', () => { await renderModal(); const endDateText = `Ends ${new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toLocaleDateString({ timeZone: 'UTC' })}.`; - expect(screen.getByText('You’ve unlocked a 14% off discount when you upgrade this course for a limited time only.')).toBeInTheDocument(); - expect(screen.getByText(endDateText)).toBeInTheDocument(); + expect(screen.getByText('You’ve unlocked a 14% off discount when you upgrade this course for a limited time only.', { exact: false })).toBeInTheDocument(); + expect(screen.getByText(endDateText, { exact: false })).toBeInTheDocument(); expect(screen.getByText('Continue with course')).toBeInTheDocument(); expect(screen.queryByText('Keep it up')).not.toBeInTheDocument(); expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.course.streak_discount_enabled', { From 7e53ddb685fa56f9788a1381547fa3540ed1fa3f Mon Sep 17 00:00:00 2001 From: Jenkins Date: Sun, 30 Oct 2022 20:59:16 +0000 Subject: [PATCH 06/92] chore(i18n): update translations --- src/i18n/messages/ar.json | 2 +- src/i18n/messages/es_419.json | 2 +- src/i18n/messages/fr.json | 2 +- src/i18n/messages/zh_CN.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 998c22ef..50450fa7 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -333,7 +333,7 @@ "notification.tray.title": "الإشعارات", "notification.tray.no.message": "ليست لديك إشعارات جديدة في الوقت الراهن.", "learn.contentLock.content.locked": "المحتوى مقفل", - "learn.contentLock.complete.prerequisite": "عليك أولا إكمال المتطلّب: '{prereqSectionName}' للوصول إلى هذا المحتوى.", + "learn.contentLock.complete.prerequisite": "You must complete the prerequisite: ''{prereqSectionName}'' to access this content.", "learn.contentLock.goToSection": "انتقل إلى القسم المتطلّب.", "learn.hiddenAfterDue.gradeAvailable": "إن كنت قد أكملت هذا الواجب، فإن درجتك ستظهر في {progressPage}.", "learn.hiddenAfterDue.header": "انقضى أجَل هذا الواجب.", diff --git a/src/i18n/messages/es_419.json b/src/i18n/messages/es_419.json index 4127affe..1cf8bfc8 100644 --- a/src/i18n/messages/es_419.json +++ b/src/i18n/messages/es_419.json @@ -333,7 +333,7 @@ "notification.tray.title": "Notificaciones", "notification.tray.no.message": "No tienes notificaciones nuevas en este momento.", "learn.contentLock.content.locked": "Contenido Bloqueado", - "learn.contentLock.complete.prerequisite": "Debe completar el prerrequisito: '{prereqSectionName}'para acceder a este contenido.", + "learn.contentLock.complete.prerequisite": "You must complete the prerequisite: ''{prereqSectionName}'' to access this content.", "learn.contentLock.goToSection": "Ir a la Sección de Prerrequisitos", "learn.hiddenAfterDue.gradeAvailable": "Si has completado esta tarea, tu calificación estará disponible en {progressPage}.", "learn.hiddenAfterDue.header": "La fecha límite para esta tarea ha pasado.", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index f4668ade..99c80e4c 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -333,7 +333,7 @@ "notification.tray.title": "Notifications", "notification.tray.no.message": "Vous n'avez aucune nouvelle notification pour le moment.", "learn.contentLock.content.locked": "Contenu vérouillé", - "learn.contentLock.complete.prerequisite": "Vous devez compléter le prérequis: '{prereqSectionName}' pour accéder à ce contenu.", + "learn.contentLock.complete.prerequisite": "You must complete the prerequisite: ''{prereqSectionName}'' to access this content.", "learn.contentLock.goToSection": "Aller à la section des prérequis", "learn.hiddenAfterDue.gradeAvailable": "Si vous avez complété ce travail, votre note est disponible sur {progressPage}.", "learn.hiddenAfterDue.header": "La date d'échéance de ce devoir est passée.", diff --git a/src/i18n/messages/zh_CN.json b/src/i18n/messages/zh_CN.json index 4b2fa33e..d3def806 100644 --- a/src/i18n/messages/zh_CN.json +++ b/src/i18n/messages/zh_CN.json @@ -333,7 +333,7 @@ "notification.tray.title": "Notifications", "notification.tray.no.message": "You have no new notifications at this time.", "learn.contentLock.content.locked": "Content Locked", - "learn.contentLock.complete.prerequisite": "You must complete the prerequisite: '{prereqSectionName}' to access this content.", + "learn.contentLock.complete.prerequisite": "You must complete the prerequisite: ''{prereqSectionName}'' to access this content.", "learn.contentLock.goToSection": "Go To Prerequisite Section", "learn.hiddenAfterDue.gradeAvailable": "If you have completed this assignment, your grade is available on the {progressPage}.", "learn.hiddenAfterDue.header": "The due date for this assignment has passed.", From df91fef82ecae79fa466bc31c60e60db601cf38f Mon Sep 17 00:00:00 2001 From: Zachary Hancock Date: Mon, 31 Oct 2022 11:49:24 -0400 Subject: [PATCH 07/92] feat: update special exams lib (#992) --- package-lock.json | 48 +++++++++++++---------------------------------- package.json | 2 +- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ecdd7cc..281a17c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@edx/brand": "npm:@edx/brand-openedx@1.1.0", "@edx/frontend-component-footer": "11.1.0", "@edx/frontend-component-header": "3.1.0", - "@edx/frontend-lib-special-exams": "2.0.0", + "@edx/frontend-lib-special-exams": "2.1.0", "@edx/frontend-platform": "2.5.1", "@edx/paragon": "19.18.3", "@fortawesome/fontawesome-svg-core": "1.3.0", @@ -3120,9 +3120,9 @@ } }, "node_modules/@edx/frontend-lib-special-exams": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.0.0.tgz", - "integrity": "sha512-GjwxsEt9QW4miAg3U94o70AvckKopQ9Na1eMQMVHXBzD+QHU/hJDHDV+fRbUhenCPld+3tZIdCUT+zkMX5PwDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.1.0.tgz", + "integrity": "sha512-ODx9czIZ6JXB48nUWn6W2lwiFwSV3SzU/6Gtp11EIWqBLNU0owPib7dibinsQGJXOygjZ0aX6V5gFyCUjPMagw==", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.34", "@fortawesome/free-brands-svg-icons": "5.11.2", @@ -18827,9 +18827,9 @@ } }, "node_modules/jquery": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", - "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==", "peer": true }, "node_modules/js-cookie": { @@ -26241,20 +26241,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz", - "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -30218,9 +30204,9 @@ } }, "@edx/frontend-lib-special-exams": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.0.0.tgz", - "integrity": "sha512-GjwxsEt9QW4miAg3U94o70AvckKopQ9Na1eMQMVHXBzD+QHU/hJDHDV+fRbUhenCPld+3tZIdCUT+zkMX5PwDA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-2.1.0.tgz", + "integrity": "sha512-ODx9czIZ6JXB48nUWn6W2lwiFwSV3SzU/6Gtp11EIWqBLNU0owPib7dibinsQGJXOygjZ0aX6V5gFyCUjPMagw==", "requires": { "@fortawesome/fontawesome-svg-core": "1.2.34", "@fortawesome/free-brands-svg-icons": "5.11.2", @@ -42524,9 +42510,9 @@ "dev": true }, "jquery": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", - "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==", "peer": true }, "js-cookie": { @@ -48260,14 +48246,6 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "type-fest": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.2.tgz", - "integrity": "sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ==", - "dev": true, - "optional": true, - "peer": true - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index c1c206ce..0052e3b6 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@edx/brand": "npm:@edx/brand-openedx@1.1.0", "@edx/frontend-component-footer": "11.1.0", "@edx/frontend-component-header": "3.1.0", - "@edx/frontend-lib-special-exams": "2.0.0", + "@edx/frontend-lib-special-exams": "2.1.0", "@edx/frontend-platform": "2.5.1", "@edx/paragon": "19.18.3", "@fortawesome/fontawesome-svg-core": "1.3.0", From 19d06d60be661c268da0721d2c4075ab8068895c Mon Sep 17 00:00:00 2001 From: Andrew Shultz Date: Thu, 3 Nov 2022 09:26:42 -0400 Subject: [PATCH 08/92] fix: display onboarding expired after expiration (#997) Currently expiring soon is displayed 28 days before expiration and forever afterwards. Adds an actual expired state for after. Also clarifies the expring soon message which assumed other course, that was not necessarily true. Also updates the take action lines when you do not have valid onboarding to make sure they appear for everything not currently valid or in process, and updates the submitted process lines to not appear for expired statuses. --- .../outline-tab/OutlineTab.test.jsx | 34 +++++++++++++++++- src/course-home/outline-tab/messages.js | 12 ++++++- .../widgets/ProctoringInfoPanel.jsx | 36 +++++++++++++------ 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/course-home/outline-tab/OutlineTab.test.jsx b/src/course-home/outline-tab/OutlineTab.test.jsx index 41335e33..c9e8aa68 100644 --- a/src/course-home/outline-tab/OutlineTab.test.jsx +++ b/src/course-home/outline-tab/OutlineTab.test.jsx @@ -1017,6 +1017,22 @@ describe('Outline Tab', () => { }); it('displays expiration warning', async () => { + const expirationDate = new Date(); + // This message will render if the expiration date is within 28 days; set the date 10 days in future + expirationDate.setTime(expirationDate.getTime() + 864800000); + axiosMock.onGet(proctoringInfoUrl).reply(200, { + onboarding_status: 'verified', + onboarding_link: 'test', + expiration_date: expirationDate.toString(), + onboarding_release_date: onboardingReleaseDate.toISOString(), + }); + await fetchAndRender(); + await screen.findByText('This course contains proctored exams'); + expect(screen.queryByText('Your onboarding profile has been approved. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.')).toBeInTheDocument(); + expect(screen.queryByText('Onboarding profile review can take 2+ business days.')).toBeInTheDocument(); + }); + + it('displays expiration warning for other course', async () => { const expirationDate = new Date(); // This message will render if the expiration date is within 28 days; set the date 10 days in future expirationDate.setTime(expirationDate.getTime() + 864800000); @@ -1028,7 +1044,23 @@ describe('Outline Tab', () => { }); await fetchAndRender(); await screen.findByText('This course contains proctored exams'); - expect(screen.queryByText('Your onboarding profile has been approved in another course. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.')).toBeInTheDocument(); + expect(screen.queryByText('Your onboarding profile has been approved. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.')).toBeInTheDocument(); + expect(screen.queryByText('Onboarding profile review can take 2+ business days.')).toBeInTheDocument(); + }); + + it('displays expired', async () => { + const expirationDate = new Date(); + // This message appears after expiration, set the date 10 days in the past + expirationDate.setTime(expirationDate.getTime() - 864800000); + axiosMock.onGet(proctoringInfoUrl).reply(200, { + onboarding_status: 'verified', + onboarding_link: 'test', + expiration_date: expirationDate.toString(), + onboarding_release_date: onboardingReleaseDate.toISOString(), + }); + await fetchAndRender(); + await screen.findByText('This course contains proctored exams'); + expect(screen.queryByText('Your onboarding status has expired. Please complete onboarding again to continue taking proctored exams.')).toBeInTheDocument(); expect(screen.queryByText('Onboarding profile review can take 2+ business days.')).toBeInTheDocument(); }); diff --git a/src/course-home/outline-tab/messages.js b/src/course-home/outline-tab/messages.js index 48c56a13..8d320459 100644 --- a/src/course-home/outline-tab/messages.js +++ b/src/course-home/outline-tab/messages.js @@ -231,6 +231,11 @@ const messages = defineMessages({ defaultMessage: 'Expiring Soon', description: 'A label to indicate that proctortrack onboarding exam will expire soon', }, + expiredProctoringStatus: { + id: 'learning.proctoringPanel.status.expired', + defaultMessage: 'Expired', + description: 'A label to indicate that proctortrack onboarding exam has expired', + }, proctoringCurrentStatus: { id: 'learning.proctoringPanel.status', defaultMessage: 'Current Onboarding Status:', @@ -278,9 +283,14 @@ const messages = defineMessages({ }, expiringSoonProctoringMessage: { id: 'learning.proctoringPanel.message.expiringSoon', - defaultMessage: 'Your onboarding profile has been approved in another course. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.', + defaultMessage: 'Your onboarding profile has been approved. However, your onboarding status is expiring soon. Please complete onboarding again to ensure that you will be able to continue taking proctored exams.', description: 'The text that recommend an action when the status of the proctortrack onboarding exam is (expiring soon)', }, + expiredProctoringMessage: { + id: 'learning.proctoringPanel.message.expired', + defaultMessage: 'Your onboarding status has expired. Please complete onboarding again to continue taking proctored exams.', + description: 'The text that recommend an action when the status of the proctortrack onboarding exam is (expired)', + }, proctoringPanelGeneralInfo: { id: 'learning.proctoringPanel.generalInfo', defaultMessage: 'You must complete the onboarding process prior to taking any proctored exam. ', diff --git a/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx b/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx index 0ee5d70e..37d90bfe 100644 --- a/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx +++ b/src/course-home/outline-tab/widgets/ProctoringInfoPanel.jsx @@ -35,6 +35,7 @@ function ProctoringInfoPanel({ intl }) { error: 'error', otherCourseApproved: 'otherCourseApproved', expiringSoon: 'expiringSoon', + expired: 'expired', }; function getReadableStatusClass(examStatus) { @@ -54,9 +55,14 @@ function ProctoringInfoPanel({ intl }) { return readableClass; } - function isNotYetSubmitted(examStatus) { - const NO_SHOW_STATES = ['submitted', 'second_review_required', 'verified']; - return !NO_SHOW_STATES.includes(examStatus); + function isCurrentlySubmitted(examStatus) { + const SUBMITTED_STATES = ['submitted', 'second_review_required']; + return SUBMITTED_STATES.includes(examStatus); + } + + function isSubmissionRequired(examStatus) { + const OK_STATES = [readableStatuses.submitted, readableStatuses.verified]; + return !OK_STATES.includes(examStatus); } function isNotYetReleased(examReleaseDate) { @@ -77,11 +83,19 @@ function ProctoringInfoPanel({ intl }) { return borderClass; } - function isExpiringSoon(dateString) { - // Returns true if the expiration date is within 28 days + function isExpired(dateString) { + // Returns true if the expiration date has passed const today = new Date(); const expirationDateObject = new Date(dateString); - return today > expirationDateObject.getTime() - 2419200000; + return today >= expirationDateObject.getTime(); + } + + function isExpiringSoon(dateString) { + // Returns true if the expiration date is within 28 days + const twentyeightDays = 28 * 24 * 60 * 60 * 1000; + const today = new Date(); + const expirationDateObject = new Date(dateString); + return today > expirationDateObject.getTime() - twentyeightDays; } useEffect(() => { @@ -96,7 +110,9 @@ function ProctoringInfoPanel({ intl }) { setStatus(response.onboarding_status); setLink(response.onboarding_link); const expirationDate = response.expiration_date; - if (expirationDate && isExpiringSoon(expirationDate)) { + if (expirationDate && isExpired(expirationDate)) { + setReadableStatus(getReadableStatusClass('expired')); + } else if (expirationDate && isExpiringSoon(expirationDate)) { setReadableStatus(getReadableStatusClass('expiringSoon')); } else { setReadableStatus(getReadableStatusClass(response.onboarding_status)); @@ -175,17 +191,17 @@ function ProctoringInfoPanel({ intl }) { {![readableStatuses.verified, readableStatuses.otherCourseApproved].includes(readableStatus) && ( <>

- {isNotYetSubmitted(status) && ( + {!isCurrentlySubmitted(status) && ( intl.formatMessage(messages.proctoringPanelGeneralInfo) )} - {!isNotYetSubmitted(status) && ( + {isCurrentlySubmitted(status) && ( intl.formatMessage(messages.proctoringPanelGeneralInfoSubmitted) )}

{intl.formatMessage(messages.proctoringPanelGeneralTime)}

)} - {isNotYetSubmitted(status) && ( + {isSubmissionRequired(readableStatus) && ( onboardingExamButton )}