From 65e38d28fe31fbe1f752d5708209317baf46f326 Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Mon, 6 May 2024 15:13:33 +0530 Subject: [PATCH 1/2] feat: option to show/hide ungraded assignment in progress page test: add tests for show ungraded toggle feat: update score label in progress page based on grading refactor: update score label text and add tooltip refactor: move label tooltip near header as normal text refactor: update problem score label Graded scores --- .env | 1 + .env.development | 1 + .env.test | 1 + .../progress-tab/ProgressTab.test.jsx | 117 +++++++++++++++++- .../grades/detailed-grades/DetailedGrades.jsx | 30 ++++- .../detailed-grades/DetailedGradesTable.jsx | 12 +- .../detailed-grades/ProblemScoreDrawer.jsx | 10 +- .../progress-tab/grades/messages.ts | 16 ++- src/index.jsx | 1 + 9 files changed, 176 insertions(+), 13 deletions(-) diff --git a/.env b/.env index 697910497c..917bdea634 100644 --- a/.env +++ b/.env @@ -48,3 +48,4 @@ TWITTER_HASHTAG='' TWITTER_URL='' USER_INFO_COOKIE_NAME='' OPTIMIZELY_FULL_STACK_SDK_KEY='' +SHOW_UNGRADED_ASSIGNMENT_PROGRESS='' diff --git a/.env.development b/.env.development index a4bceccdbf..56541aa1d4 100644 --- a/.env.development +++ b/.env.development @@ -50,3 +50,4 @@ SESSION_COOKIE_DOMAIN='localhost' CHAT_RESPONSE_URL='http://localhost:18000/api/learning_assistant/v1/course_id' PRIVACY_POLICY_URL='http://localhost:18000/privacy' OPTIMIZELY_FULL_STACK_SDK_KEY='' +SHOW_UNGRADED_ASSIGNMENT_PROGRESS='' diff --git a/.env.test b/.env.test index cb4d0e0fa2..faa4b4ffbe 100644 --- a/.env.test +++ b/.env.test @@ -47,3 +47,4 @@ TWITTER_HASHTAG='myedxjourney' TWITTER_URL='https://twitter.com/edXOnline' USER_INFO_COOKIE_NAME='edx-user-info' PRIVACY_POLICY_URL='http://localhost:18000/privacy' +SHOW_UNGRADED_ASSIGNMENT_PROGRESS='' diff --git a/src/course-home/progress-tab/ProgressTab.test.jsx b/src/course-home/progress-tab/ProgressTab.test.jsx index a69426c3bd..1a9b15408d 100644 --- a/src/course-home/progress-tab/ProgressTab.test.jsx +++ b/src/course-home/progress-tab/ProgressTab.test.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Factory } from 'rosie'; -import { getConfig } from '@edx/frontend-platform'; +import { getConfig, setConfig } from '@edx/frontend-platform'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { breakpoints } from '@openedx/paragon'; @@ -545,6 +545,111 @@ describe('Progress Tab', () => { await fetchAndRender(); expect(screen.getByText('Grades & Credit')).toBeInTheDocument(); }); + + it('does not render ungraded subsections when SHOW_UNGRADED_ASSIGNMENT_PROGRESS is false', async () => { + // The second assignment has has_graded_assignment set to false, so it should not be shown. + setTabData({ + section_scores: [ + { + display_name: 'First section', + subsections: [ + { + assignment_type: 'Homework', + block_key: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@12345', + display_name: 'First subsection', + learner_has_access: true, + has_graded_assignment: true, + num_points_earned: 1, + num_points_possible: 2, + percent_graded: 1.0, + show_correctness: 'always', + show_grades: true, + url: 'http://learning.edx.org/course/course-v1:edX+Test+run/first_subsection', + }, + ], + }, + { + display_name: 'Second section', + subsections: [ + { + assignment_type: 'Homework', + display_name: 'Second subsection', + learner_has_access: true, + has_graded_assignment: false, + num_points_earned: 1, + num_points_possible: 1, + percent_graded: 1.0, + show_correctness: 'always', + show_grades: true, + url: 'http://learning.edx.org/course/course-v1:edX+Test+run/second_subsection', + }, + ], + }, + ], + }); + + await fetchAndRender(); + expect(screen.getByText('First subsection')).toBeInTheDocument(); + expect(screen.queryByText('Second subsection')).not.toBeInTheDocument(); + }); + + it('renders both graded and ungraded subsections when SHOW_UNGRADED_ASSIGNMENT_PROGRESS is true', async () => { + // The second assignment has has_graded_assignment set to false. + setConfig({ + ...getConfig(), + SHOW_UNGRADED_ASSIGNMENT_PROGRESS: true, + }); + + setTabData({ + section_scores: [ + { + display_name: 'First section', + subsections: [ + { + assignment_type: 'Homework', + block_key: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@12345', + display_name: 'First subsection', + learner_has_access: true, + has_graded_assignment: true, + num_points_earned: 1, + num_points_possible: 2, + percent_graded: 1.0, + show_correctness: 'always', + show_grades: true, + url: 'http://learning.edx.org/course/course-v1:edX+Test+run/first_subsection', + }, + ], + }, + { + display_name: 'Second section', + subsections: [ + { + assignment_type: 'Homework', + display_name: 'Second subsection', + learner_has_access: true, + has_graded_assignment: false, + num_points_earned: 1, + num_points_possible: 1, + percent_graded: 1.0, + show_correctness: 'always', + show_grades: true, + url: 'http://learning.edx.org/course/course-v1:edX+Test+run/second_subsection', + }, + ], + }, + ], + }); + + await fetchAndRender(); + expect(screen.getByText('First subsection')).toBeInTheDocument(); + expect(screen.getByText('Second subsection')).toBeInTheDocument(); + + // reset config for other tests + setConfig({ + ...getConfig(), + SHOW_UNGRADED_ASSIGNMENT_PROGRESS: false, + }); + }); }); describe('Grade Summary', () => { @@ -809,7 +914,7 @@ describe('Progress Tab', () => { // Open the problem score drawer fireEvent.click(problemScoreDrawerToggle); - expect(screen.getByText('Problem Scores:')).toBeInTheDocument(); + expect(screen.getAllByText('Graded Scores:').length).toBeGreaterThan(1); expect(screen.getAllByText('0/1')).toHaveLength(3); }); @@ -821,6 +926,14 @@ describe('Progress Tab', () => { expect(screen.getByText('Detailed grades')).toBeInTheDocument(); expect(screen.getByText('You currently have no graded problem scores.')).toBeInTheDocument(); }); + + it('renders Detailed Grades table when section scores are populated', async () => { + await fetchAndRender(); + expect(screen.getByText('Detailed grades')).toBeInTheDocument(); + + expect(screen.getByText('First subsection')); + expect(screen.getByText('Second subsection')); + }); }); describe('Certificate Status', () => { diff --git a/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx b/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx index 40fb569c51..bb379b9164 100644 --- a/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx +++ b/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { getConfig } from '@edx/frontend-platform'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; @@ -28,6 +29,11 @@ const DetailedGrades = ({ intl }) => { } = useModel('progress', courseId); const hasSectionScores = sectionScores.length > 0; + const showUngradedAssignments = ( + getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === 'true' + || getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === true + ); + const emptyTableMsg = showUngradedAssignments ? messages.detailedGradesEmpty : messages.detailedGradesEmptyOnlyGraded; const logOutlineLinkClick = () => { sendTrackEvent('edx.ui.lms.course_progress.detailed_grades.course_outline_link.clicked', { @@ -54,7 +60,25 @@ const DetailedGrades = ({ intl }) => { return (
-

{intl.formatMessage(messages.detailedGrades)}

+

{intl.formatMessage(messages.detailedGrades)}

+ {gradesFeatureIsPartiallyLocked && (
@@ -65,9 +89,9 @@ const DetailedGrades = ({ intl }) => { )} {!hasSectionScores && ( -

{intl.formatMessage(messages.detailedGradesEmpty)}

+

{intl.formatMessage(emptyTableMsg)}

)} - {overviewTabUrl && ( + {overviewTabUrl && !showUngradedAssignments && (

{ } = useModel('progress', courseId); const isLocaleRtl = isRtl(getLocale()); + const showUngradedAssignments = ( + getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === 'true' + || getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === true + ); return ( sectionScores.map((chapter) => { const subsectionScores = chapter.subsections.filter( (subsection) => !!( - subsection.hasGradedAssignment - && subsection.showGrades - && (subsection.numPointsPossible > 0 || subsection.numPointsEarned > 0)), + (showUngradedAssignments || subsection.hasGradedAssignment) + && subsection.showGrades + && (subsection.numPointsPossible > 0 || subsection.numPointsEarned > 0) + ), ); if (subsectionScores.length === 0) { diff --git a/src/course-home/progress-tab/grades/detailed-grades/ProblemScoreDrawer.jsx b/src/course-home/progress-tab/grades/detailed-grades/ProblemScoreDrawer.jsx index 7970821aaf..9088c39f03 100644 --- a/src/course-home/progress-tab/grades/detailed-grades/ProblemScoreDrawer.jsx +++ b/src/course-home/progress-tab/grades/detailed-grades/ProblemScoreDrawer.jsx @@ -10,9 +10,12 @@ import messages from '../messages'; const ProblemScoreDrawer = ({ intl, problemScores, subsection }) => { const isLocaleRtl = isRtl(getLocale()); + + const scoreLabel = subsection.hasGradedAssignment ? messages.gradedScoreLabel : messages.practiceScoreLabel; + return ( - {intl.formatMessage(messages.problemScoreLabel)} + {intl.formatMessage(scoreLabel)}

    {problemScores.map((problemScore, i) => ( @@ -31,7 +34,10 @@ ProblemScoreDrawer.propTypes = { earned: PropTypes.number.isRequired, possible: PropTypes.number.isRequired, })).isRequired, - subsection: PropTypes.shape({ learnerHasAccess: PropTypes.bool }).isRequired, + subsection: PropTypes.shape({ + learnerHasAccess: PropTypes.bool, + hasGradedAssignment: PropTypes.bool, + }).isRequired, }; export default injectIntl(ProblemScoreDrawer); diff --git a/src/course-home/progress-tab/grades/messages.ts b/src/course-home/progress-tab/grades/messages.ts index 72a6624b2b..fb57809852 100644 --- a/src/course-home/progress-tab/grades/messages.ts +++ b/src/course-home/progress-tab/grades/messages.ts @@ -91,9 +91,14 @@ const messages = defineMessages({ defaultMessage: 'Detailed grades', description: 'Headline for the (detailed grade) section in the progress tab', }, - detailedGradesEmpty: { + detailedGradesEmptyOnlyGraded: { id: 'progress.detailedGrades.emptyTable', defaultMessage: 'You currently have no graded problem scores.', + description: 'It indicate that there are no graded problem or assignments to be scored', + }, + detailedGradesEmpty: { + id: 'progress.detailedGrades.including-ungraded.emptyTable', + defaultMessage: 'You currently have no graded or ungraded problem scores.', description: 'It indicate that there are no problem or assignments to be scored', }, footnotesTitle: { @@ -158,11 +163,16 @@ const messages = defineMessages({ defaultMessage: 'Passing grade', description: 'Label for mark on the (grade bar) chart which indicate the poisition of passing grade on the bar', }, - problemScoreLabel: { + gradedScoreLabel: { id: 'progress.detailedGrades.problemScore.label', - defaultMessage: 'Problem Scores:', + defaultMessage: 'Graded Scores:', description: 'Label text which precedes detailed view of all scores per assignment', }, + practiceScoreLabel: { + id: 'progress.detailedGrades.practice.problemScore.label', + defaultMessage: 'Practice Scores:', + description: 'Label text which precedes detailed view of all ungraded problem scores per assignment', + }, problemScoreToggleAltText: { id: 'progress.detailedGrades.problemScore.toggleButton', defaultMessage: 'Toggle individual problem scores for {subsectionTitle}', diff --git a/src/index.jsx b/src/index.jsx index 9680c134d4..ec4f4dc284 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -173,6 +173,7 @@ initialize({ PROCTORED_EXAM_RULES_URL: process.env.PROCTORED_EXAM_RULES_URL || null, CHAT_RESPONSE_URL: process.env.CHAT_RESPONSE_URL || null, PRIVACY_POLICY_URL: process.env.PRIVACY_POLICY_URL || null, + SHOW_UNGRADED_ASSIGNMENT_PROGRESS: process.env.SHOW_UNGRADED_ASSIGNMENT_PROGRESS || false, }, 'LearnerAppConfig'); }, }, From 1686988a2fd7155a66e45f2b5ea8d196d1528330 Mon Sep 17 00:00:00 2001 From: Navin Karkera Date: Thu, 17 Oct 2024 10:45:20 +0530 Subject: [PATCH 2/2] refactor: move config check to utils --- .../grades/detailed-grades/DetailedGrades.jsx | 11 ++++------- .../grades/detailed-grades/DetailedGradesTable.jsx | 8 ++------ src/course-home/progress-tab/utils.ts | 7 +++++++ 3 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 src/course-home/progress-tab/utils.ts diff --git a/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx b/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx index bb379b9164..529859c52b 100644 --- a/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx +++ b/src/course-home/progress-tab/grades/detailed-grades/DetailedGrades.jsx @@ -1,13 +1,13 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import { getConfig } from '@edx/frontend-platform'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Blocked } from '@openedx/paragon/icons'; import { Icon, Hyperlink } from '@openedx/paragon'; import { useModel } from '../../../../generic/model-store'; +import { showUngradedAssignments } from '../../utils'; import DetailedGradesTable from './DetailedGradesTable'; @@ -29,11 +29,8 @@ const DetailedGrades = ({ intl }) => { } = useModel('progress', courseId); const hasSectionScores = sectionScores.length > 0; - const showUngradedAssignments = ( - getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === 'true' - || getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === true - ); - const emptyTableMsg = showUngradedAssignments ? messages.detailedGradesEmpty : messages.detailedGradesEmptyOnlyGraded; + const emptyTableMsg = showUngradedAssignments() + ? messages.detailedGradesEmpty : messages.detailedGradesEmptyOnlyGraded; const logOutlineLinkClick = () => { sendTrackEvent('edx.ui.lms.course_progress.detailed_grades.course_outline_link.clicked', { @@ -91,7 +88,7 @@ const DetailedGrades = ({ intl }) => { {!hasSectionScores && (

    {intl.formatMessage(emptyTableMsg)}

    )} - {overviewTabUrl && !showUngradedAssignments && ( + {overviewTabUrl && !showUngradedAssignments() && (

    { const { @@ -21,15 +21,11 @@ const DetailedGradesTable = ({ intl }) => { } = useModel('progress', courseId); const isLocaleRtl = isRtl(getLocale()); - const showUngradedAssignments = ( - getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === 'true' - || getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === true - ); return ( sectionScores.map((chapter) => { const subsectionScores = chapter.subsections.filter( (subsection) => !!( - (showUngradedAssignments || subsection.hasGradedAssignment) + (showUngradedAssignments() || subsection.hasGradedAssignment) && subsection.showGrades && (subsection.numPointsPossible > 0 || subsection.numPointsEarned > 0) ), diff --git a/src/course-home/progress-tab/utils.ts b/src/course-home/progress-tab/utils.ts new file mode 100644 index 0000000000..29dd42de85 --- /dev/null +++ b/src/course-home/progress-tab/utils.ts @@ -0,0 +1,7 @@ +import { getConfig } from '@edx/frontend-platform'; + +/* eslint-disable import/prefer-default-export */ +export const showUngradedAssignments = () => ( + getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === 'true' + || getConfig().SHOW_UNGRADED_ASSIGNMENT_PROGRESS === true +);