From 56e90d71695ff518ac0e930751ffef6cc98cae5b Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 7 Aug 2023 11:37:57 -0400 Subject: [PATCH 1/5] feat: add frontend filtering - can filter by username and status - removed redundant exam name column (for now) --- .../ExamsPage/components/AttemptList.jsx | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/pages/ExamsPage/components/AttemptList.jsx b/src/pages/ExamsPage/components/AttemptList.jsx index 006c249..c86e780 100644 --- a/src/pages/ExamsPage/components/AttemptList.jsx +++ b/src/pages/ExamsPage/components/AttemptList.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import { DataTable } from '@edx/paragon'; +import { DataTable, TextFilter, CheckboxFilter } from '@edx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; import ResetExamAttemptButton from './ResetExamAttemptButton'; import ReviewExamAttemptButton from './ReviewExamAttemptButton'; @@ -27,6 +27,57 @@ const ExamAttemptStatusUILabels = { error: 'Error', }; +const StatusFilterChoices = [ + { + name: 'Created', + value: 'created', + }, + { + name: 'Download Software Clicked', + value: 'download_software_clicked', + }, + { + name: 'Ready To Start', + value: 'ready_to_start', + }, + { + name: 'Started', + value: 'started', + }, + { + name: 'Ready To Submit', + value: 'ready_to_submit', + }, + { + name: 'Timed Out', + value: 'timed_out', + }, + { + name: 'Submitted', + value: 'submitted', + }, + { + name: 'Verified', + value: 'verified', + }, + { + name: 'Rejected', + value: 'rejected', + }, + { + name: 'Expired', + value: 'expired', + }, + { + name: 'Second Review Required', + value: 'second_review_required', + }, + { + name: 'Error', + value: 'error', + }, +] + // The button components must be compartmentalized here otherwise npm lint throws an unstable-nested-component error. const ResetButton = (row) => ( { initialState={{ pageSize: 20, }} + isFilterable + defaultColumnValues={{ Filter: TextFilter }} isSortable itemCount={attempts.length} additionalColumns={[ @@ -73,10 +126,6 @@ const AttemptList = ({ attempts }) => { ]} data={attempts} columns={[ - { - Header: formatMessage(messages.examAttemptsTableHeaderExamName), - accessor: 'exam_name', - }, { Header: formatMessage(messages.examAttemptsTableHeaderUsername), accessor: 'username', @@ -84,6 +133,7 @@ const AttemptList = ({ attempts }) => { { Header: formatMessage(messages.examAttemptsTableHeaderTimeLimit), accessor: 'time_limit', + disableFilters: true, }, { Header: formatMessage(messages.examAttemptsTableHeaderExamType), @@ -111,7 +161,11 @@ const AttemptList = ({ attempts }) => { }, { Header: formatMessage(messages.examAttemptsTableHeaderStatus), + accessor: 'status', Cell: ({ row }) => ExamAttemptStatusUILabels[row.original.status], + Filter: CheckboxFilter, + filter: 'includesValue', + filterChoices: StatusFilterChoices, }, ]} > From b32123a485abf183d3d8f212ced291a654150f1b Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 7 Aug 2023 11:47:35 -0400 Subject: [PATCH 2/5] fix: lint --- src/pages/ExamsPage/components/AttemptList.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ExamsPage/components/AttemptList.jsx b/src/pages/ExamsPage/components/AttemptList.jsx index c86e780..54af3eb 100644 --- a/src/pages/ExamsPage/components/AttemptList.jsx +++ b/src/pages/ExamsPage/components/AttemptList.jsx @@ -76,7 +76,7 @@ const StatusFilterChoices = [ name: 'Error', value: 'error', }, -] +]; // The button components must be compartmentalized here otherwise npm lint throws an unstable-nested-component error. const ResetButton = (row) => ( From fd969e60101e5b086ae54dac8f11766c24f62fcd Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 7 Aug 2023 12:13:26 -0400 Subject: [PATCH 3/5] feat: StatusFilterChoices uses constants now --- .../ExamsPage/components/AttemptList.jsx | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/pages/ExamsPage/components/AttemptList.jsx b/src/pages/ExamsPage/components/AttemptList.jsx index 54af3eb..7fe721d 100644 --- a/src/pages/ExamsPage/components/AttemptList.jsx +++ b/src/pages/ExamsPage/components/AttemptList.jsx @@ -4,6 +4,7 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import ResetExamAttemptButton from './ResetExamAttemptButton'; import ReviewExamAttemptButton from './ReviewExamAttemptButton'; import messages from '../messages'; +import ExamAttemptStatus from 'data/constants' const ExamTypes = { proctored: 'Proctored', @@ -29,52 +30,52 @@ const ExamAttemptStatusUILabels = { const StatusFilterChoices = [ { - name: 'Created', - value: 'created', + name: ExamAttemptStatusUILabels.created, + value: ExamAttemptStatus.created, }, { - name: 'Download Software Clicked', - value: 'download_software_clicked', + name: ExamAttemptStatusUILabels.download_software_clicked, + value: ExamAttemptStatus.download_software_clicked, }, { - name: 'Ready To Start', - value: 'ready_to_start', + name: ExamAttemptStatusUILabels.ready_to_start, + value: ExamAttemptStatus.ready_to_start, }, { - name: 'Started', - value: 'started', + name: ExamAttemptStatusUILabels.started, + value: ExamAttemptStatus.started, }, { - name: 'Ready To Submit', - value: 'ready_to_submit', + name: ExamAttemptStatusUILabels.ready_to_submit, + value: ExamAttemptStatus.ready_to_submit, }, { - name: 'Timed Out', - value: 'timed_out', + name: ExamAttemptStatusUILabels.timed_out, + value: ExamAttemptStatus.timed_out, }, { - name: 'Submitted', - value: 'submitted', + name: ExamAttemptStatusUILabels.submitted, + value: ExamAttemptStatus.submitted, }, { - name: 'Verified', - value: 'verified', + name: ExamAttemptStatusUILabels.verified, + value: ExamAttemptStatus.verified, }, { - name: 'Rejected', - value: 'rejected', + name: ExamAttemptStatusUILabels.rejected, + value: ExamAttemptStatus.rejected, }, { - name: 'Expired', - value: 'expired', + name: ExamAttemptStatusUILabels.expired, + value: ExamAttemptStatus.expired, }, { - name: 'Second Review Required', - value: 'second_review_required', + name: ExamAttemptStatusUILabels.second_review_required, + value: ExamAttemptStatus.second_review_required, }, { - name: 'Error', - value: 'error', + name: ExamAttemptStatusUILabels.error, + value: ExamAttemptStatus.error, }, ]; From 503e9df5bd644ddf3c8d0fa7ef3e1c168cd80998 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 7 Aug 2023 13:57:29 -0400 Subject: [PATCH 4/5] test: added filtering tests --- .../__snapshots__/index.test.jsx.snap | 246 +++++++++----- .../ExamsPage/components/AttemptList.jsx | 27 +- .../ExamsPage/components/AttemptList.test.jsx | 31 +- .../__snapshots__/AttemptList.test.jsx.snap | 320 +++++++++--------- 4 files changed, 353 insertions(+), 271 deletions(-) diff --git a/src/pages/ExamsPage/__snapshots__/index.test.jsx.snap b/src/pages/ExamsPage/__snapshots__/index.test.jsx.snap index 9f06fc2..d00be79 100644 --- a/src/pages/ExamsPage/__snapshots__/index.test.jsx.snap +++ b/src/pages/ExamsPage/__snapshots__/index.test.jsx.snap @@ -138,26 +138,73 @@ Object { class="pgn__data-table-status-bar" >
- FormattedMessage +
+
+ + +
+
+
-
+
+
+
+ FormattedMessage +
+
+
+
- - - - Exam Name - - - - - - Status + + + - - Exam 1 -
- FormattedMessage +
+
+ + +
+
+
-
+
+
+
+ FormattedMessage +
+
+
+
- - - - Exam Name - - - - - - Status + + + - - Exam 1 - { const defaultAttemptsData = [ { exam_name: 'Exam 1', - username: 'username', + username: 'username1', time_limit: 60, exam_type: 'timed', started_at: '2023-04-05T19:27:16.000000Z', @@ -32,12 +34,12 @@ describe('AttemptList', () => { }, { exam_name: 'Exam 2', - username: 'username', + username: 'username2', time_limit: 60, exam_type: 'proctored', started_at: '2023-04-05T19:37:16.000000Z', completed_at: '2023-04-05T19:37:17.000000Z', - status: 'second_review_required', + status: 'submitted', attempt_id: 1, }, ]; @@ -75,4 +77,25 @@ describe('AttemptList', () => { } }); }); + it('filtering by status displays the correct entry', () => { + render(); + // Get the 2nd row of data which has the values of defaultAttemptsData[1] + const secondRow = screen.getAllByRole('row')[2]; + screen.getByText('Filters').click(); + screen.getByLabelText('Second Review Required').click(); + // Expect the first data entry, but not the second from defaultAttemptsData + // NOTE: row with index '0' in 'screen' is the header row. + expect(screen.getAllByRole('row')[1]).toBeInTheDocument(); + expect(secondRow).not.toBeInTheDocument(); + }); + it('filtering by username displays the correct entry', () => { + render(); + // Get the 2nd row of data which has the values of defaultAttemptsData[1] + const secondRow = screen.getAllByRole('row')[2]; + const searchInput = screen.getByLabelText('Search username'); + fireEvent.change(searchInput, { target: { value: 'username1' } }); + // Expect the first data entry, but not the second from defaultAttemptsData + expect(screen.getAllByRole('row')[1]).toBeInTheDocument(); + expect(secondRow).not.toBeInTheDocument(); + }); }); diff --git a/src/pages/ExamsPage/components/__snapshots__/AttemptList.test.jsx.snap b/src/pages/ExamsPage/components/__snapshots__/AttemptList.test.jsx.snap index d4a6057..3087675 100644 --- a/src/pages/ExamsPage/components/__snapshots__/AttemptList.test.jsx.snap +++ b/src/pages/ExamsPage/components/__snapshots__/AttemptList.test.jsx.snap @@ -21,26 +21,73 @@ Object { class="pgn__data-table-status-bar" >
- FormattedMessage +
+
+ + +
+
+
-
+
+
+
+ FormattedMessage +
+
+
+
- - - - Exam Name - - - - - - Status + + + - Exam 1 - - - username + username1 - Exam 2 - - - username + username2 - Second Review Required + Submitted -
- -
- + /> @@ -530,26 +530,73 @@ Object { class="pgn__data-table-status-bar" >
- FormattedMessage +
+
+ + +
+
+
-
+
+
+
+ FormattedMessage +
+
+
+
- - - - Exam Name - - - - - - Status + + + - Exam 1 - - - username + username1 - Exam 2 - - - username + username2 - Second Review Required + Submitted -
- -
- + /> From a2e392eb2dc054d6694c1580629e4c4e459b8fb9 Mon Sep 17 00:00:00 2001 From: ilee2u Date: Mon, 7 Aug 2023 14:08:19 -0400 Subject: [PATCH 5/5] chore: extra comment --- src/pages/ExamsPage/components/AttemptList.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ExamsPage/components/AttemptList.jsx b/src/pages/ExamsPage/components/AttemptList.jsx index 3bb1a44..b488c98 100644 --- a/src/pages/ExamsPage/components/AttemptList.jsx +++ b/src/pages/ExamsPage/components/AttemptList.jsx @@ -5,7 +5,6 @@ import * as constants from 'data/constants'; import ResetExamAttemptButton from './ResetExamAttemptButton'; import ReviewExamAttemptButton from './ReviewExamAttemptButton'; import messages from '../messages'; -// import ExamAttemptStatus from 'data/constants' const ExamTypes = { proctored: 'Proctored',