diff --git a/src/course-home/data/__snapshots__/redux.test.js.snap b/src/course-home/data/__snapshots__/redux.test.js.snap
index e1ff2738..504c866e 100644
--- a/src/course-home/data/__snapshots__/redux.test.js.snap
+++ b/src/course-home/data/__snapshots__/redux.test.js.snap
@@ -396,8 +396,6 @@ Object {
"courseBlocks": Object {
"courses": Object {
"block-v1:edX+DemoX+Demo_Course+type@course+block@bcdabcdabcdabcdabcdabcdabcdabcd3": Object {
- "effortActivities": undefined,
- "effortTime": undefined,
"hasScheduledContent": false,
"id": "course-v1:edX+DemoX+Demo_Course_1",
"sectionIds": Array [
@@ -410,8 +408,6 @@ Object {
"block-v1:edX+DemoX+Demo_Course+type@chapter+block@bcdabcdabcdabcdabcdabcdabcdabcd2": Object {
"complete": false,
"courseId": "course-v1:edX+DemoX+Demo_Course_1",
- "effortActivities": 2,
- "effortTime": 15,
"id": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@bcdabcdabcdabcdabcdabcdabcdabcd2",
"resumeBlock": false,
"sequenceIds": Array [
@@ -425,8 +421,8 @@ Object {
"complete": false,
"description": null,
"due": null,
- "effortActivities": undefined,
- "effortTime": undefined,
+ "effortActivities": 2,
+ "effortTime": 15,
"icon": null,
"id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bcdabcdabcdabcdabcdabcdabcdabcd1",
"legacyWebUrl": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course/jump_to/block-v1:edX+DemoX+Demo_Course+type@sequential+block@bcdabcdabcdabcdabcdabcdabcdabcd1?experience=legacy",
diff --git a/src/course-home/data/api.js b/src/course-home/data/api.js
index ae0858d4..e6fd22d5 100644
--- a/src/course-home/data/api.js
+++ b/src/course-home/data/api.js
@@ -111,8 +111,6 @@ export function normalizeOutlineBlocks(courseId, blocks) {
switch (block.type) {
case 'course':
models.courses[block.id] = {
- effortActivities: block.effort_activities,
- effortTime: block.effort_time,
id: courseId,
title: block.display_name,
sectionIds: block.children || [],
@@ -123,8 +121,6 @@ export function normalizeOutlineBlocks(courseId, blocks) {
case 'chapter':
models.sections[block.id] = {
complete: block.complete,
- effortActivities: block.effort_activities,
- effortTime: block.effort_time,
id: block.id,
title: block.display_name,
resumeBlock: block.resume_block,
diff --git a/src/course-home/outline-tab/Section.jsx b/src/course-home/outline-tab/Section.jsx
index ce69f760..b5078126 100644
--- a/src/course-home/outline-tab/Section.jsx
+++ b/src/course-home/outline-tab/Section.jsx
@@ -6,7 +6,6 @@ import { faCheckCircle as fasCheckCircle, faMinus, faPlus } from '@fortawesome/f
import { faCheckCircle as farCheckCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import EffortEstimate from '../../shared/effort-estimate';
import SequenceLink from './SequenceLink';
import { useModel } from '../../generic/model-store';
@@ -67,7 +66,6 @@ function Section({
, {intl.formatMessage(complete ? messages.completedSection : messages.incompleteSection)}
-
);
diff --git a/src/courseware/course/sequence/sequence-navigation/UnitNavigationEffortEstimate.jsx b/src/courseware/course/sequence/sequence-navigation/UnitNavigationEffortEstimate.jsx
index fda53302..7f797226 100644
--- a/src/courseware/course/sequence/sequence-navigation/UnitNavigationEffortEstimate.jsx
+++ b/src/courseware/course/sequence/sequence-navigation/UnitNavigationEffortEstimate.jsx
@@ -1,23 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
+import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import EffortEstimate from '../../../../shared/effort-estimate';
import { sequenceIdsSelector } from '../../../data';
import { useModel } from '../../../../generic/model-store';
-// This component exists to peek ahead at the next subsection or section and grab its estimated effort.
-// If we should be showing the next block's effort, we display the title and effort instead of "Next".
-// This code currently tries to handle both section and subsection estimates. But once AA-659 happens, it can be
-// simplified to one or the other code path.
+import messages from './messages';
-function UnitNavigationEffortEstimate({ children, sequenceId, unitId }) {
+// This component exists to peek ahead at the next sequence and grab its estimated effort.
+// If we should be showing the next sequence's effort, we display the title and effort instead of "Next".
+
+function UnitNavigationEffortEstimate({
+ children,
+ intl,
+ sequenceId,
+ unitId,
+}) {
const sequenceIds = useSelector(sequenceIdsSelector);
const sequenceIndex = sequenceIds.indexOf(sequenceId);
const nextSequenceId = sequenceIndex < sequenceIds.length - 1 ? sequenceIds[sequenceIndex + 1] : null;
const sequence = useModel('sequences', sequenceId);
const nextSequence = useModel('sequences', nextSequenceId);
- const nextSection = useModel('sections', nextSequence ? nextSequence.sectionId : null);
if (!sequence || !nextSequence) {
return children;
@@ -28,32 +33,23 @@ function UnitNavigationEffortEstimate({ children, sequenceId, unitId }) {
return children;
}
- let blockToShow = nextSequence;
- // The experimentation code currently only sets effort on either sequences, sections, or nothing. If we don't have
- // sequence info, we are either doing sections or nothing. Let's look into it.
+ // If we don't have info to show for the next sequence, just bail
if (!nextSequence.effortActivities && !nextSequence.effortTime) {
- if (!nextSection.effortActivities && !nextSection.effortTime) {
- return children; // control group - no effort estimates at all
- }
-
- // Are we at a section border? If so, let's show the next section's effort estimates
- if (sequence.sectionId !== nextSequence.sectionId) {
- blockToShow = nextSection;
- }
+ return children;
}
- // Note: we don't use `children` here - we replace it with the next section name.
- // AA-659: remember to add a translation for Next Up
+ // Note: we don't use `children` here - we replace it with the next sequence's title.
return (
- Next Up: {blockToShow.title}
-
+ {intl.formatMessage(messages.nextUpButton, { title: nextSequence.title })}
+
);
}
UnitNavigationEffortEstimate.propTypes = {
children: PropTypes.node,
+ intl: intlShape.isRequired,
sequenceId: PropTypes.string.isRequired,
unitId: PropTypes.string,
};
@@ -63,4 +59,4 @@ UnitNavigationEffortEstimate.defaultProps = {
unitId: null,
};
-export default UnitNavigationEffortEstimate;
+export default injectIntl(UnitNavigationEffortEstimate);
diff --git a/src/courseware/course/sequence/sequence-navigation/messages.js b/src/courseware/course/sequence/sequence-navigation/messages.js
index fc947f96..9d99fdd4 100644
--- a/src/courseware/course/sequence/sequence-navigation/messages.js
+++ b/src/courseware/course/sequence/sequence-navigation/messages.js
@@ -6,6 +6,11 @@ const messages = defineMessages({
defaultMessage: 'Next',
description: 'Button to advance to the next section',
},
+ nextUpButton: {
+ id: 'learn.sequence.navigation.next.up.button',
+ defaultMessage: 'Next Up: {title}',
+ description: 'Button to advance to the next section, with title',
+ },
previousButton: {
id: 'learn.sequence.navigation.previous.button',
defaultMessage: 'Previous',
diff --git a/src/courseware/data/__factories__/courseMetadata.factory.js b/src/courseware/data/__factories__/courseMetadata.factory.js
index 7b84c58a..49a06223 100644
--- a/src/courseware/data/__factories__/courseMetadata.factory.js
+++ b/src/courseware/data/__factories__/courseMetadata.factory.js
@@ -9,7 +9,6 @@ Factory.define('courseMetadata')
can_show_upgrade_sock: false,
content_type_gating_enabled: false,
course_expired_message: null,
- effort: null,
end: null,
enrollment_start: null,
enrollment_end: null,
diff --git a/src/courseware/data/api.js b/src/courseware/data/api.js
index 792fb401..69998c29 100644
--- a/src/courseware/data/api.js
+++ b/src/courseware/data/api.js
@@ -16,8 +16,6 @@ export function normalizeBlocks(courseId, blocks) {
switch (block.type) {
case 'course':
models.courses[block.id] = {
- effortActivities: block.effort_activities,
- effortTime: block.effort_time,
id: courseId,
title: block.display_name,
sectionIds: block.children || [],
@@ -26,8 +24,6 @@ export function normalizeBlocks(courseId, blocks) {
break;
case 'chapter':
models.sections[block.id] = {
- effortActivities: block.effort_activities,
- effortTime: block.effort_time,
id: block.id,
title: block.display_name,
sequenceIds: block.children || [],
diff --git a/src/shared/data/__factories__/courseBlocks.factory.js b/src/shared/data/__factories__/courseBlocks.factory.js
index 24d25a3e..86eefd81 100644
--- a/src/shared/data/__factories__/courseBlocks.factory.js
+++ b/src/shared/data/__factories__/courseBlocks.factory.js
@@ -106,7 +106,12 @@ export function buildSimpleCourseBlocks(courseId, title, options = {}) {
export function buildMinimalCourseBlocks(courseId, title, options = {}) {
const sequenceBlocks = options.sequenceBlocks || [Factory.build(
'block',
- { display_name: 'Title of Sequence', type: 'sequential' },
+ {
+ display_name: 'Title of Sequence',
+ effort_activities: 2,
+ effort_time: 15,
+ type: 'sequential',
+ },
{ courseId },
)];
const sectionBlocks = options.sectionBlocks || [Factory.build(
@@ -115,8 +120,6 @@ export function buildMinimalCourseBlocks(courseId, title, options = {}) {
type: 'chapter',
display_name: 'Title of Section',
complete: options.complete || false,
- effort_time: 15,
- effort_activities: 2,
resume_block: options.resumeBlock || false,
children: sequenceBlocks.map(block => block.id),
},
diff --git a/src/shared/effort-estimate/EffortEstimate.jsx b/src/shared/effort-estimate/EffortEstimate.jsx
index 55685006..3a0fae47 100644
--- a/src/shared/effort-estimate/EffortEstimate.jsx
+++ b/src/shared/effort-estimate/EffortEstimate.jsx
@@ -1,9 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
+
+import messages from './messages';
// This component shows an effort estimate provided by the backend block data. Either time, activities, or both.
-// Right now it is an experiment, and AA-659 is its cleanup ticket.
function EffortEstimate(props) {
const {
@@ -12,27 +14,34 @@ function EffortEstimate(props) {
effortTime,
},
className,
+ intl,
} = props;
- // FIXME: This is not properly internationalized. This is just an experiment right now, so I chose to not mark
- // FIXME: the strings for translation. That should be fixed if/when this is made real code. AA-659
const minuteCount = Math.ceil(effortTime / 60); // effortTime is in seconds
+ const minutesAbbreviated = intl.formatMessage(messages.minutesAbbreviated, { minuteCount });
+ const minutesFull = intl.formatMessage(messages.minutesFull, { minuteCount });
const minutes = (
<>
- {minuteCount}
- min
- {minuteCount === 1 ? 'minute' : 'minutes'}
+ {minutesAbbreviated}
+ {minutesFull}
>
);
- const activities = <>{effortActivities} {effortActivities === 1 ? 'activity' : 'activities'}>;
+ const activities = intl.formatMessage(messages.activities, { activityCount: effortActivities });
let content = null;
if (effortTime && effortActivities) {
- content = <>{minutes} + {activities}>;
+ content = (
+
+ );
} else if (effortTime) {
- content = <>{minutes}>;
+ content = minutes;
} else if (effortActivities) {
- content = <>{activities}>;
+ content = activities;
} else {
return null;
}
@@ -57,6 +66,7 @@ EffortEstimate.propTypes = {
effortTime: PropTypes.number,
}).isRequired,
className: PropTypes.string,
+ intl: intlShape.isRequired,
};
-export default EffortEstimate;
+export default injectIntl(EffortEstimate);
diff --git a/src/shared/effort-estimate/messages.js b/src/shared/effort-estimate/messages.js
new file mode 100644
index 00000000..75b544d8
--- /dev/null
+++ b/src/shared/effort-estimate/messages.js
@@ -0,0 +1,20 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ activities: {
+ id: 'learning.effortEstimation.activities',
+ defaultMessage: '{activityCount, plural, one {# activity} other {# activities}}',
+ },
+ minutesAbbreviated: {
+ id: 'learning.effortEstimation.minutesAbbreviated',
+ defaultMessage: '{minuteCount, plural, other {# min}}',
+ description: 'Number of minutes in a casual, shorthand manner: 5 min',
+ },
+ minutesFull: {
+ id: 'learning.effortEstimation.minutesFull',
+ defaultMessage: '{minuteCount, plural, one {# minute} other {# minutes}}',
+ description: 'Number of minutes spelled out: 5 minutes',
+ },
+});
+
+export default messages;