Skip to content

Commit

Permalink
feat: refactor non instruction components
Browse files Browse the repository at this point in the history
  • Loading branch information
alangsto committed Feb 2, 2024
1 parent 91a3d01 commit 93a1ea6
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 198 deletions.
4 changes: 0 additions & 4 deletions src/context.jsx

This file was deleted.

35 changes: 0 additions & 35 deletions src/core/ExamStateProvider.jsx

This file was deleted.

13 changes: 5 additions & 8 deletions src/core/OuterExamTimer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import React, { useEffect, useContext } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { AppContext } from '@edx/frontend-platform/react';
import ExamStateContext from '../context';
import { getLatestAttemptData } from '../data';
import { ExamTimerBlock } from '../timer';
import ExamAPIError from '../exam/ExamAPIError';
import ExamStateProvider from './ExamStateProvider';
import { getLatestAttemptData } from '../data/thunks';
import { IS_STARTED_STATUS } from '../constants';

const ExamTimer = ({ courseId }) => {
const examState = useContext(ExamStateContext);
const { activeAttempt } = useSelector(state => state.specialExams);
const { authenticatedUser } = useContext(AppContext);
const { showTimer } = examState;
const showTimer = !!(activeAttempt && IS_STARTED_STATUS(activeAttempt.attempt_status));

const { apiErrorMsg } = useSelector(state => state.specialExams);

Expand Down Expand Up @@ -48,9 +47,7 @@ ExamTimer.propTypes = {
* will be shown.
*/
const OuterExamTimer = ({ courseId }) => (
<ExamStateProvider>
<ExamTimer courseId={courseId} />
</ExamStateProvider>
<ExamTimer courseId={courseId} />
);

OuterExamTimer.propTypes = {
Expand Down
5 changes: 1 addition & 4 deletions src/core/SequenceExamWrapper.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import ExamWrapper from '../exam/ExamWrapper';
import ExamStateProvider from './ExamStateProvider';

/**
* SequenceExamWrapper is the component responsible for handling special exams.
Expand All @@ -14,9 +13,7 @@ import ExamStateProvider from './ExamStateProvider';
* </SequenceExamWrapper>
*/
const SequenceExamWrapper = (props) => (
<ExamStateProvider>
<ExamWrapper {...props} />
</ExamStateProvider>
<ExamWrapper {...props} />
);

export default SequenceExamWrapper;
16 changes: 8 additions & 8 deletions src/data/store.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { configureStore } from '@reduxjs/toolkit';
import examReducer from './slice';

export default configureStore({
reducer: {
specialExams: examReducer,
},
});
// import { configureStore } from '@reduxjs/toolkit';
// import examReducer from './slice';
//
// export default configureStore({
// reducer: {
// examState: examReducer,
// },
// });
19 changes: 11 additions & 8 deletions src/exam/Exam.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useContext, useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { Alert, Spinner } from '@edx/paragon';
import { Info } from '@edx/paragon/icons';
import { ExamTimerBlock } from '../timer';
import Instructions from '../instructions';
import ExamStateContext from '../context';
import ExamAPIError from './ExamAPIError';
import { ExamStatus, ExamType } from '../constants';
import { ExamStatus, ExamType, IS_STARTED_STATUS } from '../constants';
import messages from './messages';
import { getProctoringSettings } from '../data/thunks';

/**
* Exam component is intended to render exam instructions before and after exam.
Expand All @@ -23,10 +23,12 @@ import messages from './messages';
const Exam = ({
isGated, isTimeLimited, originalUserIsStaff, canAccessProctoredExams, children, intl,
}) => {
const state = useContext(ExamStateContext);
const {
isLoading, showTimer, exam, apiErrorMsg, getProctoringSettings,
} = state;
isLoading, activeAttempt, exam, apiErrorMsg,
} = useSelector(state => state.specialExams);
const dispatch = useDispatch();

const showTimer = !!(activeAttempt && IS_STARTED_STATUS(activeAttempt.attempt_status));

const {
attempt,
Expand Down Expand Up @@ -59,7 +61,7 @@ const Exam = ({
if (proctoredExamTypes.includes(examType)) {
// only fetch proctoring settings for a proctored exam
if (examId) {
getProctoringSettings();
dispatch(getProctoringSettings());
}

// Only exclude Timed Exam when restricting access to exams
Expand All @@ -68,6 +70,7 @@ const Exam = ({
// this makes sure useEffect gets called only one time after the exam has been fetched
// we can't leave this empty since initially exam is just an empty object, so
// API calls above would not get triggered
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [examId]);

if (isLoading) {
Expand Down
7 changes: 3 additions & 4 deletions src/exam/ExamAPIError.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React, { useContext } from 'react';
import React from 'react';
import { useSelector } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
import { Alert, Hyperlink, Icon } from '@edx/paragon';
import { Info } from '@edx/paragon/icons';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import ExamStateContext from '../context';
import messages from './messages';

const ExamAPIError = ({ intl }) => {
const state = useContext(ExamStateContext);
const { SITE_NAME, SUPPORT_URL } = getConfig();
const { apiErrorMsg } = state;
const { apiErrorMsg } = useSelector(state => state.specialExams);
const shouldShowApiErrorMsg = !!apiErrorMsg && !apiErrorMsg.includes('<');

return (
Expand Down
21 changes: 5 additions & 16 deletions src/exam/ExamAPIError.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { store } from '../data';
import { render } from '../setupTest';
import ExamStateProvider from '../core/ExamStateProvider';
import ExamAPIError from './ExamAPIError';

const originalConfig = jest.requireActual('@edx/frontend-platform').getConfig();
Expand All @@ -26,9 +25,7 @@ describe('ExamAPIError', () => {
store.getState = () => ({ specialExams: {} });

const tree = render(
<ExamStateProvider>
<ExamAPIError />
</ExamStateProvider>,
<ExamAPIError />,
{ store },
);

Expand All @@ -45,9 +42,7 @@ describe('ExamAPIError', () => {
store.getState = () => ({ specialExams: {} });

const { getByTestId } = render(
<ExamStateProvider>
<ExamAPIError />
</ExamStateProvider>,
<ExamAPIError />,
{ store },
);

Expand All @@ -62,9 +57,7 @@ describe('ExamAPIError', () => {
});

const { queryByTestId } = render(
<ExamStateProvider>
<ExamAPIError />
</ExamStateProvider>,
<ExamAPIError />,
{ store },
);

Expand All @@ -77,9 +70,7 @@ describe('ExamAPIError', () => {
});

const { queryByTestId } = render(
<ExamStateProvider>
<ExamAPIError />
</ExamStateProvider>,
<ExamAPIError />,
{ store },
);

Expand All @@ -92,9 +83,7 @@ describe('ExamAPIError', () => {
});

const { queryByTestId } = render(
<ExamStateProvider>
<ExamAPIError />
</ExamStateProvider>,
<ExamAPIError />,
{ store },
);

Expand Down
20 changes: 14 additions & 6 deletions src/exam/ExamWrapper.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { useDispatch, useSelector } from 'react-redux';
import React, { useContext, useEffect } from 'react';
import { AppContext } from '@edx/frontend-platform/react';
import PropTypes from 'prop-types';
import Exam from './Exam';
import ExamStateContext from '../context';
import {
getExamAttemptsData,
getAllowProctoringOptOut,
checkExamEntry,
} from '../data/thunks';

/**
* Exam wrapper is responsible for triggering initial exam data fetching and rendering Exam.
*/
const ExamWrapper = ({ children, ...props }) => {
const state = useContext(ExamStateContext);
const { authenticatedUser } = useContext(AppContext);
const {
sequence,
Expand All @@ -17,9 +21,13 @@ const ExamWrapper = ({ children, ...props }) => {
originalUserIsStaff,
canAccessProctoredExams,
} = props;
const { getExamAttemptsData, getAllowProctoringOptOut, checkExamEntry } = state;

const { isLoading } = useSelector(state => state.specialExams);

const dispatch = useDispatch();

const loadInitialData = async () => {
await getExamAttemptsData(courseId, sequence.id);
await dispatch(getExamAttemptsData(courseId, sequence.id));
await getAllowProctoringOptOut(sequence.allowProctoringOptOut);
await checkExamEntry();
};
Expand All @@ -28,10 +36,10 @@ const ExamWrapper = ({ children, ...props }) => {

useEffect(() => {
// fetch exam data on exam sequences or if no exam data has been fetched yet
if (sequence.isTimeLimited || state.isLoading) {
if (sequence.isTimeLimited || isLoading) {
loadInitialData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// if the user is browsing public content (not logged in) they cannot be in an exam
Expand Down
Loading

0 comments on commit 93a1ea6

Please sign in to comment.