From d6c1722c7ea412c51899fe62e00819cc7bd4b177 Mon Sep 17 00:00:00 2001 From: michaelroytman Date: Thu, 1 Feb 2024 10:33:35 -0500 Subject: [PATCH] refactor: replace use of context with thunks and Redux store in instructions components This commit replaces the use of the ExamStateContext with the use of thunks and the Redux store in the instructions components. The original pattern was to use the withExamStore higher-order component to provide context to the instructions components. This context contained provided the Redux store state and action creators as props by using the connect API. This posed a problem for our need to merge the frontend-app-learning and frontend-lib-special-exams stores, because the special exams store is initialized in this repository and used by the higher-order component. In order to eventually be able to remove the creation of the store in this repository, we have to remove references to the store by interfacing with the Redux more directly by using the useDispatch and useSelector hooks. --- src/data/index.js | 1 + src/instructions/Instructions.test.jsx | 13 ++++++------ src/instructions/SubmitInstructions.jsx | 13 +++++++----- src/instructions/index.jsx | 7 +++---- .../EntranceOnboardingExamInstructions.jsx | 14 ++++++++----- .../ErrorOnboardingExamInstructions.jsx | 11 +++++----- .../RejectedOnboardingExamInstructions.jsx | 14 ++++++++----- .../SubmittedOnboardingExamInstructions.jsx | 15 +++++++++----- .../VerifiedOnboardingExamInstructions.jsx | 9 +++++---- .../EntrancePracticeExamInstructions.jsx | 11 +++++----- .../ErrorPracticeExamInstructions.jsx | 11 +++++----- .../SubmittedPracticeExamInstructions.jsx | 11 +++++----- .../EntranceProctoredExamInstructions.jsx | 14 ++++++++----- .../ErrorProctoredExamInstructions.jsx | 12 +++++------ .../OnboardingErrorExamInstructions.jsx | 7 +++---- .../ProctoredExamInstructions.test.jsx | 6 ++---- .../ReadyToStartProctoredExamInstructions.jsx | 20 +++++++++---------- .../SkipProctoredExamInstruction.jsx | 11 +++++----- .../SubmitProctoredExamInstructions.jsx | 17 ++++++++-------- .../download-instructions/index.jsx | 17 ++++++++++------ .../prerequisites-instructions/index.jsx | 8 ++++---- .../timed_exam/StartTimedExamInstructions.jsx | 7 +++---- .../SubmitTimedExamInstructions.jsx | 11 +++++----- .../SubmittedTimedExamInstructions.jsx | 8 ++++---- 24 files changed, 148 insertions(+), 120 deletions(-) diff --git a/src/data/index.js b/src/data/index.js index 9c6cb1a3..27409017 100644 --- a/src/data/index.js +++ b/src/data/index.js @@ -1,4 +1,5 @@ export { + createProctoredExamAttempt, getExamAttemptsData, getLatestAttemptData, getProctoringSettings, diff --git a/src/instructions/Instructions.test.jsx b/src/instructions/Instructions.test.jsx index df8a2036..00157585 100644 --- a/src/instructions/Instructions.test.jsx +++ b/src/instructions/Instructions.test.jsx @@ -3,9 +3,10 @@ import { Factory } from 'rosie'; import React from 'react'; import { fireEvent, waitFor } from '@testing-library/dom'; import Instructions from './index'; -import { store, getExamAttemptsData, startTimedExam } from '../data'; +import { + store, continueExam, getExamAttemptsData, startProctoredExam, startTimedExam, submitExam, +} from '../data'; import { pollExamAttempt, softwareDownloadAttempt } from '../data/api'; -import { continueExam, submitExam } from '../data/thunks'; import Emitter from '../data/emitter'; import { TIMER_REACHED_NULL } from '../timer/events'; import { @@ -18,12 +19,11 @@ import { jest.mock('../data', () => ({ store: {}, - getExamAttemptsData: jest.fn(), - startTimedExam: jest.fn(), -})); -jest.mock('../data/thunks', () => ({ continueExam: jest.fn(), + getExamAttemptsData: jest.fn(), getExamReviewPolicy: jest.fn(), + startProctoredExam: jest.fn(), + startTimedExam: jest.fn(), submitExam: jest.fn(), })); jest.mock('../data/api', () => ({ @@ -33,6 +33,7 @@ jest.mock('../data/api', () => ({ continueExam.mockReturnValue(jest.fn()); submitExam.mockReturnValue(jest.fn()); getExamAttemptsData.mockReturnValue(jest.fn()); +startProctoredExam.mockReturnValue(jest.fn()); startTimedExam.mockReturnValue(jest.fn()); pollExamAttempt.mockReturnValue(Promise.resolve({})); store.subscribe = jest.fn(); diff --git a/src/instructions/SubmitInstructions.jsx b/src/instructions/SubmitInstructions.jsx index a93a29ef..816c3e3f 100644 --- a/src/instructions/SubmitInstructions.jsx +++ b/src/instructions/SubmitInstructions.jsx @@ -1,17 +1,20 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { Button, Container } from '@edx/paragon'; import { FormattedMessage } from '@edx/frontend-platform/i18n'; import Emitter from '../data/emitter'; import { ExamType } from '../constants'; +import { continueExam } from '../data'; import { SubmitProctoredExamInstructions } from './proctored_exam'; import { SubmitTimedExamInstructions } from './timed_exam'; import Footer from './proctored_exam/Footer'; -import ExamStateContext from '../context'; import { TIMER_REACHED_NULL } from '../timer/events'; const SubmitExamInstructions = () => { - const state = useContext(ExamStateContext); - const { exam, continueExam, activeAttempt } = state; + const { exam, activeAttempt } = useSelector(state => state.specialExams); + + const dispatch = useDispatch(); + const { time_remaining_seconds: timeRemaining } = activeAttempt; const { type: examType } = exam || {}; const [canContinue, setCanContinue] = useState(timeRemaining > 0); @@ -33,7 +36,7 @@ const SubmitExamInstructions = () => { ? : } {canContinue && ( -