Skip to content

Commit 87fa67d

Browse files
feat: global data state
1 parent 2b35d47 commit 87fa67d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1564
-269
lines changed

package-lock.json

Lines changed: 493 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
"@edx/brand": "npm:@edx/[email protected]",
3838
"@edx/frontend-component-footer": "12.4.0",
3939
"@edx/frontend-component-header": "4.7.1",
40-
"@edx/frontend-platform": "5.6.0",
41-
"@edx/paragon": "^20.20.0",
40+
"@edx/frontend-platform": "5.6.1",
41+
"@edx/paragon": "^21.5.3",
4242
"@edx/react-unit-test-utils": "1.7.0",
4343
"@edx/tinymce-language-selector": "1.1.0",
4444
"@fortawesome/fontawesome-svg-core": "1.2.36",
@@ -54,6 +54,7 @@
5454
"core-js": "3.32.2",
5555
"filesize": "^8.0.6",
5656
"jest-when": "^3.6.0",
57+
"moment": "^2.29.4",
5758
"pdfjs-dist": "^3.11.174",
5859
"prop-types": "15.8.1",
5960
"query-string": "^8.1.0",

src/App.jsx

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,17 @@ const RouterRoot = () => {
2424
<Route
2525
path={route}
2626
element={(
27-
<PageDataProvider>
28-
<AppContainer Component={Component} />
29-
</PageDataProvider>
27+
<AppContainer Component={Component} />
3028
)}
3129
/>
3230
);
3331
const modalRoute = (route, Component, title) => (
3432
<Route
3533
path={route}
3634
element={(
37-
<PageDataProvider>
38-
<ModalContainer {...{ title, Component }} />
39-
</PageDataProvider>
35+
<ModalContainer title={title}>
36+
<AppContainer Component={Component} />
37+
</ModalContainer>
4038
)}
4139
/>
4240
);
@@ -50,29 +48,14 @@ const RouterRoot = () => {
5048
<Route path={routes.rootEmbed} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
5149
];
5250
const baseRoutes = [
53-
appRoute(routes.xblock, PeerAssessmentView),
54-
appRoute(routes.peerAssessment, PeerAssessmentView),
55-
appRoute(routes.selfAssessment, SelfAssessmentView),
56-
appRoute(routes.studentTraining, StudentTrainingView),
57-
appRoute(routes.submission, SubmissionView),
51+
appRoute(routes.xblock, XBlockView),
52+
modalRoute(routes.peerAssessment, PeerAssessmentView, 'Assess your peers'),
53+
modalRoute(routes.selfAssessment, SelfAssessmentView, 'Assess yourself'),
54+
modalRoute(routes.studentTraining, StudentTrainingView, 'Practice grading'),
55+
modalRoute(routes.submission, SubmissionView, 'Your response'),
5856
<Route path={routes.root} element={<ErrorPage message={formatMessage(messages.error404Message)} />} />,
5957
];
6058

