diff --git a/package-lock.json b/package-lock.json
index 5f0cade..292b677 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8230,6 +8230,11 @@
"domelementtype": "^2.2.0"
}
},
+ "dompurify": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.1.tgz",
+ "integrity": "sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw=="
+ },
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -11087,6 +11092,15 @@
"wbuf": "^1.1.0"
}
},
+ "html-dom-parser": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-1.0.2.tgz",
+ "integrity": "sha512-Jq4oVkVSn+10ut3fyc2P/Fs1jqTo0l45cP6Q8d2ef/9jfkYwulO0QXmyLI0VUiZrXF4czpGgMEJRa52CQ6Fk8Q==",
+ "requires": {
+ "domhandler": "4.2.2",
+ "htmlparser2": "6.1.0"
+ }
+ },
"html-element-map": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.1.tgz",
@@ -11140,6 +11154,17 @@
}
}
},
+ "html-react-parser": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-1.3.0.tgz",
+ "integrity": "sha512-lhpkOFH8pwqEjlNUYCWvjT43/JVCZO9MAZuCS6afT1/VP+bZcNxNUs4AUqiMzH0QPSDHwM/GFNXZNok1KTA4BQ==",
+ "requires": {
+ "domhandler": "4.2.2",
+ "html-dom-parser": "1.0.2",
+ "react-property": "2.0.0",
+ "style-to-js": "1.1.0"
+ }
+ },
"html-webpack-new-relic-plugin": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/html-webpack-new-relic-plugin/-/html-webpack-new-relic-plugin-4.1.0.tgz",
@@ -11795,6 +11820,11 @@
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
},
+ "inline-style-parser": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
+ "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="
+ },
"inquirer": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
@@ -21482,6 +21512,11 @@
"warning": "^4.0.2"
}
},
+ "react-property": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz",
+ "integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw=="
+ },
"react-proptype-conditional-require": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz",
@@ -23761,6 +23796,22 @@
"schema-utils": "^3.0.0"
}
},
+ "style-to-js": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.0.tgz",
+ "integrity": "sha512-1OqefPDxGrlMwcbfpsTVRyzwdhr4W0uxYQzeA2F1CBc8WG04udg2+ybRnvh3XYL4TdHQrCahLtax2jc8xaE6rA==",
+ "requires": {
+ "style-to-object": "0.3.0"
+ }
+ },
+ "style-to-object": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
+ "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==",
+ "requires": {
+ "inline-style-parser": "0.1.1"
+ }
+ },
"stylehacks": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz",
diff --git a/package.json b/package.json
index d248f4b..52ea153 100755
--- a/package.json
+++ b/package.json
@@ -39,11 +39,13 @@
"@reduxjs/toolkit": "^1.6.1",
"classnames": "^2.3.1",
"core-js": "3.16.2",
+ "dompurify": "^2.3.1",
"email-prop-type": "^3.0.1",
"enzyme": "^3.11.0",
"enzyme-to-json": "^3.6.2",
"font-awesome": "4.7.0",
"history": "5.0.1",
+ "html-react-parser": "^1.3.0",
"lodash": "^4.17.21",
"node-sass": "^6.0.1",
"prop-types": "15.7.2",
diff --git a/public/index.html b/public/index.html
index bbeaf01..3b10fb4 100755
--- a/public/index.html
+++ b/public/index.html
@@ -1,5 +1,5 @@
-
+
ORA Enhanced Staff Grader | <%= process.env.SITE_NAME %>
diff --git a/src/containers/ListView/ReviewActions.jsx b/src/containers/ListView/ReviewActions.jsx
index cfe3bac..4c13844 100644
--- a/src/containers/ListView/ReviewActions.jsx
+++ b/src/containers/ListView/ReviewActions.jsx
@@ -17,10 +17,14 @@ import StatusBadge from './StatusBadge';
import './ReviewModal.scss';
export const ReviewActions = ({ submission: { status, username } }) => (
-
- {username}
-
-
+
+
+ {username}
+
+
+ Show Rubric
+
+
);
ReviewActions.propTypes = {
submission: PropTypes.shape({
diff --git a/src/containers/ListView/ReviewModal.jsx b/src/containers/ListView/ReviewModal.jsx
index 600249b..8e1e4c8 100644
--- a/src/containers/ListView/ReviewModal.jsx
+++ b/src/containers/ListView/ReviewModal.jsx
@@ -7,7 +7,11 @@ import {
Container,
} from '@edx/paragon';
+import createDOMPurify from 'dompurify';
+import parse from 'html-react-parser';
+
import selectors from 'data/selectors';
+import actions from 'data/actions';
import thunkActions from 'data/thunkActions';
import ReviewActions from './ReviewActions';
@@ -22,9 +26,15 @@ export class ReviewModal extends React.Component {
super(props);
console.log('review modal');
console.log({ props });
+ this.purify = createDOMPurify(window);
+ this.onClose = this.onClose.bind(this);
}
- onClose = () => {
+ get textContent() {
+ return parse(this.purify.sanitize(this.props.response.text));
+ }
+
+ onClose() {
this.props.setShowReview(false);
}
@@ -37,7 +47,7 @@ export class ReviewModal extends React.Component {
onClose={this.onClose}
>
- {this.props.response.text}
+ {this.textContent}
);
diff --git a/src/containers/ListView/ReviewModal.scss b/src/containers/ListView/ReviewModal.scss
index f609a37..5dac7c4 100644
--- a/src/containers/ListView/ReviewModal.scss
+++ b/src/containers/ListView/ReviewModal.scss
@@ -1,6 +1,6 @@
-.pgn__modal-close-container {
- // TODO: need to be able to load this from paragon
- right: 1rem;
+@import "@edx/paragon/scss/core/core";
+.review-actions {
+ padding: map_get($spacers, 3);
}
#ora-esg-list-view {
padding: 20px;
diff --git a/src/containers/ListView/StatusBadge.jsx b/src/containers/ListView/StatusBadge.jsx
index f7ad063..93171fa 100644
--- a/src/containers/ListView/StatusBadge.jsx
+++ b/src/containers/ListView/StatusBadge.jsx
@@ -8,19 +8,23 @@ import { gradingStatuses as statuses } from 'data/services/lms/constants';
/**
*
*/
-export const StatusBadge = ({ status }) => {
+export const StatusBadge = ({ className, status }) => {
if (status === statuses.ungraded) {
- return (Ungraded );
+ return (Ungraded );
}
if (status === statuses.locked) {
- return (Grading in progress );
+ return (Grading in progress );
}
if (status === statuses.graded) {
- return (Grading Complete );
+ return (Grading Complete );
}
return ({status} );
};
+StatusBadge.defaultProps = {
+ className: '',
+};
StatusBadge.propTypes = {
+ className: PropTypes.string,
status: PropTypes.string.isRequired,
};
diff --git a/src/containers/ListView/index.jsx b/src/containers/ListView/index.jsx
index 228ff76..0086577 100644
--- a/src/containers/ListView/index.jsx
+++ b/src/containers/ListView/index.jsx
@@ -31,6 +31,7 @@ export class ListView extends React.Component {
constructor(props) {
super(props);
this.props.initializeApp();
+ this.handleViewAllResponsesClick = this.handleViewAllResponsesClick.bind(this);
}
formatDate = ({ value }) => {
@@ -44,7 +45,7 @@ export class ListView extends React.Component {
formatStatus = ({ value }) => ( );
- handleViewAllResponsesClick = (data) => {
+ handleViewAllResponsesClick(data) {
console.log("View all responses button clicked");
console.log(data);
if (data.selectedRows.length) {
@@ -56,16 +57,17 @@ export class ListView extends React.Component {
}
render() {
+ console.log({ props: this.props, length: this.props.listData.length });
return (
{
case actions.submissions.loadSubmission.toString():
return {
...state,
- selectedItem: payload.learnerId,
current: payload,
activeIndex: 0,
};
-
case actions.submissions.preloadNext.toString():
return { ...state, next: payload };
case actions.submissions.preloadPrev.toString():
diff --git a/src/data/selectors/submissions.js b/src/data/selectors/submissions.js
index 6ca2219..4a8ccab 100644
--- a/src/data/selectors/submissions.js
+++ b/src/data/selectors/submissions.js
@@ -12,19 +12,19 @@ const simpleSelectors = {
export const selectionIndex = createSelector(
[simpleSelectors.list],
- (list, { learnerId }) => list.selected.indexOf(learnerId),
+ (list, { submissionId }) => list.selected.indexOf(submissionId),
);
export const listData = createSelector(
[simpleSelectors.list],
(list) => _.sortBy(
Object.values(list),
- ['learnerId'],
+ ['submissionId'],
),
);
-export const selectionLearnerId = (state, { index }) => (
- simpleSelectors.selected(state)[index].learnerId
+export const selectionSubmissionId = (state, { index }) => (
+ simpleSelectors.selected(state)[index].submissionId
);
export const currentSelection = createSelector(
@@ -32,25 +32,25 @@ export const currentSelection = createSelector(
(list, activeIndex) => list[activeIndex],
);
-export const selectedLearnerId = createSelector(
+export const selectedSubmissionId = createSelector(
[currentSelection],
- (selection) => selection.learnerId,
+ (selection) => selection.submissionId,
);
-export const prevLearnerId = createSelector(
+export const prevSubmissionId = createSelector(
[simpleSelectors.selected, simpleSelectors.activeIndex],
(list, activeIndex) => {
if (activeIndex > 0) {
- return list[activeIndex - 1].learnerId;
+ return list[activeIndex - 1].submissionId;
}
return null;
},
);
-export const nextLearnerId = createSelector(
+export const nextSubmissionId = createSelector(
[simpleSelectors.selected, simpleSelectors.activeIndex],
(list, activeIndex) => {
if (activeIndex < list.length - 1) {
- return list[activeIndex + 1].learnerId;
+ return list[activeIndex + 1].submissionId;
}
return null;
},
@@ -61,7 +61,7 @@ export default StrictDict({
currentSelection,
selectionIndex,
listData,
- selectedLearnerId,
- nextLearnerId,
- prevLearnerId,
+ selectedSubmissionId,
+ nextSubmissionId,
+ prevSubmissionId,
});
diff --git a/src/data/services/lms/fakeData/submissionFull.js b/src/data/services/lms/fakeData/submissionFull.js
index 1189c9e..6759c56 100644
--- a/src/data/services/lms/fakeData/submissionFull.js
+++ b/src/data/services/lms/fakeData/submissionFull.js
@@ -1,9 +1,10 @@
import submissionList from './submissionList';
-const responseText = `
+const responseText = `
Title
Phasellus tempor eros aliquam ipsum molestie, vitae varius lectus tempus. Morbi iaculis, libero euismod vehicula rutrum, nisi leo volutpat diam, quis commodo ex nunc ut odio. Pellentesque condimentum feugiat erat ac vulputate. Pellentesque porta rutrum sagittis. Curabitur vulputate tempus accumsan. Fusce bibendum gravida metus a scelerisque. Mauris fringilla orci non lobortis commodo. Quisque iaculis, quam a tincidunt vehicula, erat nisi accumsan quam, eu cursus ligula magna id odio. Nulla porttitor, lorem gravida vehicula tristique, sapien metus tristique ex, id tincidunt sapien justo nec sapien. Maecenas luctus, nisl vestibulum scelerisque pharetra, ligula orci vulputate turpis, in ultrices mauris dolor eu enim. Suspendisse quis nibh nec augue semper maximus. Morbi maximus eleifend magna.
-Phasellus porttitor vel magna et auctor. Nulla porttitor convallis aliquam. Donec cursus, ipsum ut egestas bibendum, purus metus dignissim est, ac condimentum leo felis eget diam. In magna mi, tincidunt id sapien id, fermentum vestibulum quam. Quisque et dui sed urna convallis rutrum pellentesque quis sapien. Cras non lectus velit. Praesent semper eros id risus mollis, quis interdum quam imperdiet. Sed nec vulputate tortor, at tristique tortor.`;
+Phasellus porttitor vel magna et auctor. Nulla porttitor convallis aliquam. Donec cursus, ipsum ut egestas bibendum, purus metus dignissim est, ac condimentum leo felis eget diam. In magna mi, tincidunt id sapien id, fermentum vestibulum quam. Quisque et dui sed urna convallis rutrum pellentesque quis sapien. Cras non lectus velit. Praesent semper eros id risus mollis, quis interdum quam imperdiet. Sed nec vulputate tortor, at tristique tortor.
+`;
const rubric = {
name: 'Rubric name',
diff --git a/src/data/services/lms/fakeData/submissionList.js b/src/data/services/lms/fakeData/submissionList.js
index bdc7bd4..7b5e304 100644
--- a/src/data/services/lms/fakeData/submissionList.js
+++ b/src/data/services/lms/fakeData/submissionList.js
@@ -24,7 +24,7 @@ const createSubmission = (grade, status) => {
const submissionId = ids.submissionId(index);
const learnerId = ids.learnerId(index);
submissions[submissionId] = {
- id: submissionId,
+ submissionId,
username: ids.username(index),
learnerId,
dateSubmitted: date0 + (day * index),
diff --git a/src/data/thunkActions/app.js b/src/data/thunkActions/app.js
index 66353ab..68cbd4c 100644
--- a/src/data/thunkActions/app.js
+++ b/src/data/thunkActions/app.js
@@ -15,13 +15,13 @@ export const initialize = () => (dispatch) => (
);
export const prefetchPrev = () => (dispatch, getState) => (
- api.fetchSubmission(selectors.submissions.prevLearnerId(getState())).then((response) => {
+ api.fetchSubmission(selectors.submissions.prevSubmissionId(getState())).then((response) => {
dispatch(actions.submissions.preloadPrev(response.submission));
})
);
export const prefetchNext = () => (dispatch, getState) => (
- api.fetchSubmission(selectors.submissions.nextLearnerId(getState())).then((response) => {
+ api.fetchSubmission(selectors.submissions.nextSubmissionId(getState())).then((response) => {
dispatch(actions.submissions.preloadNext(response.submission));
if (selectors.submissions.activeIndex(getState()) > 0) {
return dispatch(prefetchPrev());
@@ -30,22 +30,27 @@ export const prefetchNext = () => (dispatch, getState) => (
})
);
-export const fetchSubmissionFromSelection = () => (dispatch, getState) => (
- api.fetchSubmission(
- selectors.submissions.selectedLearnerId(getState()),
- ).then((response) => {
- dispatch(actions.submissions.loadSubmission(response.submission));
- const activeIndex = selectors.submissions.activeIndex(getState());
- const selected = selectors.submissions.selected(getState());
- if (activeIndex < selected.length - 1) {
- return dispatch(prefetchNext());
- }
- if (activeIndex > 0) {
- return dispatch(prefetchPrev());
- }
- return true;
- })
-);
+export const fetchSubmissionFromSelection = () => (dispatch, getState) => {
+ console.log({
+ fetchSubmission: selectors.submissions.selectedSubmissionId(getState()),
+ });
+ return (
+ api.fetchSubmission(
+ selectors.submissions.selectedSubmissionId(getState()),
+ ).then((response) => {
+ dispatch(actions.submissions.loadSubmission(response.submission));
+ const activeIndex = selectors.submissions.activeIndex(getState());
+ const selected = selectors.submissions.selected(getState());
+ if (activeIndex < selected.length - 1) {
+ return dispatch(prefetchNext());
+ }
+ if (activeIndex > 0) {
+ return dispatch(prefetchPrev());
+ }
+ return true;
+ })
+ );
+};
export const loadSelectionForReview = (selection) => (dispatch) => {
const submissions = selection.map(row => row.original);
diff --git a/src/index.jsx b/src/index.jsx
index 61fa3be..d12027e 100755
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -15,33 +15,19 @@ import { messages as footerMessages } from '@edx/frontend-component-footer';
import appMessages from './i18n';
import App from './App';
-/*
subscribe(APP_READY, () => {
- ReactDOM.render( , document.getElementById('root'));
-});
-
-initialize({
- messages: [
- appMessages,
- footerMessages,
- ],
- requireAuthenticatedUser: true,
-});
-*/
-
-// ReactDOM.render(
-//
-//
-// ,
-// document.getElementById('root'),
-// );
-
-subscribe(APP_READY, () => {
- ReactDOM.render( , document.getElementById('root'));
+ ReactDOM.render(
+
+
+ ,
+ document.getElementById('root'));
});
subscribe(APP_INIT_ERROR, (error) => {
- ReactDOM.render( , document.getElementById('root'));
+ ReactDOM.render(
+ ,
+ document.getElementById('root'),
+ );
});
initialize({
@@ -50,4 +36,4 @@ initialize({
footerMessages,
],
requireAuthenticatedUser: true,
-});
\ No newline at end of file
+});