Compare commits
10 Commits
ttracy/MIC
...
open-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72bce9e652 | ||
|
|
66482ab23a | ||
|
|
03e0f41692 | ||
|
|
8e1904a235 | ||
|
|
88485a0f77 | ||
|
|
361a099ed1 | ||
|
|
7f3757539a | ||
|
|
44f5132e2a | ||
|
|
53b19c9be3 | ||
|
|
abc374b60a |
6
.github/workflows/validate.yml
vendored
6
.github/workflows/validate.yml
vendored
@@ -2,7 +2,7 @@ name: validate
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [12, 14, 16]
|
||||
node: [16]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
@@ -19,6 +19,6 @@ jobs:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: make validate.ci
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
|
||||
1
.husky/_/.gitignore
vendored
Normal file
1
.husky/_/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*
|
||||
31
.husky/_/husky.sh
Normal file
31
.husky/_/husky.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
if [ -z "$husky_skip_init" ]; then
|
||||
debug () {
|
||||
if [ "$HUSKY_DEBUG" = "1" ]; then
|
||||
echo "husky (debug) - $1"
|
||||
fi
|
||||
}
|
||||
|
||||
readonly hook_name="$(basename "$0")"
|
||||
debug "starting $hook_name..."
|
||||
|
||||
if [ "$HUSKY" = "0" ]; then
|
||||
debug "HUSKY env variable is set to 0, skipping hook"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f ~/.huskyrc ]; then
|
||||
debug "sourcing ~/.huskyrc"
|
||||
. ~/.huskyrc
|
||||
fi
|
||||
|
||||
export readonly husky_skip_init=1
|
||||
sh -e "$0" "$@"
|
||||
exitCode="$?"
|
||||
|
||||
if [ $exitCode != 0 ]; then
|
||||
echo "husky - $hook_name hook exited with code $exitCode (error)"
|
||||
fi
|
||||
|
||||
exit $exitCode
|
||||
fi
|
||||
1
Makefile
1
Makefile
@@ -58,7 +58,6 @@ validate:
|
||||
npm run lint -- --max-warnings 0
|
||||
npm run test
|
||||
npm run build
|
||||
npm run is-es5
|
||||
|
||||
.PHONY: validate.ci
|
||||
validate.ci:
|
||||
|
||||
9521
package-lock.json
generated
9521
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -7,13 +7,11 @@
|
||||
"url": "git+https://github.com/edx/frontend-app-learning.git"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"ie 11"
|
||||
"extends @edx/browserslist-config"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "fedx-scripts webpack",
|
||||
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
|
||||
"is-es5": "es-check es5 ./dist/*.js",
|
||||
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
|
||||
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .",
|
||||
"prepare": "husky install",
|
||||
@@ -34,9 +32,9 @@
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/frontend-component-footer": "10.2.2",
|
||||
"@edx/frontend-component-header": "2.4.6",
|
||||
"@edx/frontend-lib-special-exams": "1.16.0",
|
||||
"@edx/frontend-lib-special-exams": "1.16.3",
|
||||
"@edx/frontend-platform": "1.15.6",
|
||||
"@edx/paragon": "19.13.6",
|
||||
"@edx/paragon": "19.14.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
@@ -49,8 +47,8 @@
|
||||
"js-cookie": "3.0.1",
|
||||
"lodash.camelcase": "4.3.0",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-helmet": "6.1.0",
|
||||
"react-redux": "7.2.8",
|
||||
"react-router": "5.2.1",
|
||||
@@ -63,6 +61,7 @@
|
||||
"util": "0.12.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/browserslist-config": "1.0.2",
|
||||
"@edx/frontend-build": "9.1.4",
|
||||
"@edx/reactifex": "1.1.0",
|
||||
"@pact-foundation/pact": "9.17.3",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useDispatch } from 'react-redux';
|
||||
@@ -46,10 +46,8 @@ function Course({
|
||||
|
||||
// Below the tabs, above the breadcrumbs alerts (appearing in the order listed here)
|
||||
const dispatch = useDispatch();
|
||||
const celebrateFirstSection = celebrations && celebrations.firstSection;
|
||||
const [firstSectionCelebrationOpen, setFirstSectionCelebrationOpen] = useState(shouldCelebrateOnSectionLoad(
|
||||
courseId, sequenceId, celebrateFirstSection, dispatch, celebrations,
|
||||
));
|
||||
|
||||
const [firstSectionCelebrationOpen, setFirstSectionCelebrationOpen] = useState(false);
|
||||
// If streakLengthToCelebrate is populated, that modal takes precedence. Wait til the next load to display
|
||||
// the weekly goal celebration modal.
|
||||
const [weeklyGoalCelebrationOpen, setWeeklyGoalCelebrationOpen] = useState(
|
||||
@@ -74,6 +72,17 @@ function Course({
|
||||
/** [MM-P2P] Experiment */
|
||||
const MMP2P = initCoursewareMMP2P(courseId, sequenceId, unitId);
|
||||
|
||||
useEffect(() => {
|
||||
const celebrateFirstSection = celebrations && celebrations.firstSection;
|
||||
setFirstSectionCelebrationOpen(shouldCelebrateOnSectionLoad(
|
||||
courseId,
|
||||
sequenceId,
|
||||
celebrateFirstSection,
|
||||
dispatch,
|
||||
celebrations,
|
||||
));
|
||||
}, [sequenceId]);
|
||||
|
||||
return (
|
||||
<SidebarProvider courseId={courseId} unitId={unitId}>
|
||||
<Helmet>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useWindowSize,
|
||||
} from '@edx/paragon';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ClapsMobile from './assets/claps_280x201.gif';
|
||||
import ClapsTablet from './assets/claps_456x328.gif';
|
||||
import messages from './messages';
|
||||
@@ -19,12 +20,13 @@ import { useModel } from '../../../generic/model-store';
|
||||
function CelebrationModal({
|
||||
courseId, intl, isOpen, onClose, ...rest
|
||||
}) {
|
||||
const { org } = useModel('courseHomeMeta', courseId);
|
||||
const { org, celebrations } = useModel('courseHomeMeta', courseId);
|
||||
const dispatch = useDispatch();
|
||||
const wideScreen = useWindowSize().width >= breakpoints.small.minWidth;
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
recordFirstSectionCelebration(org, courseId);
|
||||
recordFirstSectionCelebration(org, courseId, celebrations, dispatch);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
|
||||
@@ -15,9 +15,20 @@ function handleNextSectionCelebration(sequenceId, nextSequenceId) {
|
||||
});
|
||||
}
|
||||
|
||||
function recordFirstSectionCelebration(org, courseId) {
|
||||
function recordFirstSectionCelebration(org, courseId, celebrations, dispatch) {
|
||||
// Tell the LMS
|
||||
postCelebrationComplete(courseId, { first_section: false });
|
||||
// Update our local copy of course data from LMS
|
||||
dispatch(updateModel({
|
||||
modelType: 'courseHomeMeta',
|
||||
model: {
|
||||
id: courseId,
|
||||
celebrations: {
|
||||
...celebrations,
|
||||
firstSection: false,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Tell our analytics
|
||||
const { administrator } = getAuthenticatedUser();
|
||||
|
||||
15
src/courseware/course/celebration/utils.test.jsx
Normal file
15
src/courseware/course/celebration/utils.test.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { recordFirstSectionCelebration } from './utils';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
jest.mock('./data/api');
|
||||
jest.mock('@edx/frontend-platform/auth', () => ({
|
||||
getAuthenticatedUser: jest.fn(() => ({ administrator: 'admin' })),
|
||||
}));
|
||||
|
||||
describe('recordFirstSectionCelebration', () => {
|
||||
it('updates the local copy of the course data from the LMS', async () => {
|
||||
const dispatchMock = jest.fn();
|
||||
recordFirstSectionCelebration('org', 'courseId', 'celebration', dispatchMock);
|
||||
expect(dispatchMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -96,6 +96,7 @@ function Unit({
|
||||
const [showError, setShowError] = useState(false);
|
||||
const [modalOptions, setModalOptions] = useState({ open: false });
|
||||
const [shouldDisplayHonorCode, setShouldDisplayHonorCode] = useState(false);
|
||||
const [windowTopOffset, setWindowTopOffset] = useState(null);
|
||||
|
||||
const unit = useModel('units', id);
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
@@ -123,6 +124,13 @@ function Unit({
|
||||
} = data;
|
||||
if (type === 'plugin.resize') {
|
||||
setIframeHeight(payload.height);
|
||||
|
||||
// We observe exit from the video xblock full screen mode
|
||||
// and do page scroll to the previously saved scroll position
|
||||
if (windowTopOffset !== null) {
|
||||
window.scrollTo(0, Number(windowTopOffset));
|
||||
}
|
||||
|
||||
if (!hasLoaded && iframeHeight === 0 && payload.height > 0) {
|
||||
setHasLoaded(true);
|
||||
if (onLoaded) {
|
||||
@@ -132,12 +140,16 @@ function Unit({
|
||||
} else if (type === 'plugin.modal') {
|
||||
payload.open = true;
|
||||
setModalOptions(payload);
|
||||
} else if (type === 'plugin.videoFullScreen') {
|
||||
// We listen for this message from LMS to know when we need to
|
||||
// save or reset scroll position on toggle video xblock full screen mode.
|
||||
setWindowTopOffset(payload.open ? window.scrollY : null);
|
||||
} else if (data.offset) {
|
||||
// We listen for this message from LMS to know when the page needs to
|
||||
// be scrolled to another location on the page.
|
||||
window.scrollTo(0, data.offset + document.getElementById('unit-iframe').offsetTop);
|
||||
}
|
||||
}, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded]);
|
||||
}, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded, setWindowTopOffset, windowTopOffset]);
|
||||
useEventListener('message', receiveMessage);
|
||||
useEffect(() => {
|
||||
sendUrlHashToFrame(document.getElementById('unit-iframe'));
|
||||
|
||||
@@ -131,6 +131,21 @@ describe('Unit', () => {
|
||||
expect(window.scrollY === testMessageWithOffset.offset);
|
||||
});
|
||||
|
||||
it('scrolls page on MessagaeEvent when receiving videoFullScreen state', async () => {
|
||||
// Set message to constain video full screen data.
|
||||
const defaultTopOffset = 800;
|
||||
const testMessageWithOtherHeight = { ...messageEvent, payload: { height: 500 } };
|
||||
const testMessageWithFullscreenState = (isOpen) => ({ type: 'plugin.videoFullScreen', payload: { open: isOpen } });
|
||||
render(<Unit {...mockData} />);
|
||||
Object.defineProperty(window, 'scrollY', { value: defaultTopOffset, writable: true });
|
||||
window.postMessage(testMessageWithFullscreenState(true), '*');
|
||||
window.postMessage(testMessageWithFullscreenState(false), '*');
|
||||
window.postMessage(testMessageWithOtherHeight, '*');
|
||||
|
||||
await expect(waitFor(() => expect(window.scrollTo()).toHaveBeenCalledTimes(1)));
|
||||
expect(window.scrollY === defaultTopOffset);
|
||||
});
|
||||
|
||||
it('ignores MessageEvent with unhandled type', async () => {
|
||||
// Clone message and set different type.
|
||||
const testMessageWithUnhandledType = { ...messageEvent, type: 'wrong type' };
|
||||
|
||||
@@ -98,9 +98,9 @@ export function SupportMissionBullet() {
|
||||
<CheckmarkBullet />
|
||||
<FormattedMessage
|
||||
id="learning.generic.upsell.supportMissionBullet"
|
||||
defaultMessage="Support our {missionInBoldText} at edX"
|
||||
defaultMessage="Support our {missionInBoldText} at {siteName}"
|
||||
description="Bullet encouraging user to support edX's goals."
|
||||
values={{ missionInBoldText }}
|
||||
values={{ missionInBoldText, siteName: getConfig().SITE_NAME }}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
"learning.goals.unsubscribe.header": "قمت بالغاء اشتراكك في شعارات التذكيرية لاهداف",
|
||||
"learning.goals.unsubscribe.loading": "يجري الغاء الاشتراك...",
|
||||
"learning.goals.unsubscribe.errorDescription": "لم نستطع الغاء اشتراكك في اشعارات التذكيرية باهدافك على بريدك الالكتروني. رجاءا حاول مرة اخرى او {contactSupport} للمساعدة.",
|
||||
"learning.outline.alert.cert.when": "تنتهي هذه الدورة التدريبية في {courseEndDateFormatted}. تمت جدولة الدرجات النهائية والشهادات\n لتكون متاحة بعد {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header": "ستكون درجتك وشهادتك جاهزة قريبًا!",
|
||||
"learning.outline.alert.cert.earnedNotAvailable": "This course ends on {courseEndDateFormatted}. Final grades and any earned certificates are\n scheduled to be available after {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header.v2": "Your grade and certificate status will be available soon.",
|
||||
"cert.alert.earned.ready.header": "ألف مبروك! شهادتك جاهزة.",
|
||||
"cert.alert.notPassing.header": "أنت غير مؤهل بعد للحصول على شهادة",
|
||||
"cert.alert.notPassing.button": "عرض الدرجات",
|
||||
@@ -113,7 +113,7 @@
|
||||
"learning.outline.sequence-due": "{description} في{assignmentDue}",
|
||||
"progress.certificateStatus.unverifiedBody": "لإنشاء شهادة ، يجب عليك إكمال عملية التحقق من الهوية. {idVerificationSupportLink}.",
|
||||
"progress.certificateStatus.downloadableBody": "اعرض إنجازاتك على لينكد ان أو على سيرتك الذاتية اليوم. يمكنك تنزيل شهادتك الآن والوصول إليها في أي وقت من لوحة التحكم والملف الشخصي.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "تنتهي هذه الدورة التدريبية في {endDate} ومن المقرر أن تكون الدرجات النهائية والشهادات\n متاحة بعد {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailabilityDate}.",
|
||||
"progress.certificateStatus.notPassingHeader": "حالة الشهادة",
|
||||
"progress.certificateStatus.notPassingBody": "من أجل التأهل للحصول على شهادة ، يجب أن تكون حاصلاً على درجة النجاح.",
|
||||
"progress.certificateStatus.inProgressHeader": "المزيد من المحتوى قريبا!",
|
||||
@@ -131,7 +131,7 @@
|
||||
"progress.certificateStatus.upgradeHeader": "احصل على شهادة.",
|
||||
"progress.certificateStatus.upgradeBody": "أنت في مسجل في المساق كمستمع ولست مؤهلاً للحصول على شهادة. من أجل الحصول على شهادة ، قم بترقية تسجيلك في المسافق اليوم.",
|
||||
"progress.certificateStatus.upgradeButton": "الترقية الآن",
|
||||
"progress.certificateStatus.unverifiedHomeHeader": "يجب التحقق من هويتك لتتمكن من الحصول على شهادة!",
|
||||
"progress.certificateStatus.unverifiedHomeHeader.v2": "Verify your identity to qualify for a certificate.",
|
||||
"progress.certificateStatus.unverifiedHomeButton": "تحقق من هويتي",
|
||||
"progress.certificateStatus.unverifiedHomeBody": "من أجل إنشاء شهادة لهذه الدورة ، يجب عليك إكمال عملية التحقق من الهوية.",
|
||||
"progress.completion.donut.label": "مكتمل",
|
||||
@@ -262,6 +262,7 @@
|
||||
"notes.button.hide": "إخفاء الملاحظات",
|
||||
"courseExit.catalogSearchSuggestion": "هل تطمح إلى تعلّم المزيد؟{searchOurCatalogLink} لاستكشاف المزيد من المساقات والبرامج.",
|
||||
"courseCelebration.certificateBody.available": "اعرض إنجازاتك على لينكد إن أو سيرتك الذاتية اليوم.\nيمكنك تنزيل الشهادة الآن والوصول إليها في أي وقت من\n{dashboardLink} و{profileLink}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate.v2": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.unverified": "لإنشاء شهادة يجب عليك إتمام عملية التحقق من الهوية.\n{idVerificationSupportLink} الآن.",
|
||||
"courseCelebration.certificateBody.upgradable": "لم يفت الأوان للترقية. بالنسبة لـ {price} ستقوم بإلغاء تأمين الوصول إلى كافة أنواع \nالواجبات في هذا المساق. عند الانتهاء، ستحصل على شهادة تم التحقق منها وهي إحدى\nالوثائق القيّمة لتحسين فرصك الوظيفية وتطويرك المهني، أو لتسليط الضوء على\nشهادة في التطبيقات التعليمية.",
|
||||
"courseCelebration.upgradeDiscountCodePrompt": "استخدم الرمز {code} عند إتمام الطلب لخصم {percent}%!",
|
||||
@@ -274,7 +275,7 @@
|
||||
"courseCelebration.dashboardInfo": "يمكنك الوصول إلى هذا المساق ومواده على لوحة معلوماتك {dashboardLink}.",
|
||||
"courseExit.programs.applyForCredit": "تقدم بطلب للحصول على ائتمان",
|
||||
"courseCelebration.certificateHeader.downloadable": "!شهادتك جاهزة",
|
||||
"courseCelebration.certificateHeader.notAvailable": "سيكون كل من درجتك وشهادتك جاهزين قريبًا!",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Your grade and certificate status will be available soon.",
|
||||
"courseCelebration.certificateBody.notAvailable.accessCertificate": "إذا كنت قد حصلت على درجة النجاح ، فسيتم إصدار شهادتك تلقائيًا.",
|
||||
"courseCelebration.certificateHeader.unverified": "يجب إكمال عملية التحقق للحصول على شهادتك",
|
||||
"courseCelebration.certificateHeader.requestable": "تهانينا، لقد تأهلت للحصول على شهادة!",
|
||||
@@ -396,7 +397,7 @@
|
||||
"learning.generic.upsell.fullAccessBullet.fullAccess": "Full access",
|
||||
"learning.generic.upsell.fullAccessBullet": "{fullAccessInBoldText} to course content and materials, even after the course ends",
|
||||
"learning.generic.upsell.supportMissionBullet.mission": "mission",
|
||||
"learning.generic.upsell.supportMissionBullet": "ادعم {missionInBoldText} في edX",
|
||||
"learning.generic.upsell.supportMissionBullet": "ادعم {missionInBoldText} في {siteName}",
|
||||
"masquerade-widget.userName.error.generic": "حدث خطأ؛ يرجى المحاولة مرة أخرى.",
|
||||
"masquerade-widget.userName.input.placeholder": "اسم المستخدم أو البريد الإلكتروني",
|
||||
"masquerade-widget.userName.input.label": "عرف كهذا المستخدم",
|
||||
@@ -408,7 +409,7 @@
|
||||
"tours.button.okay": "تمام",
|
||||
"tours.button.beginTour": "ابدأ الجولة",
|
||||
"tours.button.launchTour": "انطلاق الجولة",
|
||||
"tours.newUserModal.body": "Let’s take a quick tour of edX so you can get the most out of your course.",
|
||||
"tours.newUserModal.body": "Let’s take a quick tour of {siteName} so you can get the most out of your course.",
|
||||
"tours.newUserModal.title.welcome": "مرحبًا بك في",
|
||||
"tours.button.skipForNow": "تخطي في الوقت الراهن",
|
||||
"tours.datesCheckpoint.body": "Important dates can help you stay on track.",
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
"learning.goals.unsubscribe.header": "Te has desinscrito de los recordatorios de objetivos.",
|
||||
"learning.goals.unsubscribe.loading": "Desinscribiendo...",
|
||||
"learning.goals.unsubscribe.errorDescription": "No fue posible desinscribirte de tus correos de recordatorios de objetivos. Por favor inténtalo más tarde o ponte en contacto con el equipo de soporte para solicitar ayuda {contactSupport}",
|
||||
"learning.outline.alert.cert.when": "Este curso finaliza el {courseEndDateFormatted}. Las calificaciones finales y los certificados están\n programados para estar disponibles después de {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header": "Tu calificación y tu certificado estarán listos en breve.",
|
||||
"learning.outline.alert.cert.earnedNotAvailable": "This course ends on {courseEndDateFormatted}. Final grades and any earned certificates are\n scheduled to be available after {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header.v2": "Your grade and certificate status will be available soon.",
|
||||
"cert.alert.earned.ready.header": "¡Felicitaciones! Tu certificado está listo.",
|
||||
"cert.alert.notPassing.header": "Aún no eres elegible para obtener un certificado",
|
||||
"cert.alert.notPassing.button": "Ver calificaciones",
|
||||
@@ -113,7 +113,7 @@
|
||||
"learning.outline.sequence-due": "Fecha límite para {description}: {assignmentDue}",
|
||||
"progress.certificateStatus.unverifiedBody": "Para generar un certificado, debes completar la verificación de identidad. {idVerificationSupportLink}.",
|
||||
"progress.certificateStatus.downloadableBody": "Muestra tu logro en LinkedIn o en tu currículum. Puedes descargar tu certificado ahora y acceder a él en cualquier momento desde tu panel de estudiante y tu perfil.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "Este curso terminó el {endDate} y las calificaciones finales y los certificados están programados para estar\ndisponibles después de {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailabilityDate}.",
|
||||
"progress.certificateStatus.notPassingHeader": "Estado del certificado",
|
||||
"progress.certificateStatus.notPassingBody": "Para poder obtener un certificado, es necesario tener una calificación de aprobado.",
|
||||
"progress.certificateStatus.inProgressHeader": "¡Pronto habrá más contenido!",
|
||||
@@ -131,7 +131,7 @@
|
||||
"progress.certificateStatus.upgradeHeader": "Obtén un certificado",
|
||||
"progress.certificateStatus.upgradeBody": "Estás en la opción auditada y no calificas para un certificado. Para poder obtener un certificado, cambiate a la opción verificada del curso hoy mismo.",
|
||||
"progress.certificateStatus.upgradeButton": "Actualizar Ahora",
|
||||
"progress.certificateStatus.unverifiedHomeHeader": "Verifica tu identidad para obtener un certificado.",
|
||||
"progress.certificateStatus.unverifiedHomeHeader.v2": "Verify your identity to qualify for a certificate.",
|
||||
"progress.certificateStatus.unverifiedHomeButton": "Verificar mi identidad",
|
||||
"progress.certificateStatus.unverifiedHomeBody": "Para generar un certificado para este curso, debes completar el proceso de verificación de identidad.",
|
||||
"progress.completion.donut.label": "Completado",
|
||||
@@ -262,6 +262,7 @@
|
||||
"notes.button.hide": "Ocultar Notas",
|
||||
"courseExit.catalogSearchSuggestion": "¿Quieres saber más? {searchOurCatalogLink} para buscar más cursos y programas por explorar.",
|
||||
"courseCelebration.certificateBody.available": "\n Muestra tu logro en LinkedIn o en tu currículum hoy mismo.\n Puedes descargar tu certificado ahora y acceder a él en cualquier momento desde tu\n {dashboardLink} y {profileLink}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate.v2": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.unverified": "Para generar un certificado, debes completar la verificación de ID.\n {idVerificationSupportLink} ahora.",
|
||||
"courseCelebration.certificateBody.upgradable": "No es demasiado tarde para mejorar de categoría. Por {price}, obtendrás acceso a todas las asignaciones\n calificadas de este curso. Al terminar, recibirás un certificado verificado que es una\n valiosa credencial para mejorar tus perspectivas de trabajo y avanzar en tu carrera, o puedes usar dicho\n certificado para destacarlo en solicitudes universitarias.",
|
||||
"courseCelebration.upgradeDiscountCodePrompt": "Utiliza el código {code} en el momento de la compra para obtener un {percent} % de descuento.",
|
||||
@@ -274,7 +275,7 @@
|
||||
"courseCelebration.dashboardInfo": "Puedes acceder a este curso y a sus materiales en tu {dashboardLink}.",
|
||||
"courseExit.programs.applyForCredit": "Solicitar crédito",
|
||||
"courseCelebration.certificateHeader.downloadable": "¡Tu certificado está disponible!",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Tu calificación y tu certificado estarán listos en breve.",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Your grade and certificate status will be available soon.",
|
||||
"courseCelebration.certificateBody.notAvailable.accessCertificate": "Si has obtenido una calificación de aprobado, tu certificado se emitirá automáticamente.",
|
||||
"courseCelebration.certificateHeader.unverified": "Debes completar la verificación para recibir tu certificado.",
|
||||
"courseCelebration.certificateHeader.requestable": "¡Felicitaciones, usted califica para recibir un certificado!",
|
||||
@@ -396,7 +397,7 @@
|
||||
"learning.generic.upsell.fullAccessBullet.fullAccess": "Acceso completo",
|
||||
"learning.generic.upsell.fullAccessBullet": "{fullAccessInBoldText} al contenido y los materiales del curso, incluso después de que finalice el curso",
|
||||
"learning.generic.upsell.supportMissionBullet.mission": "misión",
|
||||
"learning.generic.upsell.supportMissionBullet": "Apoya nuestra {missionInBoldText} en edX",
|
||||
"learning.generic.upsell.supportMissionBullet": "Apoya nuestra {missionInBoldText} en {siteName}",
|
||||
"masquerade-widget.userName.error.generic": "Se ha producido un error. Inténtalo de nuevo.",
|
||||
"masquerade-widget.userName.input.placeholder": "Nombre de usuario o correo electrónico",
|
||||
"masquerade-widget.userName.input.label": "Hazte pasar por este usuario",
|
||||
@@ -408,7 +409,7 @@
|
||||
"tours.button.okay": "Okey",
|
||||
"tours.button.beginTour": "Comenzar recorrido",
|
||||
"tours.button.launchTour": "gira de lanzamiento",
|
||||
"tours.newUserModal.body": "Hagamos un recorrido rápido por edX para que pueda aprovechar al máximo su curso.",
|
||||
"tours.newUserModal.body": "Hagamos un recorrido rápido por {siteName} para que pueda aprovechar al máximo su curso.",
|
||||
"tours.newUserModal.title.welcome": "Bienvenido a tu",
|
||||
"tours.button.skipForNow": "Saltar por ahora ",
|
||||
"tours.datesCheckpoint.body": "Las fechas importantes pueden ayudarlo a mantenerse encaminado.",
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
"learning.goals.unsubscribe.header": "Vous vous êtes désabonné des rappels d'objectifs",
|
||||
"learning.goals.unsubscribe.loading": "Désinscription...",
|
||||
"learning.goals.unsubscribe.errorDescription": "Nous n'avons pas pu vous désinscrire des courriels de rappel d'objectif. Veuillez réessayer plus tard ou {contactSupport} pour obtenir de l'aide.",
|
||||
"learning.outline.alert.cert.when": "Ce cours se termine le {courseEndDateFormatted}. Les notes finales et les attestations\n devraient être disponibles après {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header": "Votre note et votre attestation seront bientôt prêtes !",
|
||||
"learning.outline.alert.cert.earnedNotAvailable": "This course ends on {courseEndDateFormatted}. Final grades and any earned certificates are\n scheduled to be available after {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header.v2": "Your grade and certificate status will be available soon.",
|
||||
"cert.alert.earned.ready.header": "Félicitations ! Votre attestation est prête.",
|
||||
"cert.alert.notPassing.header": "Vous n'êtes pas encore éligible pour une attestation",
|
||||
"cert.alert.notPassing.button": "Voir les notes",
|
||||
@@ -113,7 +113,7 @@
|
||||
"learning.outline.sequence-due": "{description} échéance {assignmentDue}",
|
||||
"progress.certificateStatus.unverifiedBody": "Afin de générer une attestation, vous devez effectuer une vérification d'identité. {idVerificationSupportLink}.",
|
||||
"progress.certificateStatus.downloadableBody": "Présentez vos réalisations sur LinkedIn ou votre curriculum vitae aujourd'hui. Vous pouvez télécharger votre certificat maintenant et y accéder à tout moment depuis votre tableau de bord et votre profil.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "Ce cours se termine le {endDate} et les notes finales et les attestations sont programmées pour être\n disponibles après le {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailabilityDate}.",
|
||||
"progress.certificateStatus.notPassingHeader": "État de l'attestation",
|
||||
"progress.certificateStatus.notPassingBody": "Pour être admissible à une attestation, vous devez avoir la note de passage.",
|
||||
"progress.certificateStatus.inProgressHeader": "Plus de contenu sera bientôt disponible!",
|
||||
@@ -131,7 +131,7 @@
|
||||
"progress.certificateStatus.upgradeHeader": "Obtenir un certificat",
|
||||
"progress.certificateStatus.upgradeBody": "Vous êtes dans une piste d'audit et n'êtes pas admissible à une attestation. Afin d'obtenir vers une attestation, mettez à niveau votre cours dès aujourd'hui.",
|
||||
"progress.certificateStatus.upgradeButton": "Mettre à jour dès maintenant",
|
||||
"progress.certificateStatus.unverifiedHomeHeader": "Vérifiez votre identité pour obtenir une attestation !",
|
||||
"progress.certificateStatus.unverifiedHomeHeader.v2": "Verify your identity to qualify for a certificate.",
|
||||
"progress.certificateStatus.unverifiedHomeButton": "Vérifiez mon identité",
|
||||
"progress.certificateStatus.unverifiedHomeBody": "Afin de générer une attestation pour ce cours, vous devez compléter le processus de vérification d'identité.",
|
||||
"progress.completion.donut.label": "achevée",
|
||||
@@ -262,6 +262,7 @@
|
||||
"notes.button.hide": "Masquer les notes",
|
||||
"courseExit.catalogSearchSuggestion": "Vous souhaitez en apprendre plus? {searchOurCatalogLink} pour trouver plus de cours et de programmes à explorer.",
|
||||
"courseCelebration.certificateBody.available": "\n Affichez vos accomplissements sur LinkedIn ou votre CV dès aujourd'hui.\n Vous pouvez télécharger votre attestation maintenant et y accéder à tout moment depuis vos\n {dashboardLink} et {profileLink}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate.v2": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.unverified": "Afin de générer une attestation, vous devez effectuer une vérification d'identité.\n {idVerificationSupportLink} maintenant.",
|
||||
"courseCelebration.certificateBody.upgradable": "Il n’est pas trop tard pour effectuer une mise à niveau. Pour {price}, vous débloquerez l'accès à tous les\n devoirs dans ce cours. À la fin, vous recevrez une attestation qui est une source\n d'informations précieuses pour améliorer vos perspectives d'emploi et faire progresser votre carrière, ou mettre en valeur votre\n attestation dans des demandes d'admission.",
|
||||
"courseCelebration.upgradeDiscountCodePrompt": "Utilisez le code {code} lors du paiement pour {percent}% de réduction!",
|
||||
@@ -274,7 +275,7 @@
|
||||
"courseCelebration.dashboardInfo": "Vous pouvez accéder à ce cours et à ses supports sur votre {dashboardLink}.",
|
||||
"courseExit.programs.applyForCredit": "Demander un crédit",
|
||||
"courseCelebration.certificateHeader.downloadable": "Votre attestation est disponible!",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Votre note et votre attestation seront bientôt prêtes !",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Your grade and certificate status will be available soon.",
|
||||
"courseCelebration.certificateBody.notAvailable.accessCertificate": "Si vous avez obtenu une note de passage, votre attestation sera automatiquement générée.",
|
||||
"courseCelebration.certificateHeader.unverified": "Vous devez avoir complété votre vérification pour recevoir votre attestation.",
|
||||
"courseCelebration.certificateHeader.requestable": "Félicitations, vous avez terminé le processus pour passer un certificat !",
|
||||
@@ -396,7 +397,7 @@
|
||||
"learning.generic.upsell.fullAccessBullet.fullAccess": "Accès complet",
|
||||
"learning.generic.upsell.fullAccessBullet": "{fullAccessInBoldText} au contenu et aux supports du cours, même après la fin du cours",
|
||||
"learning.generic.upsell.supportMissionBullet.mission": "mission",
|
||||
"learning.generic.upsell.supportMissionBullet": "Supportez notre {missionInBoldText} chez edX",
|
||||
"learning.generic.upsell.supportMissionBullet": "Supportez notre {missionInBoldText} chez {siteName}",
|
||||
"masquerade-widget.userName.error.generic": "Une erreur est survenue; veuillez réessayer.",
|
||||
"masquerade-widget.userName.input.placeholder": "Nom d'utilisateur ou courriel",
|
||||
"masquerade-widget.userName.input.label": "Se faire passer pour cet utilisateur",
|
||||
@@ -408,7 +409,7 @@
|
||||
"tours.button.okay": "Okay",
|
||||
"tours.button.beginTour": "Commencer la visite guidée",
|
||||
"tours.button.launchTour": "Lancer la visite guidée",
|
||||
"tours.newUserModal.body": "Faisons une visite guidée de edX afin de profiter au maximum de votre cours.",
|
||||
"tours.newUserModal.body": "Faisons une visite guidée de {siteName} afin de profiter au maximum de votre cours.",
|
||||
"tours.newUserModal.title.welcome": "Bienvenue à votre",
|
||||
"tours.button.skipForNow": "Ignorer pour l'instant",
|
||||
"tours.datesCheckpoint.body": "Dates importantes afin de vous maintenir sur la bonne voie.",
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
"learning.goals.unsubscribe.header": "You’ve unsubscribed from goal reminders",
|
||||
"learning.goals.unsubscribe.loading": "Unsubscribing…",
|
||||
"learning.goals.unsubscribe.errorDescription": "We were unable to unsubscribe you from goal reminder emails. Please try again later or {contactSupport} for help.",
|
||||
"learning.outline.alert.cert.when": "This course ends on {courseEndDateFormatted}. Final grades and certificates are\n scheduled to be available after {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header": "Your grade and certificate will be ready soon!",
|
||||
"learning.outline.alert.cert.earnedNotAvailable": "This course ends on {courseEndDateFormatted}. Final grades and any earned certificates are\n scheduled to be available after {certificateAvailableDate}.",
|
||||
"cert.alert.earned.unavailable.header.v2": "Your grade and certificate status will be available soon.",
|
||||
"cert.alert.earned.ready.header": "Congratulations! Your certificate is ready.",
|
||||
"cert.alert.notPassing.header": "You are not yet eligible for a certificate",
|
||||
"cert.alert.notPassing.button": "View grades",
|
||||
@@ -113,7 +113,7 @@
|
||||
"learning.outline.sequence-due": "{description} due {assignmentDue}",
|
||||
"progress.certificateStatus.unverifiedBody": "In order to generate a certificate, you must complete ID verification. {idVerificationSupportLink}.",
|
||||
"progress.certificateStatus.downloadableBody": "Showcase your accomplishment on LinkedIn or your resumé today. You can download your certificate now and access it any time from your Dashboard and Profile.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "This course ended on {endDate} and final grades and certificates are scheduled to be\n available after {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailabilityDate}.",
|
||||
"progress.certificateStatus.notPassingHeader": "Certificate status",
|
||||
"progress.certificateStatus.notPassingBody": "In order to qualify for a certificate, you must have a passing grade.",
|
||||
"progress.certificateStatus.inProgressHeader": "More content is coming soon!",
|
||||
@@ -131,7 +131,7 @@
|
||||
"progress.certificateStatus.upgradeHeader": "Earn a certificate",
|
||||
"progress.certificateStatus.upgradeBody": "You are in an audit track and do not qualify for a certificate. In order to work towards a certificate, upgrade your course today.",
|
||||
"progress.certificateStatus.upgradeButton": "Upgrade now",
|
||||
"progress.certificateStatus.unverifiedHomeHeader": "Verify your identity to earn a certificate!",
|
||||
"progress.certificateStatus.unverifiedHomeHeader.v2": "Verify your identity to qualify for a certificate.",
|
||||
"progress.certificateStatus.unverifiedHomeButton": "Verify my ID",
|
||||
"progress.certificateStatus.unverifiedHomeBody": "In order to generate a certificate for this course, you must complete the ID verification process.",
|
||||
"progress.completion.donut.label": "completed",
|
||||
@@ -262,6 +262,7 @@
|
||||
"notes.button.hide": "Hide Notes",
|
||||
"courseExit.catalogSearchSuggestion": "Looking to learn more? {searchOurCatalogLink} to find more courses and programs to explore.",
|
||||
"courseCelebration.certificateBody.available": "\n Showcase your accomplishment on LinkedIn or your resumé today.\n You can download your certificate now and access it any time from your\n {dashboardLink} and {profileLink}.",
|
||||
"courseCelebration.certificateBody.notAvailable.endDate.v2": "This course ends on {endDate}. Final grades and any earned certificates are\n scheduled to be available after {certAvailableDate}.",
|
||||
"courseCelebration.certificateBody.unverified": "In order to generate a certificate, you must complete ID verification.\n {idVerificationSupportLink} now.",
|
||||
"courseCelebration.certificateBody.upgradable": "It’s not too late to upgrade. For {price} you will unlock access to all graded\n assignments in this course. Upon completion, you will receive a verified certificate which is a\n valuable credential to improve your job prospects and advance your career, or highlight your\n certificate in school applications.",
|
||||
"courseCelebration.upgradeDiscountCodePrompt": "Use code {code} at checkout for {percent}% off!",
|
||||
@@ -274,7 +275,7 @@
|
||||
"courseCelebration.dashboardInfo": "You can access this course and its materials on your {dashboardLink}.",
|
||||
"courseExit.programs.applyForCredit": "Apply for credit",
|
||||
"courseCelebration.certificateHeader.downloadable": "Your certificate is available!",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Your grade and certificate will be ready soon!",
|
||||
"courseCelebration.certificateHeader.notAvailable": "Your grade and certificate status will be available soon.",
|
||||
"courseCelebration.certificateBody.notAvailable.accessCertificate": "If you have earned a passing grade, your certificate will be automatically issued.",
|
||||
"courseCelebration.certificateHeader.unverified": "You must complete verification to receive your certificate.",
|
||||
"courseCelebration.certificateHeader.requestable": "Congratulations, you qualified for a certificate!",
|
||||
@@ -396,7 +397,7 @@
|
||||
"learning.generic.upsell.fullAccessBullet.fullAccess": "Full access",
|
||||
"learning.generic.upsell.fullAccessBullet": "{fullAccessInBoldText} to course content and materials, even after the course ends",
|
||||
"learning.generic.upsell.supportMissionBullet.mission": "mission",
|
||||
"learning.generic.upsell.supportMissionBullet": "Support our {missionInBoldText} at edX",
|
||||
"learning.generic.upsell.supportMissionBullet": "Support our {missionInBoldText} at {siteName}",
|
||||
"masquerade-widget.userName.error.generic": "An error has occurred; please try again.",
|
||||
"masquerade-widget.userName.input.placeholder": "Username or email",
|
||||
"masquerade-widget.userName.input.label": "Masquerade as this user",
|
||||
@@ -408,7 +409,7 @@
|
||||
"tours.button.okay": "Okay",
|
||||
"tours.button.beginTour": "Begin tour",
|
||||
"tours.button.launchTour": "Launch tour",
|
||||
"tours.newUserModal.body": "Let’s take a quick tour of edX so you can get the most out of your course.",
|
||||
"tours.newUserModal.body": "Let’s take a quick tour of {siteName} so you can get the most out of your course.",
|
||||
"tours.newUserModal.title.welcome": "Welcome to your",
|
||||
"tours.button.skipForNow": "Skip for now",
|
||||
"tours.datesCheckpoint.body": "Important dates can help you stay on track.",
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = defineMessages({
|
||||
},
|
||||
newUserModalBody: {
|
||||
id: 'tours.newUserModal.body',
|
||||
defaultMessage: 'Let’s take a quick tour of edX so you can get the most out of your course.',
|
||||
defaultMessage: 'Let’s take a quick tour of {siteName} so you can get the most out of your course.',
|
||||
},
|
||||
newUserModalTitleWelcome: {
|
||||
id: 'tours.newUserModal.title.welcome',
|
||||
|
||||
@@ -58,7 +58,7 @@ function NewUserCourseHomeTourModal({
|
||||
)}
|
||||
onClose={onDismiss}
|
||||
>
|
||||
<p className="text-dark-900">{intl.formatMessage(messages.newUserModalBody)}</p>
|
||||
<p className="text-dark-900">{intl.formatMessage(messages.newUserModalBody, { siteName: getConfig().SITE_NAME })}</p>
|
||||
</MarketingModal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,14 +5,13 @@ import '@testing-library/jest-dom/extend-expect';
|
||||
import './courseware/data/__factories__';
|
||||
import './course-home/data/__factories__';
|
||||
import { getConfig, mergeConfig } from '@edx/frontend-platform';
|
||||
import { configure as configureI18n } from '@edx/frontend-platform/i18n';
|
||||
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { configure as configureLogging } from '@edx/frontend-platform/logging';
|
||||
import { configure as configureAuth, getAuthenticatedHttpClient, MockAuthService } from '@edx/frontend-platform/auth';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { render as rtlRender } from '@testing-library/react';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import AppProvider from '@edx/frontend-platform/react/AppProvider';
|
||||
import { reducer as courseHomeReducer } from './course-home/data';
|
||||
|
||||
@@ -160,8 +160,8 @@ function StreakModal({
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body className="modal-body">
|
||||
<p>{intl.formatMessage(messages.streakBody)}</p>
|
||||
<p className="modal-image">
|
||||
<p className="text-center">{intl.formatMessage(messages.streakBody)}</p>
|
||||
<p className="modal-image text-center">
|
||||
{!wideScreen && <img src={StreakMobileImage} alt="" className="img-fluid" />}
|
||||
{wideScreen && <img src={StreakDesktopImage} alt="" className="img-fluid" />}
|
||||
</p>
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
.modal-body {
|
||||
padding-top: .5rem;
|
||||
font-size: 1.2rem;
|
||||
|
||||
.pgn__modal-body-content {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
|
||||
Reference in New Issue
Block a user