61-
const isConfigLoaded = useIsORAConfigLoaded();
62-
63-
if (!isConfigLoaded) {
64-
return (
65-
<div className="h-screen d-flex justify-content-center align-items-center">
66-
<Spinner
67-
animation="border"
68-
variant="primary"
69-
className="mr-3 spinner-md"
70-
screenReaderText="loading"
71-
/>
72-
</div>
73-
);
74-
}
75-
7659
return (
7760
<Routes>
7861
{embeddedRoutes}

src/components/AppContainer.jsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33

4+
import { useIsPageDataLoaded, useIsORAConfigLoaded } from 'data/services/lms/hooks/selectors';
5+
46
/* The purpose of this component is to wrap views with a header/footer for situations
57
* where we need to run non-embedded
68
*/
79

8-
const AppContainer = ({ Component }) => (
9-
<div className="bg-light-300">
10-
<Component />
11-
</div>
12-
);
10+
const AppContainer = ({ Component }) => {
11+
const isConfigLoaded = useIsORAConfigLoaded();
12+
const isPageDataLoaded = useIsPageDataLoaded();
13+
if (!isConfigLoaded || !isPageDataLoaded) {
14+
return null;
15+
}
16+
return <Component />;
17+
};
1318
AppContainer.propTypes = {
1419
Component: PropTypes.elementType.isRequired,
1520
};

src/components/Assessment/EditableAssessment/hooks.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,6 @@ const useEditableAssessmentData = () => {
2525
submitRubricMutation.mutate(currentValue);
2626
};
2727

28-
console.log({
29-
criteria,
30-
formFields,
31-
onSubmit,
32-
});
33-
3428
return {
3529
criteria,
3630
formFields,

src/components/FileUpload/UploadConfirmModal.jsx

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
44
import {
55
Form, FormLabel, ModalDialog, Button, ActionRow,
66
} from '@edx/paragon';
7-
import { useIntl } from '@edx/frontend-platform/i18n';
7+
import { useIntl, IntlProvider } from '@edx/frontend-platform/i18n';
88
import messages from './messages';
99
import { useUploadConfirmModalHooks } from './hooks';
1010

@@ -39,28 +39,30 @@ const UploadConfirmModal = ({
3939
</ModalDialog.Header>
4040

4141
<ModalDialog.Body>
42-
<div>
43-
{file && (
44-
<Form.Group>
45-
<FormLabel>
46-
<strong>
47-
{formatMessage(messages.uploadFileDescriptionFieldLabel)}
48-
</strong>
49-
<span className="file-name-ellipsis">{file.name}</span>
50-
</FormLabel>
51-
<Form.Control
52-
isInvalid={shouldShowError}
53-
name="file-description"
54-
onChange={onFileDescriptionChange}
55-
/>
56-
{shouldShowError && (
57-
<Form.Control.Feedback type="invalid">
58-
formatMessage(messages.fileDescriptionMissingError)
59-
</Form.Control.Feedback>
60-
)}
61-
</Form.Group>
62-
)}
63-
</div>
42+
<IntlProvider>
43+
<div>
44+
{file && (
45+
<Form.Group>
46+
<FormLabel>
47+
<strong>
48+
{formatMessage(messages.uploadFileDescriptionFieldLabel)}
49+
</strong>
50+
<span className="file-name-ellipsis">{file.name}</span>
51+
</FormLabel>
52+
<Form.Control
53+
isInvalid={shouldShowError}
54+
name="file-description"
55+
onChange={onFileDescriptionChange}
56+
/>
57+
{shouldShowError && (
58+
<Form.Control.Feedback type="invalid">
59+
formatMessage(messages.fileDescriptionMissingError)
60+
</Form.Control.Feedback>
61+
)}
62+
</Form.Group>
63+
)}
64+
</div>
65+
</IntlProvider>
6466
</ModalDialog.Body>
6567
<ModalDialog.Footer>
6668
<ActionRow>

src/components/FileUpload/index.jsx

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ const FileUpload = ({
3535
} = useFileUploadHooks({
3636
onFileUploaded,
3737
});
38-
3938
return (
4039
<div>
4140
<h3>File Upload</h3>
@@ -46,8 +45,7 @@ const FileUpload = ({
4645
itemCount={uploadedFiles.length}
4746
data={uploadedFiles.map((file) => ({
4847
...file,
49-
size:
50-
typeof file.size === 'number' ? filesize(file.size) : 'Unknown',
48+
size: typeof file.size === 'number' ? filesize(file.size) : 'Unknown',
5149
}))}
5250
columns={[
5351
{
@@ -72,19 +70,15 @@ const FileUpload = ({
7270
</>
7371
)}
7472
{!isReadOnly && (
75-
<>
76-
<Dropzone
77-
multiple
78-
onProcessUpload={onProcessUpload}
79-
progressVariant="bar"
80-
/>
81-
<UploadConfirmModal
82-
open={isModalOpen}
83-
file={uploadArgs.fileData?.getAll('file')[0]}
84-
closeHandler={closeUploadModal}
85-
uploadHandler={confirmUpload}
86-
/>
87-
</>
73+
<Dropzone multiple onProcessUpload={onProcessUpload} progressVariant="bar" />
74+
)}
75+
{!isReadOnly && isModalOpen && (
76+
<UploadConfirmModal
77+
open={isModalOpen}
78+
file={uploadArgs.fileData?.getAll('file')[0]}
79+
closeHandler={closeUploadModal}
80+
uploadHandler={confirmUpload}
81+
/>
8882
)}
8983
</div>
9084
);

src/components/Instructions/index.jsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import useInstructionsMessage from './useInstructionsMessage';
5+
6+
const Instructions = ({ step }) => {
7+
const message = useInstructionsMessage(step);
8+
return (
9+
<div>
10+
<h2>Instructions</h2>
11+
{message}
12+
</div>
13+
);
14+
};
15+
Instructions.defaultProps = {
16+
step: null,
17+
};
18+
Instructions.propTypes = {
19+
step: PropTypes.string,
20+
};
21+
export default Instructions;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { defineMessages } from '@edx/frontend-platform/i18n';
2+
3+
import { stepNames } from 'data/services/lms/constants';
4+
5+
const messages = defineMessages({
6+
[stepNames.submission]: {
7+
defaultMessage: '<Submittion Instructions: TODO>',
8+
description: 'Submission step instructions',
9+
id: 'frontend-app-ora.instructions.submisison',
10+
},
11+
[stepNames.studentTraining]: {
12+
defaultMessage: '<Student Training Instructions: TODO>',
13+
description: 'StudentTraining step instructions',
14+
id: 'frontend-app-ora.instructions.studentTraining',
15+
},
16+
[stepNames.self]: {
17+
defaultMessage: '<Self Assessment Instructions: TODO>',
18+
description: 'Self Assessment step instructions',
19+
id: 'frontend-app-ora.instructions.selfAssessment',
20+
},
21+
[stepNames.peer]: {
22+
defaultMessage: '<Peer Assessment Instructions: TODO>',
23+
description: 'Peer Assessment step instructions',
24+
id: 'frontend-app-ora.instructions.peerAssessment',
25+
},
26+
[stepNames.done]: {
27+
defaultMessage: 'You have successfully completed this problem and received a {earned}/{possible}.',
28+
description: 'Graded step instructions',
29+
id: 'frontend-app-ora.instructions.done',
30+
},
31+
});
32+
33+
export default messages;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useIntl } from '@edx/frontend-platform/i18n';
2+
3+
import { useGlobalState } from 'data/services/lms/hooks/selectors';
4+
import { stepNames, stepStates } from 'data/services/lms/constants';
5+
6+
import messages from './messages';
7+
8+
const useInstructionsMessage = (step = null) => {
9+
const { formatMessage } = useIntl();
10+
const {
11+
activeStepName,
12+
effectiveGrade,
13+
stepState,
14+
} = useGlobalState();
15+
if (step || stepState !== stepStates.inProgress) {
16+
return null;
17+
}
18+
const stepName = step || activeStepName;
19+
if (stepName === stepNames.done) {
20+
return formatMessage(messages[stepNames.done], effectiveGrade);
21+
}
22+
return formatMessage(messages[activeStepName]);
23+
};
24+
25+
export default useInstructionsMessage;

src/components/ModalContainer.jsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import { FullscreenModal } from '@edx/paragon';
4+
import { IntlProvider } from '@edx/frontend-platform/i18n';
5+
6+
import AppContainer from 'components/AppContainer';
47

58
/* The purpose of this component is to wrap views with a header/footer for situations
69
* where we need to run non-embedded
710
*/
811

9-
const ModalContainer = ({ Component, title }) => (
10-
<FullscreenModal
11-
isOpen
12-
onClose={null}
13-
hasCloseButton={false}
14-
title={title}
15-
modalBodyClassName="content-body"
16-
>
17-
<Component />
18-
</FullscreenModal>
19-
);
12+
const ModalContainer = ({ title, children }) => {
13+
console.log({ children, title });
14+
return (
15+
<FullscreenModal
16+
isOpen
17+
onClose={null}
18+
hasCloseButton={false}
19+
title={title}
20+
modalBodyClassName="content-body bg-light-300"
21+
>
22+
{children}
23+
</FullscreenModal>
24+
);
25+
};
2026
ModalContainer.propTypes = {
21-
Component: PropTypes.elementType.isRequired,
27+
children: PropTypes.node.isRequired,
2228
title: PropTypes.string.isRequired,
2329
};
2430

src/components/ProgressBar/ProgressStep.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const stepIcons = StrictDict({
2020
[stepNames.studentTraining]: Highlight,
2121
[stepNames.self]: Highlight,
2222
[stepNames.peer]: Highlight,
23-
[stepNames.myGrades]: Rule,
23+
[stepNames.done]: Rule,
2424
});
2525

2626
const ProgressStep = ({
@@ -45,7 +45,7 @@ const ProgressStep = ({
4545
subLabel = 'Past due!';
4646
} else if (isComplete) {
4747
iconSrc = CheckCircle;
48-
if (step === stepNames.myGrades && myGrade) {
48+
if (step === stepNames.done && myGrade) {
4949
subLabel = `${myGrade.earned} / ${myGrade.possible}`;
5050
}
5151
}

src/components/ProgressBar/index.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ export const stepLabels = {
1919
[stepNames.peer]: messages.peerAssess,
2020
[stepNames.studentTraining]: messages.studentTraining,
2121
[stepNames.self]: messages.selfAssess,
22-
[stepNames.myGrades]: messages.myGrade,
22+
[stepNames.done]: messages.myGrade,
2323
};
2424

2525
export const stepCanRevisit = {
2626
[stepNames.submission]: true,
2727
[stepNames.peer]: true,
2828
[stepNames.studentTraining]: false,
2929
[stepNames.self]: false,
30-
[stepNames.myGrades]: true,
30+
[stepNames.done]: true,
3131
};
3232

3333
export const ProgressBar = () => {
@@ -58,7 +58,7 @@ export const ProgressBar = () => {
5858
<hr className="ora-progress-divider" />
5959
{stepEl(stepNames.submission)}
6060
{stepOrder.map(stepEl)}
61-
{stepEl(stepNames.myGrades)}
61+
{stepEl(stepNames.done)}
6262
</Navbar.Collapse>
6363
</Navbar>
6464
);

0 commit comments

Comments
 (0)