diff --git a/src/components/AppContainer.jsx b/src/components/AppContainer.jsx index 73abc8b3..4cbdbedf 100644 --- a/src/components/AppContainer.jsx +++ b/src/components/AppContainer.jsx @@ -6,7 +6,7 @@ import PropTypes from 'prop-types'; */ const AppContainer = ({ Component }) => ( -
+
); diff --git a/src/components/FilePreview/Banners/ErrorBanner.jsx b/src/components/FilePreview/components/FileRenderer/Banners/ErrorBanner.jsx similarity index 100% rename from src/components/FilePreview/Banners/ErrorBanner.jsx rename to src/components/FilePreview/components/FileRenderer/Banners/ErrorBanner.jsx diff --git a/src/components/FilePreview/Banners/ErrorBanner.test.jsx b/src/components/FilePreview/components/FileRenderer/Banners/ErrorBanner.test.jsx similarity index 100% rename from src/components/FilePreview/Banners/ErrorBanner.test.jsx rename to src/components/FilePreview/components/FileRenderer/Banners/ErrorBanner.test.jsx diff --git a/src/components/FilePreview/Banners/LoadingBanner.jsx b/src/components/FilePreview/components/FileRenderer/Banners/LoadingBanner.jsx similarity index 100% rename from src/components/FilePreview/Banners/LoadingBanner.jsx rename to src/components/FilePreview/components/FileRenderer/Banners/LoadingBanner.jsx diff --git a/src/components/FilePreview/Banners/LoadingBanner.test.jsx b/src/components/FilePreview/components/FileRenderer/Banners/LoadingBanner.test.jsx similarity index 100% rename from src/components/FilePreview/Banners/LoadingBanner.test.jsx rename to src/components/FilePreview/components/FileRenderer/Banners/LoadingBanner.test.jsx diff --git a/src/components/FilePreview/Banners/__snapshots__/ErrorBanner.test.jsx.snap b/src/components/FilePreview/components/FileRenderer/Banners/__snapshots__/ErrorBanner.test.jsx.snap similarity index 100% rename from src/components/FilePreview/Banners/__snapshots__/ErrorBanner.test.jsx.snap rename to src/components/FilePreview/components/FileRenderer/Banners/__snapshots__/ErrorBanner.test.jsx.snap diff --git a/src/components/FilePreview/Banners/__snapshots__/LoadingBanner.test.jsx.snap b/src/components/FilePreview/components/FileRenderer/Banners/__snapshots__/LoadingBanner.test.jsx.snap similarity index 100% rename from src/components/FilePreview/Banners/__snapshots__/LoadingBanner.test.jsx.snap rename to src/components/FilePreview/components/FileRenderer/Banners/__snapshots__/LoadingBanner.test.jsx.snap diff --git a/src/components/FilePreview/Banners/index.jsx b/src/components/FilePreview/components/FileRenderer/Banners/index.jsx similarity index 100% rename from src/components/FilePreview/Banners/index.jsx rename to src/components/FilePreview/components/FileRenderer/Banners/index.jsx diff --git a/src/components/FilePreview/BaseRenderers/ImageRenderer.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/ImageRenderer.jsx similarity index 100% rename from src/components/FilePreview/BaseRenderers/ImageRenderer.jsx rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/ImageRenderer.jsx diff --git a/src/components/FilePreview/BaseRenderers/ImageRenderer.test.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/ImageRenderer.test.jsx similarity index 100% rename from src/components/FilePreview/BaseRenderers/ImageRenderer.test.jsx rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/ImageRenderer.test.jsx diff --git a/src/components/FilePreview/BaseRenderers/PDFRenderer.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/PDFRenderer.jsx similarity index 100% rename from src/components/FilePreview/BaseRenderers/PDFRenderer.jsx rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/PDFRenderer.jsx diff --git a/src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/PDFRenderer.test.jsx similarity index 100% rename from src/components/FilePreview/BaseRenderers/PDFRenderer.test.jsx rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/PDFRenderer.test.jsx diff --git a/src/components/FilePreview/BaseRenderers/TXTRenderer.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/TXTRenderer.jsx similarity index 76% rename from src/components/FilePreview/BaseRenderers/TXTRenderer.jsx rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/TXTRenderer.jsx index 340cc48d..33303a2b 100644 --- a/src/components/FilePreview/BaseRenderers/TXTRenderer.jsx +++ b/src/components/FilePreview/components/FileRenderer/BaseRenderers/TXTRenderer.jsx @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { rendererHooks } from './textHooks'; +import { useTextRendererData } from './textHooks'; const TXTRenderer = ({ url, onError, onSuccess }) => { - const { content } = rendererHooks({ url, onError, onSuccess }); + const { content } = useTextRendererData({ url, onError, onSuccess }); return (
       {content}
diff --git a/src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/TXTRenderer.test.jsx
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/TXTRenderer.test.jsx
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/TXTRenderer.test.jsx
diff --git a/src/components/FilePreview/BaseRenderers/__snapshots__/ImageRenderer.test.jsx.snap b/src/components/FilePreview/components/FileRenderer/BaseRenderers/__snapshots__/ImageRenderer.test.jsx.snap
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/__snapshots__/ImageRenderer.test.jsx.snap
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/__snapshots__/ImageRenderer.test.jsx.snap
diff --git a/src/components/FilePreview/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap b/src/components/FilePreview/components/FileRenderer/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/__snapshots__/PDFRenderer.test.jsx.snap
diff --git a/src/components/FilePreview/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap b/src/components/FilePreview/components/FileRenderer/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/__snapshots__/TXTRenderer.test.jsx.snap
diff --git a/src/components/FilePreview/BaseRenderers/index.jsx b/src/components/FilePreview/components/FileRenderer/BaseRenderers/index.jsx
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/index.jsx
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/index.jsx
diff --git a/src/components/FilePreview/BaseRenderers/pdfHooks.js b/src/components/FilePreview/components/FileRenderer/BaseRenderers/pdfHooks.js
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/pdfHooks.js
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/pdfHooks.js
diff --git a/src/components/FilePreview/BaseRenderers/pdfHooks.test.js b/src/components/FilePreview/components/FileRenderer/BaseRenderers/pdfHooks.test.js
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/pdfHooks.test.js
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/pdfHooks.test.js
diff --git a/src/components/FilePreview/BaseRenderers/textHooks.js b/src/components/FilePreview/components/FileRenderer/BaseRenderers/textHooks.js
similarity index 82%
rename from src/components/FilePreview/BaseRenderers/textHooks.js
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/textHooks.js
index e755c504..e2edd60d 100644
--- a/src/components/FilePreview/BaseRenderers/textHooks.js
+++ b/src/components/FilePreview/components/FileRenderer/BaseRenderers/textHooks.js
@@ -1,6 +1,6 @@
 import { useEffect } from 'react';
 
-import { get } from 'axios';
+import axios from 'axios';
 import { useKeyedState, StrictDict } from '@edx/react-unit-test-utils';
 
 export const stateKeys = StrictDict({
@@ -12,14 +12,14 @@ export const fetchFile = async ({
   url,
   onError,
   onSuccess,
-}) => get(url)
+}) => axios.get(url)
   .then(({ data }) => {
     onSuccess();
     setContent(data);
   })
   .catch((e) => onError(e.response.status));
 
-export const rendererHooks = ({ url, onError, onSuccess }) => {
+export const useTextRendererData = ({ url, onError, onSuccess }) => {
   const [content, setContent] = useKeyedState(stateKeys.content, '');
   useEffect(() => {
     fetchFile({
@@ -33,6 +33,6 @@ export const rendererHooks = ({ url, onError, onSuccess }) => {
 };
 
 export default {
-  rendererHooks,
+  useTextRendererData,
   fetchFile,
 };
diff --git a/src/components/FilePreview/BaseRenderers/textHooks.test.js b/src/components/FilePreview/components/FileRenderer/BaseRenderers/textHooks.test.js
similarity index 100%
rename from src/components/FilePreview/BaseRenderers/textHooks.test.js
rename to src/components/FilePreview/components/FileRenderer/BaseRenderers/textHooks.test.js
diff --git a/src/components/FilePreview/FileCard.scss b/src/components/FilePreview/components/FileRenderer/FileCard/FileCard.scss
similarity index 100%
rename from src/components/FilePreview/FileCard.scss
rename to src/components/FilePreview/components/FileRenderer/FileCard/FileCard.scss
diff --git a/src/components/FilePreview/FileCard.jsx b/src/components/FilePreview/components/FileRenderer/FileCard/index.jsx
similarity index 54%
rename from src/components/FilePreview/FileCard.jsx
rename to src/components/FilePreview/components/FileRenderer/FileCard/index.jsx
index 905aea1f..9b622883 100644
--- a/src/components/FilePreview/FileCard.jsx
+++ b/src/components/FilePreview/components/FileRenderer/FileCard/index.jsx
@@ -8,12 +8,12 @@ import './FileCard.scss';
 /**
  * 
  */
-export const FileCard = ({ file, children }) => (
-  
+const FileCard = ({ file, children }) => (
+  
     {file.name}}
+      title={

{file.fileName}

} >
{children} @@ -21,14 +21,8 @@ export const FileCard = ({ file, children }) => ( ); -FileCard.defaultProps = { -}; FileCard.propTypes = { - file: PropTypes.shape({ - name: PropTypes.string.isRequired, - downloadUrl: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - }).isRequired, + file: PropTypes.shape({ fileName: PropTypes.string.isRequired }).isRequired, children: PropTypes.node.isRequired, }; diff --git a/src/components/FilePreview/FileCard.test.jsx b/src/components/FilePreview/components/FileRenderer/FileCard/index.test.jsx similarity index 96% rename from src/components/FilePreview/FileCard.test.jsx rename to src/components/FilePreview/components/FileRenderer/FileCard/index.test.jsx index e5831ea5..b35868f8 100644 --- a/src/components/FilePreview/FileCard.test.jsx +++ b/src/components/FilePreview/components/FileRenderer/FileCard/index.test.jsx @@ -3,7 +3,7 @@ import { shallow } from '@edx/react-unit-test-utils'; import { Collapsible } from '@edx/paragon'; -import FileCard from './FileCard'; +import FileCard from './'; describe('File Preview Card component', () => { const props = { diff --git a/src/components/FilePreview/FileRenderer.test.jsx b/src/components/FilePreview/components/FileRenderer/FileRenderer.test.jsx similarity index 100% rename from src/components/FilePreview/FileRenderer.test.jsx rename to src/components/FilePreview/components/FileRenderer/FileRenderer.test.jsx diff --git a/src/components/FilePreview/components/FileRenderer/hooks.js b/src/components/FilePreview/components/FileRenderer/hooks.js new file mode 100644 index 00000000..e6142e6c --- /dev/null +++ b/src/components/FilePreview/components/FileRenderer/hooks.js @@ -0,0 +1,58 @@ +import { useKeyedState, StrictDict } from '@edx/react-unit-test-utils'; + +import { errorStatuses, errorMessages, renderers } from '../constants'; +import { getFileType } from '../utils'; +import messages from './messages'; + +export const stateKeys = StrictDict({ + errorStatus: 'errorStatus', + isLoading: 'isLoading', +}); + +/** + * component hooks + */ +export const useRenderData = ({ + file, + formatMessage, +}) => { + const [errorStatus, setErrorStatus] = useKeyedState(stateKeys.errorStatus, null); + const [isLoading, setIsLoading] = useKeyedState(stateKeys.isLoading, true); + + const setState = (newState) => { + setErrorStatus(newState.errorStatus); + setIsLoading(newState.isLoading); + }; + + const stopLoading = (status = null) => setState({ isLoading: false, errorStatus: status }); + + const errorMessage = ( + errorMessages[errorStatus] || errorMessages[errorStatuses.serverError] + ); + const errorAction = { + id: 'retry', + onClick: () => setState({ errorStatus: null, isLoading: true }), + message: messages.retryButton, + }; + const error = { + headerMessage: errorMessage, + children: formatMessage(errorMessage), + actions: [errorAction], + }; + + const Renderer = renderers[getFileType(file.fileName)]; + const rendererProps = { + fileName: file.fileName, + url: file.fileUrl, + onError: stopLoading, + onSuccess: () => stopLoading(), + }; + + return { + errorStatus, + isLoading, + error, + Renderer, + rendererProps, + }; +}; diff --git a/src/components/FilePreview/FileRenderer.jsx b/src/components/FilePreview/components/FileRenderer/index.jsx similarity index 75% rename from src/components/FilePreview/FileRenderer.jsx rename to src/components/FilePreview/components/FileRenderer/index.jsx index f33b0783..679e664e 100644 --- a/src/components/FilePreview/FileRenderer.jsx +++ b/src/components/FilePreview/components/FileRenderer/index.jsx @@ -5,14 +5,12 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import FileCard from './FileCard'; import { ErrorBanner, LoadingBanner } from './Banners'; -import { renderHooks } from './hooks'; +import { useRenderData } from './hooks'; /** * */ -export const FileRenderer = ({ - file, -}) => { +export const FileRenderer = ({ file }) => { const { formatMessage } = useIntl(); const { Renderer, @@ -20,10 +18,10 @@ export const FileRenderer = ({ errorStatus, error, rendererProps, - } = renderHooks({ file, formatMessage }); + } = useRenderData({ file, formatMessage }); return ( - + {isLoading && } {errorStatus ? ( @@ -37,8 +35,8 @@ export const FileRenderer = ({ FileRenderer.defaultProps = {}; FileRenderer.propTypes = { file: PropTypes.shape({ - name: PropTypes.string, - downloadUrl: PropTypes.string, + fileName: PropTypes.string, + fileUrl: PropTypes.string, }).isRequired, // injected // intl: intlShape.isRequired, diff --git a/src/components/FilePreview/components/FileRenderer/messages.js b/src/components/FilePreview/components/FileRenderer/messages.js new file mode 100644 index 00000000..904c99bb --- /dev/null +++ b/src/components/FilePreview/components/FileRenderer/messages.js @@ -0,0 +1,16 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + fileInfo: { + id: 'ora-grading.InfoPopover.fileInfo', + defaultMessage: 'File info', + description: 'Popover trigger button text for file preview card', + }, + retryButton: { + id: 'ora-grading.ResponseDisplay.FileRenderer.retryButton', + defaultMessage: 'Retry', + description: 'Retry button for error in file renderer', + }, +}); + +export default messages; diff --git a/src/components/FilePreview/__snapshots__/FileCard.test.jsx.snap b/src/components/FilePreview/components/__snapshots__/FileCard.test.jsx.snap similarity index 100% rename from src/components/FilePreview/__snapshots__/FileCard.test.jsx.snap rename to src/components/FilePreview/components/__snapshots__/FileCard.test.jsx.snap diff --git a/src/components/FilePreview/__snapshots__/FileRenderer.test.jsx.snap b/src/components/FilePreview/components/__snapshots__/FileRenderer.test.jsx.snap similarity index 100% rename from src/components/FilePreview/__snapshots__/FileRenderer.test.jsx.snap rename to src/components/FilePreview/components/__snapshots__/FileRenderer.test.jsx.snap diff --git a/src/components/FilePreview/components/constants.js b/src/components/FilePreview/components/constants.js new file mode 100644 index 00000000..7e9a7749 --- /dev/null +++ b/src/components/FilePreview/components/constants.js @@ -0,0 +1,56 @@ +import { StrictDict } from '@edx/react-unit-test-utils'; + +import { + PDFRenderer, + ImageRenderer, + TXTRenderer, +} from './FileRenderer/BaseRenderers'; + +import messages from './messages'; + +export const errorStatuses = StrictDict({ + badRequest: 400, + unauthorized: 401, + forbidden: 403, + notFound: 404, + conflict: 409, + serverError: 500, +}); + +export const fileTypes = StrictDict({ + pdf: 'pdf', + jpg: 'jpg', + jpeg: 'jpeg', + png: 'png', + bmp: 'bmp', + txt: 'txt', + gif: 'gif', + jfif: 'jfif', + pjpeg: 'pjpeg', + pjp: 'pjp', + svg: 'svg', +}); + +export const errorMessages = { + [errorStatuses.notFound]: messages.fileNotFoundError, + [errorStatuses.serverError]: messages.unknownError, +}; + +/** + * Config data + */ +export const renderers = { + [fileTypes.pdf]: PDFRenderer, + [fileTypes.jpg]: ImageRenderer, + [fileTypes.jpeg]: ImageRenderer, + [fileTypes.bmp]: ImageRenderer, + [fileTypes.png]: ImageRenderer, + [fileTypes.txt]: TXTRenderer, + [fileTypes.gif]: ImageRenderer, + [fileTypes.jfif]: ImageRenderer, + [fileTypes.pjpeg]: ImageRenderer, + [fileTypes.pjp]: ImageRenderer, + [fileTypes.svg]: ImageRenderer, +}; + +export const supportedTypes = Object.keys(renderers); diff --git a/src/components/FilePreview/components/index.jsx b/src/components/FilePreview/components/index.jsx new file mode 100644 index 00000000..e1efe627 --- /dev/null +++ b/src/components/FilePreview/components/index.jsx @@ -0,0 +1,2 @@ +export { default as FileRenderer } from './FileRenderer'; +export { isSupported } from './utils'; diff --git a/src/components/FilePreview/messages.js b/src/components/FilePreview/components/messages.js similarity index 58% rename from src/components/FilePreview/messages.js rename to src/components/FilePreview/components/messages.js index 573a546c..2576b854 100644 --- a/src/components/FilePreview/messages.js +++ b/src/components/FilePreview/components/messages.js @@ -1,16 +1,6 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ - fileInfo: { - id: 'ora-grading.InfoPopover.fileInfo', - defaultMessage: 'File info', - description: 'Popover trigger button text for file preview card', - }, - retryButton: { - id: 'ora-grading.ResponseDisplay.FileRenderer.retryButton', - defaultMessage: 'Retry', - description: 'Retry button for error in file renderer', - }, fileNotFoundError: { id: 'ora-grading.ResponseDisplay.FileRenderer.fileNotFound', defaultMessage: 'File not found', diff --git a/src/components/FilePreview/components/utils.js b/src/components/FilePreview/components/utils.js new file mode 100644 index 00000000..92d51b00 --- /dev/null +++ b/src/components/FilePreview/components/utils.js @@ -0,0 +1,15 @@ +import { + PDFRenderer, + ImageRenderer, + TXTRenderer, +} from './FileRenderer/BaseRenderers'; + +import { supportedTypes } from './constants'; + +/** + * Util methods and transforms + */ +export const getFileType = (fileName) => fileName.split('.').pop()?.toLowerCase(); +export const isSupported = ({ fileName }) => supportedTypes.includes( + getFileType(fileName), +); diff --git a/src/components/FilePreview/hooks.js b/src/components/FilePreview/hooks.js deleted file mode 100644 index bf148103..00000000 --- a/src/components/FilePreview/hooks.js +++ /dev/null @@ -1,117 +0,0 @@ -import { useKeyedState, StrictDict } from '@edx/react-unit-test-utils'; - -import { - PDFRenderer, - ImageRenderer, - TXTRenderer, -} from 'components/FilePreview/BaseRenderers'; - -import messages from './messages'; - -export const ErrorStatuses = StrictDict({ - badRequest: 400, - unauthorized: 401, - forbidden: 403, - notFound: 404, - conflict: 409, - serverError: 500, -}); - -export const FileTypes = StrictDict({ - pdf: 'pdf', - jpg: 'jpg', - jpeg: 'jpeg', - png: 'png', - bmp: 'bmp', - txt: 'txt', - gif: 'gif', - jfif: 'jfif', - pjpeg: 'pjpeg', - pjp: 'pjp', - svg: 'svg', -}); - -/** - * Config data - */ -export const RENDERERS = { - [FileTypes.pdf]: PDFRenderer, - [FileTypes.jpg]: ImageRenderer, - [FileTypes.jpeg]: ImageRenderer, - [FileTypes.bmp]: ImageRenderer, - [FileTypes.png]: ImageRenderer, - [FileTypes.txt]: TXTRenderer, - [FileTypes.gif]: ImageRenderer, - [FileTypes.jfif]: ImageRenderer, - [FileTypes.pjpeg]: ImageRenderer, - [FileTypes.pjp]: ImageRenderer, - [FileTypes.svg]: ImageRenderer, -}; - -export const SUPPORTED_TYPES = Object.keys(RENDERERS); - -export const ERROR_STATUSES = { - [ErrorStatuses.notFound]: messages.fileNotFoundError, - [ErrorStatuses.serverError]: messages.unknownError, -}; - -/** - * Util methods and transforms - */ -export const getFileType = (fileName) => fileName.split('.').pop()?.toLowerCase(); -export const isSupported = (file) => SUPPORTED_TYPES.includes( - getFileType(file.name), -); - -export const stateKeys = StrictDict({ - errorStatus: 'errorStatus', - isLoading: 'isLoading', -}); - -/** - * component hooks - */ -export const renderHooks = ({ - file, - formatMessage, -}) => { - const [errorStatus, setErrorStatus] = useKeyedState(stateKeys.errorStatus, null); - const [isLoading, setIsLoading] = useKeyedState(stateKeys.isLoading, true); - - const setState = (newState) => { - setErrorStatus(newState.errorStatus); - setIsLoading(newState.isLoading); - }; - - const stopLoading = (status = null) => setState({ isLoading: false, errorStatus: status }); - - const errorMessage = ( - ERROR_STATUSES[errorStatus] || ERROR_STATUSES[ErrorStatuses.serverError] - ); - const errorAction = { - id: 'retry', - onClick: () => setState({ errorStatus: null, isLoading: true }), - message: messages.retryButton, - }; - const error = { - headerMessage: errorMessage, - children: formatMessage(errorMessage), - actions: [errorAction], - }; - - const Renderer = RENDERERS[getFileType(file.name)]; - const rendererProps = { - fileName: file.name, - url: file.downloadUrl, - onError: stopLoading, - onSuccess: () => stopLoading(), - }; - - return { - errorStatus, - isLoading, - error, - Renderer, - rendererProps, - }; -}; diff --git a/src/components/FilePreview/hooks.test.js b/src/components/FilePreview/hooks.test.js deleted file mode 100644 index f5974061..00000000 --- a/src/components/FilePreview/hooks.test.js +++ /dev/null @@ -1,101 +0,0 @@ -import { mockUseKeyedState, formatMessage } from '@edx/react-unit-test-utils'; - -import { - ErrorStatuses, - RENDERERS, - SUPPORTED_TYPES, - ERROR_STATUSES, - getFileType, - isSupported, - stateKeys, -} from './hooks'; - -jest.mock('./hooks', () => ({ - ...jest.requireActual('./hooks'), - renderHooks: jest.fn(), -})); - -const state = mockUseKeyedState(stateKeys); - -describe('FilePreview hooks', () => { - const props = { - file: { - name: 'test-file-name.txt', - downloadUrl: 'my-test-download-url.jpg', - }, - formatMessage, - }; - - const actualHooks = jest.requireActual('./hooks'); - - beforeEach(() => state.mock()); - afterEach(() => state.resetVals()); - - test('state initialization', () => { - actualHooks.renderHooks(props); - state.expectInitializedWith(stateKeys.errorStatus, null); - state.expectInitializedWith(stateKeys.isLoading, true); - }); - - test('getFileType returns file extension if available, in lowercase', () => { - expect(getFileType('thing.TXT')).toEqual('txt'); - expect(getFileType(props.file.name)).toEqual('txt'); - }); - - test('isSupported returns true iff the filetype is included in SUPPORTED_TYPES', () => { - SUPPORTED_TYPES.forEach((type) => { - expect(isSupported({ name: `thing.${type}` })).toEqual(true); - }); - expect(isSupported({ name: 'thing' })).toEqual(false); - }); - - describe('renderHooks', () => { - test('errorStatus and isLoading tied to state, initialized to null and true', () => { - const hook = actualHooks.renderHooks(props); - expect(hook.errorStatus).toEqual(state.values.errorStatus); - expect(hook.errorStatus).toEqual(null); - expect(hook.isLoading).toEqual(state.values.isLoading); - expect(hook.isLoading).toEqual(true); - }); - - test('error', () => { - const hook = actualHooks.renderHooks(props); - expect(hook.error.headerMessage).toEqual( - ERROR_STATUSES[ErrorStatuses.serverError], - ); - expect(hook.error.children).toEqual( - props.formatMessage(ERROR_STATUSES[ErrorStatuses.serverError]), - ); - hook.rendererProps.onError(ErrorStatuses.notFound); - expect(state.setState.errorStatus).toHaveBeenCalledWith( - ErrorStatuses.notFound, - ); - }); - - test('Renderer', () => { - SUPPORTED_TYPES.forEach((type) => { - const hook = actualHooks.renderHooks({ - ...props, - file: { ...props.file, name: `thing.${type}` }, - }); - expect(hook.Renderer).toEqual(RENDERERS[type]); - }); - }); - - test('rendererProps', () => { - const hook = actualHooks.renderHooks(props); - expect(hook.rendererProps.fileName).toEqual(props.file.name); - expect(hook.rendererProps.url).toEqual(props.file.downloadUrl); - - hook.rendererProps.onSuccess(); - expect(state.setState.isLoading).toHaveBeenCalledWith(false); - expect(state.setState.errorStatus).toHaveBeenCalledWith(null); - - hook.rendererProps.onError(ErrorStatuses.notFound); - expect(state.setState.isLoading).toHaveBeenCalledWith(false); - expect(state.setState.errorStatus).toHaveBeenCalledWith( - ErrorStatuses.notFound, - ); - }); - }); -}); diff --git a/src/components/FilePreview/index.jsx b/src/components/FilePreview/index.jsx index 0addafe2..38a32520 100644 --- a/src/components/FilePreview/index.jsx +++ b/src/components/FilePreview/index.jsx @@ -1,2 +1,17 @@ -export { default as FileRenderer } from './FileRenderer'; -export { isSupported } from './hooks'; +import React from 'react'; + +import { useResponseData } from 'data/services/lms/hooks/selectors'; +import { FileRenderer, isSupported } from './components'; + +const FilePreview = () => { + const { uploadedFiles } = useResponseData(); + return ( +
+ {uploadedFiles.filter(isSupported).map((file) => ( + + ))} +
+ ); +}; + +export default FilePreview; diff --git a/src/components/FileUpload/ActionCell.jsx b/src/components/FileUpload/ActionCell.jsx index 9f97f288..76313a35 100644 --- a/src/components/FileUpload/ActionCell.jsx +++ b/src/components/FileUpload/ActionCell.jsx @@ -19,13 +19,15 @@ const ActionCell = ({ }, [onDeletedFile, row.index]); return ( <> - + {!disabled && ( + + )} { @@ -54,7 +54,7 @@ export const ProgressBar = () => { return ( - +
{stepEl(stepNames.submission)} {stepOrder.map(stepEl)} diff --git a/src/data/services/lms/constants.js b/src/data/services/lms/constants.js index e3bb2a45..0a2865ce 100644 --- a/src/data/services/lms/constants.js +++ b/src/data/services/lms/constants.js @@ -37,6 +37,7 @@ export const stepNames = StrictDict({ peer: 'peer', self: 'self', studentTraining: 'studentTraining', + staff: 'staff', myGrades: 'myGrades', }); diff --git a/src/data/services/lms/fakeData/constants.js b/src/data/services/lms/fakeData/constants.js new file mode 100644 index 00000000..0435412c --- /dev/null +++ b/src/data/services/lms/fakeData/constants.js @@ -0,0 +1,70 @@ +import { StrictDict } from '@edx/react-unit-test-utils'; +import { stepNames } from 'data/services/lms/constants'; + +export const viewKeys = StrictDict({ + xblock: 'xblock', + submission: 'submission', + studentTraining: 'student_training', + self: 'self_assessment', + peer: 'peer_assessment', + myGrades: 'my_grades', +}); + +export const progressKeys = StrictDict({ + cancelledDuringSubmission: 'cancelled_during_submission', + cancelledDuringStudentTraining: 'cancelled_during_student_training', + cancelledDuringSelf: 'cancelled_during_self', + cancelledDuringPeer: 'cancelled_during_peer', + cancelledDuringStaff: 'cancelled_during_staff', + submissionEarly: 'submission_early', + submissionClosed: 'submission_closed', + submissionTeamAlreadySubmitted: 'submission_team_already_submitted', + submissionNeedTeam: 'submission_need_team', + submissionUnsaved: 'submission_unsaved', + submissionSaved: 'submission_saved', + submissionFinished: 'submission_finished', + studentTraining: 'student_training', + studentTrainingValidation: 'student_training_validation', + studentTrainingPartial: 'student_training_partial', + studentTrainingFinished: 'student_training_finished', + selfAssessment: 'self_assessment', + selfAssessmentLate: 'self_assessment_late', + selfAssessmentFinished: 'self_assessment_finished', + peerAssessment: 'peer_assessment', + peerAssessmentEarly: 'peer_assessment_early', + peerAssessmentLate: 'peer_assessment_late', + peerAssessmentWaiting: 'peer_assessment_waiting', + peerAssessmentFinished: 'peer_assessment_finished', + staffAfterSubmission: 'staff_after_submission', + staffAfterSelf: 'staff_after_self', + staffAfterPeer: 'staff_after_peer', + graded: 'graded', + gradedSubmittedOnPreviousTeam: 'graded_submitted_on_previous_team', +}); + +export const teamStates = [ + progressKeys.gradedSubmittedOnPreviousTeam, + progressKeys.submissionTeamAlreadySubmitted, + progressKeys.submissionNeedTeam, +]; + +export const closedStates = { + open: { isClosed: false }, + notAvailable: { isClosed: true, closedReason: 'notAvailable' }, + closed: { isClosed: true, closedReason: 'pastDue' }, +}; + +export const stepConfigs = StrictDict({ + all: [stepNames.studentTraining, stepNames.self, stepNames.peer, stepNames.staff], + self: [stepNames.self], + peer: [stepNames.peer], + staff: [stepNames.staff], + trainingAndSelf: [stepNames.studentTraining, stepNames.self], + trainingAndPeer: [stepNames.studentTraining, stepNames.peer], + selfAndStaff: [stepNames.self, stepNames.staff], +}); + +export const stateStepConfigs = { + [progressKeys.staffAfterSubmission]: stepConfigs.staff, + [progressKeys.staffAfterSelf]: stepConfigs.selfAndStaff, +}; diff --git a/src/data/services/lms/fakeData/dataStates.js b/src/data/services/lms/fakeData/dataStates.js index 4ecb21a8..1a474c65 100644 --- a/src/data/services/lms/fakeData/dataStates.js +++ b/src/data/services/lms/fakeData/dataStates.js @@ -1,83 +1,38 @@ import { StrictDict } from '@edx/react-unit-test-utils'; -import { routeSteps } from 'data/services/lms/constants'; -import pageData from './pageData'; - -export const viewKeys = StrictDict({ - xblock: 'xblock', - submission: 'submission', - studentTraining: 'student_training', - self: 'self_assessment', - peer: 'peer_assessment', - myGrades: 'my_grades', -}); -export const progressKeys = StrictDict({ - unsaved: 'unsaved', - saved: 'saved', - studentTraining: 'studentTraining', - self: 'self', - peer: 'peer', - peerWaiting: 'peerWaiting', - staff: 'staff', - graded: 'graded', +import pageData from './pageData'; +import { + progressKeys, + stepConfigs, + teamStates, + viewKeys, + stateStepConfigs, +} from './constants'; +import { routeSteps } from '../constants'; + +export const defaultViewProgressKeys = StrictDict({ + [viewKeys.xblock]: progressKeys.submissionUnsaved, + [viewKeys.submission]: progressKeys.submissionSaved, + [viewKeys.studentTraining]: progressKeys.studentTraining, + [viewKeys.self]: progressKeys.selfAssessment, + [viewKeys.peer]: progressKeys.peerAssessment, + [viewKeys.myGrades]: progressKeys.graded, }); -export const progressStates = { - unsaved: pageData.progressStates.submission, - saved: pageData.progressStates.submission, - studentTraining: pageData.progressStates.training({ numCompleted: 0 }), - self: pageData.progressStates.self, - peer: pageData.progressStates.peer(), - peerWaiting: pageData.progressStates.peer({ numCompleted: 1, isWaiting: true }), - staff: pageData.progressStates.staff, - graded: pageData.progressStates.graded, -}; - -export const submissionStatesByView = { - [viewKeys.xblock]: null, - [viewKeys.submission]: pageData.submissionStates.individualSubmission, - [viewKeys.self]: pageData.submissionStates.individualSubmission, - [viewKeys.studentTraining]: pageData.submissionStates.individualSubmission, - [viewKeys.peer]: pageData.submissionStates.individualSubmission, - [viewKeys.myGrades]: pageData.submissionStates.individualSubmission, -}; - -export const staffAssessmentGroup = { - effectiveAssessmentType: 'staff', - ...pageData.assessmentStates.graded, -}; - -export const assessmentStatesByView = { - [viewKeys.xblock]: null, - [viewKeys.submission]: null, - [viewKeys.self]: null, - [viewKeys.studentTraining]: null, - [viewKeys.peer]: null, - [viewKeys.myGrades]: { - effectiveAssessmentType: 'staff', - ...pageData.assessmentStates.graded, - }, -}; - export const loadState = (opts) => { - const { view } = opts; - let progressKey = opts.progressKey || routeSteps[view]; - if (progressKey === routeSteps.submission) { - progressKey = progressKeys.unsaved; - } + const { + view, + } = opts; + const viewStep = routeSteps[view]; + const progressKey = opts.progressKey || defaultViewProgressKeys[view]; + const isTeam = teamStates.includes(progressKey) || (opts.isTeam === true); + const stepConfig = stateStepConfigs[progressKey] || stepConfigs.all; const state = { - progress: progressStates[progressKey], - submission: submissionStatesByView[view], - assessments: progressKey === progressKeys.graded ? staffAssessmentGroup : null, + progress: pageData.getProgressState({ progressKey, stepConfig, viewStep }), + response: pageData.getResponseState({ progressKey, isTeam }), + assessments: pageData.getAssessmentState({ progressKey, stepConfig }), }; - if (view === viewKeys.submission && progressKey === progressKeys.unsaved) { - state.submission = pageData.submissionStates.emptySubmission; - } - console.log({ - progressKey, - view, - state, - }); + console.log({ opts, progressKey, state, isTeam }); return state; }; diff --git a/src/data/services/lms/fakeData/pageData/assessments.js b/src/data/services/lms/fakeData/pageData/assessments.js index 2a897000..abef9a96 100644 --- a/src/data/services/lms/fakeData/pageData/assessments.js +++ b/src/data/services/lms/fakeData/pageData/assessments.js @@ -1,4 +1,5 @@ -/* eslint-disable camelcase */ +import { stepNames } from 'data/services/lms/constants'; +import { progressKeys } from '../constants'; export const createAssessmentState = ({ options_selected = [], @@ -34,13 +35,19 @@ const gradedState = createAssessmentState({ overall_feedback: 'nice job', }); -export default { - graded: { - staff: { +export const getAssessmentState = ({ progressKey, stepConfig }) => { + if (![progressKeys.graded, progressKeys.gradedSubmittedOnPreviousTeam].includes(progressKey)) { + return null; + } + const out = {}; + if (stepConfig.includes(stepNames.staff)) { + out.staff = { stepScore: { earned: 10, possible: 10 }, assessment: gradedState, - }, - peer: { + }; + } + if (stepConfig.includes(stepNames.peer)) { + out.peer = { stepScore: { earned: 10, possible: 10 }, assessment: [ gradedState, @@ -49,18 +56,17 @@ export default { gradedState, gradedState, ], - }, - peerUnweighted: { + }; + out.peerUnweighted = { stepScore: null, assessment: [ gradedState, gradedState, gradedState, ], - }, - self: { - stepScore: { earned: 10, possible: 10 }, - assessment: gradedState, - }, - }, + }; + } + return out; }; + +export default { getAssessmentState }; diff --git a/src/data/services/lms/fakeData/pageData/index.jsx b/src/data/services/lms/fakeData/pageData/index.jsx index 354639e0..6dd01b7c 100644 --- a/src/data/services/lms/fakeData/pageData/index.jsx +++ b/src/data/services/lms/fakeData/pageData/index.jsx @@ -1,9 +1,9 @@ -import progressStates from './progress'; -import assessmentStates from './assessments'; -import submissionStates from './submission'; +import { getAssessmentState } from './assessments'; +import { getProgressState } from './progress'; +import { getResponseState } from './response'; export default { - progressStates, - assessmentStates, - submissionStates, + getAssessmentState, + getProgressState, + getResponseState, }; diff --git a/src/data/services/lms/fakeData/pageData/progress.js b/src/data/services/lms/fakeData/pageData/progress.js index 6a2a2354..28b4c76e 100644 --- a/src/data/services/lms/fakeData/pageData/progress.js +++ b/src/data/services/lms/fakeData/pageData/progress.js @@ -1,36 +1,93 @@ +import { StrictDict } from '@edx/react-unit-test-utils'; + +import { stepNames } from 'data/services/lms/constants'; import { assessmentSteps } from '../oraConfig'; +import { closedStates, progressKeys } from '../constants'; /* eslint-disable camelcase */ -// Progress -export const createProgressData = ({ - active_step_name = null, - has_received_final_grade = false, - step_info = {}, -}) => ({ - active_step_name, - has_received_final_grade, - step_info, +export const createTeamInfo = ({ + team_name = 'Team name', + team_usernames = ['user1', 'user2'], + previous_team_name = null, + has_submitted = false, +} = {}) => ({ + team_name, + team_usernames, + previous_team_name, + has_submitted, +}); + +export const createSubmissionStatus = ({ + has_submitted = false, + has_cancelled = false, + closedState = closedStates.open, + team_info = null, +} = {}) => ({ + ...closedState, + has_submitted, + has_cancelled, + team_info, }); -export const closedStates = { - open: { isClosed: false }, - notAvailable: { isClosed: true, closedReason: 'notAvailable' }, - closed: { isClosed: true, closedReason: 'pastDue' }, +const subStatuses = { + cancelled: createSubmissionStatus({ has_cancelled: true }), + cancelledAfterSubmission: createSubmissionStatus({ has_submitted: true, has_cancelled: true }), + closed: createSubmissionStatus({ closedState: closedStates.closed }), + notAvailable: createSubmissionStatus({ closedState: closedStates.notAvailable }), + unsubmitted: createSubmissionStatus(), + submitted: createSubmissionStatus({ has_submitted: true }), }; -export const genPeerStepInfo = ({ - closedState, +const teamStatuses = { + unsubmitted: createTeamInfo(), + submitted: createTeamInfo({ has_submitted: true }), + previousTeam: createTeamInfo({ previous_team_name: 'Previous Team Name' }), + needTeam: createTeamInfo({ team_name: null, team_usernames: null }), +}; +const teamSubStatuses = { + cancelled: { ...subStatuses.cancelled, team_info: teamStatuses.unsubmitted }, + cancelledAfterSubmission: { + ...subStatuses.cancelledAfterSubmission, + team_info: teamStatuses.unsubmitted, + }, + closed: { ...subStatuses.closed, team_info: teamStatuses.unsubmitted }, + notAvailable: { ...subStatuses.notAvailable, team_info: teamStatuses.unsubmitted }, + unsubmitted: { ...subStatuses.unsubmitted, team_info: teamStatuses.unsubmitted }, + submitted: { ...subStatuses.submitted, team_info: teamStatuses.submitted }, + teamAlreadySubmitted: { ...subStatuses.unsubmitted, team_info: teamStatuses.submitted }, + submittedOnPreviousTeam: { ...subStatuses.submitted, team_info: teamStatuses.previousTeam }, +}; + +export const createPeerStepInfo = ({ + closedState = closedStates.open, numCompleted = 0, isWaiting = false, numReceived = 0, -}) => ({ +} = {}) => ({ ...closedState, number_of_assessments_completed: numCompleted, is_waiting_for_submissions: isWaiting, number_of_received_assessments: numReceived, }); -export const genTrainingStepInfo = ({ closedState, numCompleted }) => ({ +const peerStatuses = { + unsubmitted: createPeerStepInfo(), + closed: createPeerStepInfo({ closedState: closedStates.closed }), + notAvilable: createPeerStepInfo({ closedState: closedStates.notAvailable }), + waiting: createPeerStepInfo({ is_waiting_for_submission: true }), + partial: createPeerStepInfo({ number_of_assessments_completed: 1 }), + finished: createPeerStepInfo({ + closedState: closedStates.open, + numCompleted: assessmentSteps.settings.peer.data.min_number_to_grade, + isWaiting: false, + numReceived: assessmentSteps.settings.peer.data.min_number_to_be_graded_by, + }), +}; + +export const createTrainingStepInfo = ({ + closedState = closedStates.open, + numCompleted = 0, +} = {}) => ({ ...closedState, number_of_assessments_completed: numCompleted, expected_rubric_selections: [ @@ -41,76 +98,154 @@ export const genTrainingStepInfo = ({ closedState, numCompleted }) => ({ ], }); -const finishedTrainingStepInfo = genTrainingStepInfo({ - closedState: closedStates.open, - numCompleted: assessmentSteps.settings.training.data.examples.length, -}); +const trainingStatuses = { + unsubmitted: createTrainingStepInfo(), + partial: createTrainingStepInfo({ numCompleted: 1}), + finished: createTrainingStepInfo({ + closedState: closedStates.open, + numCompleted: assessmentSteps.settings.training.data.examples.length, + }), +}; -const finishedPeerStepInfo = genPeerStepInfo({ - closedState: closedStates.open, - numCompleted: assessmentSteps.settings.peer.data.min_number_to_grade, - isWaiting: false, - numReceived: assessmentSteps.settings.peer.data.min_number_to_be_graded_by, +const finishedStates = StrictDict({ + [stepNames.submission]: subStatuses.finished, + [stepNames.studentTraining]: trainingStatuses.finished, + [stepNames.self]: closedStates.open, + [stepNames.peer]: peerStatuses.finished, }); +const staffStates = { + afterSubmission: { step: stepNames.staff }, + afterSelf: { step: stepNames.staff, self: closedStates.open }, + afterPeer: { step: stepNames.staff }, +}; + +const nullStepInfo = { studentTraining: null, self: null, peer: null }; + +export const getProgressState = ({ viewStep, progressKey, stepConfig }) => { + const createStepInfo = ({ + isGraded = false, + step = null, + submission = createSubmissionStatus(), + studentTraining = null, + self = null, + peer = null, + }) => { + if (step === stepNames.submission) { + return { submission, ...nullStepInfo }; + } + + // by default, pass null for all steps after submission + const stepIndex = isGraded ? stepConfig.length - 1 : stepConfig.indexOf(step); + const out = {}; + for (let i = 0; i < stepIndex; i++) { + out[stepConfig[i]] = null; + } + + // populate finished state for view step if we are past it + if (stepIndex >= stepConfig.indexOf(viewStep)) { + out[viewStep] = finishedStates[viewStep]; + } + + // Need to view peer data even if past it + if (step !== stepNames.peer) { + const peerIndex = stepConfig.indexOf(stepNames.peer); + if (stepIndex > peerIndex) { + out[stepNames.peer] = finishedStates[stepNames.peer]; + } + } + return { + submission, + studentTraining, + self, + peer, + ...out, + }; + }; + + const createProgressData = (activeStepName, stepInfoData) => ({ + active_step_name: activeStepName, + step_info: createStepInfo({ step: activeStepName, ...stepInfoData }), + }); + + const createFinishedState = (step) => { + if (step === stepNames.submission) { + return stepConfig[0]; + } + const stepIndex = stepConfig.indexOf(step); + const nextStep = stepConfig[stepIndex + 1]; + return createProgressData(nextStep, { step: nextStep }); + }; + + const submissionState = (stepInfoData) => ( + createProgressData(stepNames.submission, { submission: stepInfoData }) + ); + const createCancelledState = (step) => ( + createProgressData(step, { submission: subStatuses.cancelled }) + ); + + const trainingState = (stepInfoData) => createProgressData( + stepNames.studentTraining, + { studentTraining: stepInfoData, step: stepNames.studentTraining }, + ); + + const selfState = (closedState) => createProgressData( + stepNames.self, + { self: closedState, step: stepNames.self }, + ); + + const peerState = (stepInfoData) => createProgressData( + stepNames.peer, + { peer: stepInfoData, step: stepNames.peer }, + ); + + const staffState = (stepInfoData = {}) => ( + createProgressData(stepNames.staff, { step: stepNames.staff, ...stepInfoData }) + ); + + const mapping = StrictDict({ + [progressKeys.cancelledDuringSubmission]: createCancelledState(stepNames.submission), + [progressKeys.cancelledDuringStudentTraining]: createCancelledState(stepNames.studentTraining), + [progressKeys.cancelledDuringSelf]: createCancelledState(stepNames.self), + [progressKeys.cancelledDuringPeer]: createCancelledState(stepNames.peer), + [progressKeys.cancelledDuringStaff]: createCancelledState(stepNames.staff), + + [progressKeys.submissionEarly]: submissionState(subStatuses.notAvailable), + [progressKeys.submissionClosed]: submissionState(subStatuses.closed), + [progressKeys.submissionTeamAlreadySubmitted]: + submissionState(teamSubStatuses.teamAlreadySubmitted), + [progressKeys.submissionNeedTeam]: submissionState(teamSubStatuses.needTeam), + [progressKeys.submissionUnsaved]: submissionState(subStatuses.unsubmitted), + [progressKeys.submissionSaved]: submissionState(subStatuses.unsubmitted), + + [progressKeys.submissionFinished]: createFinishedState(stepNames.submission), + + [progressKeys.studentTraining]: trainingState(trainingStatuses.unsubmitted), + [progressKeys.studentTrainingValidation]: trainingState(trainingStatuses.unsubmitted), + [progressKeys.studentTrainingPartial]: trainingState(trainingStatuses.partial), + [progressKeys.studentTrainingFinished]: createFinishedState(stepNames.studentTraining), + + [progressKeys.selfAssessment]: selfState(closedStates.open), + [progressKeys.selfAssessmentLate]: selfState(closedStates.closed), + [progressKeys.selfAssessmentFinished]: createFinishedState(stepNames.self), + + [progressKeys.peerAssessment]: peerState(peerStatuses.unsubmitted), + [progressKeys.peerAssessmentEarly]: peerState(peerStatuses.notAvailable), + [progressKeys.peerAssessmentWaiting]: peerState(peerStatuses.waiting), + [progressKeys.peerAssessmentLate]: peerState(peerStatuses.closed), + [progressKeys.peerAssessmentFinished]: createFinishedState(stepNames.peer), + + [progressKeys.staffAfterSubmission]: staffState(), + [progressKeys.staffAfterSelf]: staffState(), + [progressKeys.staffAfterPeer]: staffState({ peer: finishedStates.peer }), + [progressKeys.graded]: + createProgressData(stepConfig[stepConfig.length - 1], { isGraded: true }), + [progressKeys.gradedSubmittedOnPreviousTeam]: + createProgressData(stepConfig[stepConfig.length - 1], { isGrdaed: true }), + }); + return mapping[progressKey]; +}; + export default { - submission: createProgressData({}), - training: ({ - closedState = closedStates.open, - numCompleted, - }) => createProgressData({ - active_step_name: 'studentTraining', - step_info: { - studentTraining: genTrainingStepInfo({ closedState, numCompleted }), - self: null, - peer: null, - }, - }), - self: createProgressData({ - active_step_name: 'self', - step_info: { - studentTraining: finishedTrainingStepInfo, - self: closedStates.open, - peer: null, - }, - }), - peer: ({ - closedState = closedStates.open, - numCompleted = 0, - isWaiting = false, - numReceived = 0, - } = {}) => createProgressData({ - active_step_name: 'peer', - step_info: { - studentTraining: finishedTrainingStepInfo, - self: closedStates.open, - peer: genPeerStepInfo({ - closedState, - numCompleted, - isWaiting, - numReceived, - }), - }, - }), - staff: createProgressData({ - active_step_name: 'staff', - step_info: { - training: finishedTrainingStepInfo, - self: closedStates.open, - peer: finishedPeerStepInfo, - }, - }), - graded: createProgressData({ - active_step_name: null, - has_received_final_grade: true, - received_grades: { - peer: { earned: 5, possible: 25 }, - staff: { earned: 8, possible: 10 }, - }, - step_info: { - training: finishedTrainingStepInfo, - self: closedStates.open, - peer: finishedPeerStepInfo, - }, - }), + getProgressState, }; diff --git a/src/data/services/lms/fakeData/pageData/response.js b/src/data/services/lms/fakeData/pageData/response.js new file mode 100644 index 00000000..cda1f701 --- /dev/null +++ b/src/data/services/lms/fakeData/pageData/response.js @@ -0,0 +1,106 @@ +/* eslint-disable camelcase */ +import { StrictDict } from '@edx/react-unit-test-utils'; + +import { closedStates, progressKeys } from '../constants'; + +const files = [ + { + file_name: 'test.png', + file_url: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/1920px-Image_created_with_a_mobile_phone.png', + file_description: 'test description', + }, + { + file_name: 'test.txt', + file_url: 'https://raw.githubusercontent.com/openedx/edx-ora2/master/README.rst', + file_description: 'test description', + }, + { + file_name: 'test.pdf', + file_url: 'https://raw.githubusercontent.com/py-pdf/sample-files/main/004-pdflatex-4-pages/pdflatex-4-pages.pdf', + file_description: 'test description', + }, + { + file_name: 'error.pdf', + file_url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', + file_description: 'failed to load', + }, +]; + +const users = ['user1', 'user2', 'user3', 'user4']; + +/// Submission +export const createFiles = (numFiles, { isTeam = false } = {}) => Array.from(Array(numFiles)).map( + (_, i) => ({ + ...files[i], + file_size: 100, + file_index: i, + uploaded_by: isTeam ? users[0] : users[numFiles % i], + }), +); + +export const createResponse = ({ + text_responses = null, + uploaded_files = null, + team_uploaded_files = null, +} = {}) => ({ + text_responses, + uploaded_files, + team_uploaded_files, +}); + +export const states = StrictDict({ + individual: { + empty: createResponse({ + text_responses: ['', ''], + uploaded_files: [], + }), + filled: createResponse({ + text_responses: ['Response 1', 'Response 2'], + uploaded_files: createFiles(2), + }), + cancelled: null, + }, + team: { + empty: createResponse({ + text_responses: ['', ''], + uploaded_files: [], + team_uploaded_files: [], + }), + filled: createResponse({ + text_responses: ['Response 1', 'Response 2'], + uploaded_files: createFiles(2), + team_uploaded_files: createFiles(3, { isTeam: true }), + }), + }, +}); + +export const getResponseState = ({ progressKey, isTeam }) => { + if ([ + progressKeys.cancelledDuringSubmission, + progressKeys.cancelledDuringStudentTraining, + progressKeys.cancelledDuringSubmission, + progressKeys.cancelledDuringStudentTraining, + progressKeys.cancelledDuringSelf, + progressKeys.cancelledDuringPeer, + progressKeys.cancelledDuringStaff, + progressKeys.submissionEarly, + progressKeys.submissionClosed, + progressKeys.submissionTeamAlreadySubmitted, + progressKeys.submissionNeedTeam, + ].includes(progressKey)) { + return null; + } // no submission info for these views + + if (!isTeam) { + return progressKey === progressKeys.submissionUnsaved + ? states.individual.empty + : states.individual.filled; + } + return progressKey === progressKeys.submissionUnsaved + ? states.team.empty + : states.team.filled; +}; + +export default StrictDict({ + getResponseState, +}); diff --git a/src/data/services/lms/fakeData/pageData/submission.js b/src/data/services/lms/fakeData/pageData/submission.js deleted file mode 100644 index b8a65bd0..00000000 --- a/src/data/services/lms/fakeData/pageData/submission.js +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable camelcase */ -import { StrictDict } from '@edx/react-unit-test-utils'; -import { closedStates } from './progress'; - -/// Submission -export const createFiles = (numFiles) => Array.from(Array(numFiles)).map((_, i) => ({ - file_url: `https://placehold.co/600x400?text=File+${i}`, - file_name: `file_name_${i}`, - file_description: 'file_description', - file_size: 100, - file_index: 0, -})); - -export const createTeamInfo = ({ - team_name = 'Team name', - team_usernames = ['user1', 'user2'], - previous_team_name = 'Previous team name', - has_submitted = false, - team_uploaded_files = null, -} = {}) => ({ - team_name, - team_usernames, - previous_team_name, - has_submitted, - team_uploaded_files: team_uploaded_files || createFiles(3), -}); - -export const createSubmissionStatus = ({ - has_submitted = true, - has_cancelled = false, - has_recieved_grade = false, - closedState = closedStates.open, -} = {}) => ({ - ...closedState, - has_submitted, - has_cancelled, - has_recieved_grade, -}); - -export const createSubmissionResponse = ({ - text_responses = ['Response 1', 'Response 2'], - uploaded_files = null, -} = {}) => ({ - text_responses, - uploaded_files: uploaded_files || createFiles(2), -}); - -export const createSubmission = ({ - teamInfo = createTeamInfo(), - submissionStatus = createSubmissionStatus(), - response = createSubmissionResponse(), -} = {}) => ({ - team_info: teamInfo, - response, - ...submissionStatus, -}); - -export default StrictDict({ - emptySubmission: createSubmission({ - teamInfo: {}, - submissionStatus: createSubmissionStatus({ - has_submitted: false, - has_cancelled: false, - has_received_grade: false, - }), - response: createSubmissionResponse({ - text_responses: ['', ''], - uploaded_files: [], - }), - }), - individualSubmission: createSubmission({ - team_info: {}, - submission_status: createSubmissionStatus(), - response: createSubmissionResponse(), - }), - teamSubmission: createSubmission(), -}); diff --git a/src/data/services/lms/hooks/data.ts b/src/data/services/lms/hooks/data.ts index 389bcbe1..38d50c55 100644 --- a/src/data/services/lms/hooks/data.ts +++ b/src/data/services/lms/hooks/data.ts @@ -19,7 +19,7 @@ export const usePageData = (): types.QueryData => { const location = useLocation(); const view = location.pathname.split('/')[1]; const params = useParams(); - const progressKey = params.progressKey || routeSteps[view]; + const progressKey = params.progressKey; return useQuery({ queryKey: [queryKeys.pageData], queryFn: () => Promise.resolve(camelCaseObject(loadState({ diff --git a/src/data/services/lms/hooks/selectors.ts b/src/data/services/lms/hooks/selectors.ts deleted file mode 100644 index 4686ca68..00000000 --- a/src/data/services/lms/hooks/selectors.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { useActiveView } from 'hooks'; -import { - closedReasons, - stepStates, - stepNames, -} from '../constants'; -import * as data from './data'; - -import * as types from '../types'; - -export const useORAConfigDataStatus = (): types.QueryStatus => { - const queryStatus = data.useORAConfig(); - return { - isLoading: queryStatus.isLoading, - isFetching: queryStatus.isFetching, - isInitialLoading: queryStatus.isInitialLoading, - status: queryStatus.status, - error: queryStatus.error, - }; -}; - -export const useIsORAConfigLoaded = (): boolean => ( - data.useORAConfig().status === 'success' -); - -export const useORAConfigData = (): types.ORAConfig => ( - data.useORAConfig().data -); - -export const usePrompts = () => useORAConfigData().prompts; - -export const useSubmissionConfig = (): types.SubmissionConfig => ( - useORAConfigData().submissionConfig -); - -export const useAssessmentStepConfig = (): types.AssessmentStepConfig => ( - useORAConfigData().assessmentSteps -); - -export const useAssessmentStepOrder = () => useAssessmentStepConfig()?.order; - -export const useRubricConfig = (): types.RubricConfig => useORAConfigData().rubric; - -export const useEmptyRubric = () => { - const rubric = useRubricConfig(); - const out = { - optionsSelected: rubric.criteria.reduce( - (obj, curr) => ({ ...obj, [curr.name]: null }), - {}, - ), - criterionFeedback: {}, - overallFeedback: '', - }; - rubric.criteria.forEach(criterion => { - if (criterion.feedbackEnabled) { - out.criterionFeedback[criterion.name] = ''; - } - }); - return out; -}; - -export const useLeaderboardConfig = (): types.LeaderboardConfig => useORAConfigData().leaderboardConfig; - -export const usePageDataStatus = () => { - const queryStatus = data.usePageData(); - return { - isLoading: queryStatus.isLoading, - isFetching: queryStatus.isFetching, - isInitialLoading: queryStatus.isInitialLoading, - status: queryStatus.status, - error: queryStatus.error, - }; -}; -export const useIsPageDataLoaded = (): boolean => ( - data.usePageData().status === 'success' -); - -export const usePageData = (): types.PageData => data.usePageData()?.data; - -export const useProgressData = (): types.ProgressData => usePageData()?.progress; - -export const useActiveStepName = (): string => useProgressData().activeStepName; - -export const useSubmissionTeamInfo = (): types.SubmissionTeamData => usePageData()?.submission.teamInfo; - -export const useSubmissionStatus = (): types.SubmissionStatusData => { - const { - hasCancelled, - hasReceivedGrade, - hasSubmitted, - isClosed, - closedReason, - } = usePageData().submission; - return { - hasCancelled, - hasReceivedGrade, - hasSubmitted, - isClosed, - closedReason, - }; -}; - -export const useHasCancelled = () => useSubmissionStatus().hasCancelled; -export const useHasReceivedFinalGrade = () => useProgressData().hasReceivedFinalGrade; - -export const useSubmissionData = () => usePageData().submission; - -export const useSubmissionResponse = (): types.SubmissionResponseData => ( - usePageData().submission.response -); - -export const useStepIndex = ({ step }) => useAssessmentStepOrder().indexOf(step); - -export const useSubmissionState = () => { - const subStatus = useSubmissionStatus(); - const teamInfo = useSubmissionTeamInfo(); - - if (subStatus.hasCancelled) { - return stepStates.cancelled; - } - - if (subStatus.hasSubmitted) { - return stepStates.completed; - } - if (subStatus.isClosed) { - if (subStatus.closedReason === closedReasons.pastDue) { - return stepStates.closed; - } - return stepStates.notAvailable; - } - if (!subStatus.hasSubmitted && teamInfo.hasSubmitted) { - return stepStates.teamAlreadySubmitted; - } - return stepStates.inProgress; -}; - -export const useStepInfo = () => useProgressData().stepInfo; - -export const useStepState = ({ step }) => { - const hasCancelled = useHasCancelled(); - const hasReceivedFinalGrade = useHasReceivedFinalGrade(); - const stepInfo = useStepInfo(); - const activeStepIndex = useStepIndex({ step: useActiveStepName() }); - const stepIndex = useStepIndex({ step }); - const subState = useSubmissionState(); - - if (hasReceivedFinalGrade) { - return stepStates.completed; - } - - if (step === stepNames.submission) { - return subState; - } - - // Cancelled submission affects all states - if (hasCancelled) { return stepStates.cancelled; } - - if (step === stepNames.myGrades) { - return hasReceivedFinalGrade ? stepStates.completed : stepStates.notAvailable; - } - - // For Assessment steps - if (stepIndex < activeStepIndex) { return stepStates.completed; } - if (stepIndex > activeStepIndex) { return stepStates.notAvailable; } - - // only check for closed or not-available on active step - if (stepInfo[step]?.isClosed) { - return stepInfo[step].closedReason === closedReasons.pastDue - ? stepStates.closed - : stepStates.notAvailable; - } - return stepStates.inProgress; -}; - -export const useXBlockState = () => { - const activeStepState = useStepState({ step: useActiveStepName() }); - if ([ - stepStates.cancelled, - stepStates.closed, - stepStates.notAvailable, - ].includes(activeStepState)) { - return activeStepState; - } - return stepStates.inProgress; -}; - -export const useEffectiveGrade = () => { - const { assessments } = usePageData(); - return assessments ? assessments[assessments.effectiveAssessmentType] : null; -}; diff --git a/src/data/services/lms/hooks/selectors/index.ts b/src/data/services/lms/hooks/selectors/index.ts new file mode 100644 index 00000000..63d51ddd --- /dev/null +++ b/src/data/services/lms/hooks/selectors/index.ts @@ -0,0 +1,69 @@ +import { StrictDict } from '@edx/react-unit-test-utils'; + +import { stepNames, closedReasons, stepStates } from 'data/services/lms/constants'; + +import * as oraConfigSelectors from './oraConfig'; +import * as pageDataSelectors from './pageData'; + +export * from './oraConfig'; +export * from './pageData'; + +const selectors = { + ...oraConfigSelectors, + ...pageDataSelectors, +}; + +// Meta +export const useStepState = ({ step }) => { + const hasCancelled = selectors.useHasCancelled(); + const hasReceivedFinalGrade = selectors.useHasReceivedFinalGrade(); + const stepInfo = selectors.useStepInfo(); + const activeStepIndex = selectors.useStepIndex({ step: selectors.useActiveStepName() }); + const stepIndex = selectors.useStepIndex({ step }); + const subState = selectors.useSubmissionState(); + + if (hasReceivedFinalGrade) { + return stepStates.completed; + } + + if (step === stepNames.submission) { + return subState; + } + + // Cancelled submission affects all states + if (hasCancelled) { return stepStates.cancelled; } + + if (step === stepNames.myGrades) { + return hasReceivedFinalGrade ? stepStates.completed : stepStates.notAvailable; + } + + // For Assessment steps + if (stepIndex < activeStepIndex) { return stepStates.completed; } + if (stepIndex > activeStepIndex) { return stepStates.notAvailable; } + + // only check for closed or not-available on active step + if (stepInfo[step]?.isClosed) { + return stepInfo[step].closedReason === closedReasons.pastDue + ? stepStates.closed + : stepStates.notAvailable; + } + return stepStates.inProgress; +}; + +export const useXBlockState = () => { + const activeStepState = useStepState({ step: selectors.useActiveStepName() }); + if ([ + stepStates.cancelled, + stepStates.closed, + stepStates.notAvailable, + ].includes(activeStepState)) { + return activeStepState; + } + return stepStates.inProgress; +}; + +export default StrictDict({ + ...selectors, + useStepState, + useXBlockState, +}); diff --git a/src/data/services/lms/hooks/selectors/oraConfig.ts b/src/data/services/lms/hooks/selectors/oraConfig.ts new file mode 100644 index 00000000..928529e8 --- /dev/null +++ b/src/data/services/lms/hooks/selectors/oraConfig.ts @@ -0,0 +1,59 @@ +import * as data from 'data/services/lms/hooks/data'; +import * as types from 'data/services/lms/types'; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ORA Config Data +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +export const useORAConfigDataStatus = (): types.QueryStatus => { + const queryStatus = data.useORAConfig(); + return { + isLoading: queryStatus.isLoading, + isFetching: queryStatus.isFetching, + isInitialLoading: queryStatus.isInitialLoading, + status: queryStatus.status, + error: queryStatus.error, + }; +}; + +export const useIsORAConfigLoaded = (): boolean => ( + data.useORAConfig().status === 'success' +); + +export const useORAConfigData = (): types.ORAConfig => ( + data.useORAConfig().data +); + +export const usePrompts = () => useORAConfigData().prompts; + +export const useSubmissionConfig = (): types.SubmissionConfig => ( + useORAConfigData().submissionConfig +); + +export const useAssessmentStepConfig = (): types.AssessmentStepConfig => ( + useORAConfigData().assessmentSteps +); + +export const useAssessmentStepOrder = (): string[] => useAssessmentStepConfig()?.order; +export const useStepIndex = ({ step }): number => useAssessmentStepOrder().indexOf(step); + +export const useRubricConfig = (): types.RubricConfig => useORAConfigData().rubric; + +export const useEmptyRubric = () => { + const rubric = useRubricConfig(); + const out = { + optionsSelected: rubric.criteria.reduce( + (obj, curr) => ({ ...obj, [curr.name]: null }), + {}, + ), + criterionFeedback: {}, + overallFeedback: '', + }; + rubric.criteria.forEach(criterion => { + if (criterion.feedbackEnabled) { + out.criterionFeedback[criterion.name] = ''; + } + }); + return out; +}; + +export const useLeaderboardConfig = (): types.LeaderboardConfig => useORAConfigData().leaderboardConfig; diff --git a/src/data/services/lms/hooks/selectors/pageData.ts b/src/data/services/lms/hooks/selectors/pageData.ts new file mode 100644 index 00000000..1f42d32f --- /dev/null +++ b/src/data/services/lms/hooks/selectors/pageData.ts @@ -0,0 +1,69 @@ +import { + closedReasons, + stepStates, +} from 'data/services/lms/constants'; +import * as data from 'data/services/lms/hooks/data'; +import * as types from 'data/services/lms/types'; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Page Data +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +export const usePageDataStatus = () => { + const queryStatus = data.usePageData(); + return { + isLoading: queryStatus.isLoading, + isFetching: queryStatus.isFetching, + isInitialLoading: queryStatus.isInitialLoading, + status: queryStatus.status, + error: queryStatus.error, + }; +}; +export const useIsPageDataLoaded = (): boolean => ( + data.usePageData().status === 'success' +); +export const usePageData = (): types.PageData => data.usePageData()?.data; + +// progress +export const useProgressData = (): types.ProgressData => usePageData()?.progress; +export const useActiveStepName = (): string => useProgressData().activeStepName; +export const useStepInfo = () => useProgressData().stepInfo; + +export const useSubmissionStatus = (): types.SubmissionStepInfo => useStepInfo().submission; +export const useSubmissionTeamInfo = (): types.SubmissionTeamInfo | null => ( + useSubmissionStatus().teamInfo +); +export const useHasCancelled = () => useSubmissionStatus().hasCancelled; + +// response +export const useResponseData = (): types.ResponseData => usePageData().response; + +export const useSubmissionState = () => { + const subStatus = useSubmissionStatus(); + const teamInfo = useSubmissionTeamInfo(); + + if (subStatus.hasCancelled) { + return stepStates.cancelled; + } + + if (subStatus.hasSubmitted) { + return stepStates.completed; + } + if (subStatus.isClosed) { + if (subStatus.closedReason === closedReasons.pastDue) { + return stepStates.closed; + } + return stepStates.notAvailable; + } + if (!subStatus.hasSubmitted && teamInfo && teamInfo.hasSubmitted) { + return stepStates.teamAlreadySubmitted; + } + return stepStates.inProgress; +}; + +// Assessments +export const useAssessmentsData = (): types.AssessmentsData => usePageData().assessments; +export const useHasReceivedFinalGrade = (): boolean => useAssessmentsData() !== null; +export const useEffectiveGrade = () => { + const assessments = useAssessmentsData(); + return assessments ? assessments[assessments.effectiveAssessmentType] : null; +}; diff --git a/src/data/services/lms/types/pageData.ts b/src/data/services/lms/types/pageData.ts index 3b30fbbc..aeeae9d1 100644 --- a/src/data/services/lms/types/pageData.ts +++ b/src/data/services/lms/types/pageData.ts @@ -19,6 +19,19 @@ export interface StepClosedInfo { closedReason?: 'notAvailable' | 'pastDue', } +export interface SubmissionTeamInfo { + teamName: string, + teamUsernames: string[], + previousTeamName: string | null, + hasSubmitted: boolean, +} + +export interface SubmissionStepInfo extends StepClosedInfo { + hasSubmitted: boolean, + hasCancelled: boolean, + teamInfo: SubmissionTeamInfo | null, +} + export interface LearnerTrainingStepInfo extends StepClosedInfo { numberOfAssessmentsCompleted: number, expectedRubricSelctions: RubricSelection[], @@ -31,6 +44,7 @@ export interface PeerStepInfo extends StepClosedInfo { } export interface StepInfo { + submission: SubmissionStepInfo, peer: PeerStepInfo | null, learnerTraining: LearnerTrainingStepInfo | null, self: StepClosedInfo | null, @@ -43,13 +57,6 @@ export interface ProgressData { stepInfo: StepInfo, } -// Submission Data -export interface SubmissionStatusData extends StepClosedInfo { - hasSubmitted: boolean, - hasCancelled: boolean, - hasReceivedGrade: boolean, -} - export interface UploadedFile { fileUrl: string, fileDescription: string, @@ -59,22 +66,10 @@ export interface UploadedFile { fileIndex?: number, } -export interface SubmissionTeamData { - teamName: string, - teamUsernames: string[], - previousTeamName: string | null, - hasSubmitted: boolean, - teamUploadedFiles: UploadedFile[], -} - -export interface SubmissionResponseData { - textResponses: string[], - uploadedFiles: UploadedFile[], -} - -export interface SubmissionData extends SubmissionStatusData { - teamInfo: SubmissionTeamData, - response: SubmissionResponseData, +export interface ResponseData { + textResponses: string[] | null, + uploadedFiles: UploadedFile[] | null, + teamUploadedFiles: UploadedFile[] | null, } // Assessments Data @@ -85,7 +80,7 @@ export interface AssessmentData { } export interface AssessmentsData { - effectiveAssessmentType: 'staff' | 'peer' | 'self', + effectiveAssessmentType: 'self' | 'peer' | 'staff', assessments: { staff?: { stepScore: { earned: number, possible: number }, @@ -108,6 +103,6 @@ export interface AssessmentsData { export interface PageData { progress: ProgressData, - submission: SubmissionData, + response: ResponseData, assessments: AssessmentsData } diff --git a/src/views/FilePreviewView/index.jsx b/src/views/FilePreviewView/index.jsx deleted file mode 100644 index 7537201f..00000000 --- a/src/views/FilePreviewView/index.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; - -import { FileRenderer, isSupported } from 'components/FilePreview'; - -const FilePreviewView = () => ( -
- {[ - { - name: 'test.png', - downloadUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/1920px-Image_created_with_a_mobile_phone.png', - description: 'test description', - }, - { - name: 'test.txt', - downloadUrl: 'https://raw.githubusercontent.com/openedx/edx-ora2/master/README.rst', - description: 'test description', - }, - { - name: 'test.pdf', - downloadUrl: 'https://raw.githubusercontent.com/py-pdf/sample-files/main/004-pdflatex-4-pages/pdflatex-4-pages.pdf', - description: 'test description', - }, - { - name: 'error.pdf', - downloadUrl: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf', - description: 'failed to load', - }, - ].filter(isSupported).map((file) => ( - - ))} -
-); - -export default FilePreviewView; diff --git a/src/views/PeerAssessmentView/Content.jsx b/src/views/PeerAssessmentView/Content.jsx index 315e11bb..42683a8f 100644 --- a/src/views/PeerAssessmentView/Content.jsx +++ b/src/views/PeerAssessmentView/Content.jsx @@ -2,22 +2,24 @@ import React from 'react'; import { usePrompts, - useSubmissionResponse, + useResponseData, } from 'data/services/lms/hooks/selectors'; import Prompt from 'components/Prompt'; import TextResponse from 'components/TextResponse'; import FileUpload from 'components/FileUpload'; +import FilePreview from 'components/FilePreview'; const AssessmentContent = () => { const prompts = usePrompts(); - const response = useSubmissionResponse(); + const response = useResponseData(); return (
{React.Children.toArray( prompts.map((prompt, index) => (
+
)), diff --git a/src/views/SelfAssessmentView/Content.jsx b/src/views/SelfAssessmentView/Content.jsx index 315e11bb..42683a8f 100644 --- a/src/views/SelfAssessmentView/Content.jsx +++ b/src/views/SelfAssessmentView/Content.jsx @@ -2,22 +2,24 @@ import React from 'react'; import { usePrompts, - useSubmissionResponse, + useResponseData, } from 'data/services/lms/hooks/selectors'; import Prompt from 'components/Prompt'; import TextResponse from 'components/TextResponse'; import FileUpload from 'components/FileUpload'; +import FilePreview from 'components/FilePreview'; const AssessmentContent = () => { const prompts = usePrompts(); - const response = useSubmissionResponse(); + const response = useResponseData(); return (
{React.Children.toArray( prompts.map((prompt, index) => (
+
)), diff --git a/src/views/StatusAlert/SelfAssessment/index.jsx b/src/views/StatusAlert/SelfAssessment/index.jsx index 2e6f84b6..658ae113 100644 --- a/src/views/StatusAlert/SelfAssessment/index.jsx +++ b/src/views/StatusAlert/SelfAssessment/index.jsx @@ -10,7 +10,6 @@ import SelfCompleteStatusAlert from './CompleteStatusAlert'; const SelfAssessmentStatusAlert = () => { const stepState = useStepState({ step: stepNames.self }); // const stepState = stepStates.completed; - return ; if (stepState === stepStates.cancelled) { return ; } diff --git a/src/views/StudentTrainingView/Content.jsx b/src/views/StudentTrainingView/Content.jsx index 315e11bb..73fe80a1 100644 --- a/src/views/StudentTrainingView/Content.jsx +++ b/src/views/StudentTrainingView/Content.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { usePrompts, - useSubmissionResponse, + useResponseData, } from 'data/services/lms/hooks/selectors'; import Prompt from 'components/Prompt'; @@ -11,7 +11,7 @@ import FileUpload from 'components/FileUpload'; const AssessmentContent = () => { const prompts = usePrompts(); - const response = useSubmissionResponse(); + const response = useResponseData(); return (
{React.Children.toArray( diff --git a/src/views/SubmissionView/SubmissionActions.jsx b/src/views/SubmissionView/SubmissionActions.jsx index 442462c7..e778951f 100644 --- a/src/views/SubmissionView/SubmissionActions.jsx +++ b/src/views/SubmissionView/SubmissionActions.jsx @@ -6,10 +6,8 @@ import { MutationStatus } from 'data/services/lms/constants'; import messages from './messages'; const SubmissionActions = ({ - submitResponseHandler, - submitResponseStatus, - saveResponseHandler, - saveResponseStatus, + submitResponse, + saveResponse, }) => { const { formatMessage } = useIntl(); @@ -17,8 +15,8 @@ const SubmissionActions = ({ @@ -63,11 +64,11 @@ SubmissionContent.propTypes = { draftSaved: PropTypes.bool.isRequired, }).isRequired, uploadedFiles: PropTypes.shape({ - value: PropTypes.shape({ + value: PropTypes.arrayOf(PropTypes.shape({ fileDescription: PropTypes.string, fileName: PropTypes.string, fileSize: PropTypes.number, - }), + })), onDeletedFile: PropTypes.func.isRequired, onFileUploaded: PropTypes.func.isRequired, }).isRequired, diff --git a/src/views/SubmissionView/SubmissionContentLayout.jsx b/src/views/SubmissionView/SubmissionContentLayout.jsx deleted file mode 100644 index 6467225c..00000000 --- a/src/views/SubmissionView/SubmissionContentLayout.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { Col, Row } from '@edx/paragon'; - -import Rubric from 'components/Rubric'; -import SubmissionContent from './SubmissionContent'; - -import './SubmissionContentLayout.scss'; - -const SubmissionContentLayout = ({ - submission, - oraConfigData, - onTextResponseChange, - onFileUploaded, - onDeletedFile, - draftSaved, -}) => ( -
-
- - - - - {oraConfigData.rubric.showDuringResponse && } - -
-
-); - -SubmissionContentLayout.propTypes = { - // eslint-disable-next-line react/forbid-prop-types - submission: PropTypes.any.isRequired, - // eslint-disable-next-line react/forbid-prop-types - oraConfigData: PropTypes.any.isRequired, - onTextResponseChange: PropTypes.func.isRequired, - onFileUploaded: PropTypes.func.isRequired, - onDeletedFile: PropTypes.func.isRequired, - draftSaved: PropTypes.bool.isRequired, -}; - -export default SubmissionContentLayout; diff --git a/src/views/SubmissionView/SubmissionContentLayout.test.jsx b/src/views/SubmissionView/SubmissionContentLayout.test.jsx deleted file mode 100644 index 98069ed0..00000000 --- a/src/views/SubmissionView/SubmissionContentLayout.test.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import { shallow } from '@edx/react-unit-test-utils'; -import SubmissionContentLayout from './SubmissionContentLayout'; - -jest.mock('components/Rubric', () => 'Rubric'); -jest.mock('./SubmissionContent', () => 'SubmissionContent'); - -describe('', () => { - const props = { - submission: 'submission', - oraConfigData: { - showDuringResponse: true, - }, - onTextResponseChange: jest.fn().mockName('onTextResponseChange'), - onFileUploaded: jest.fn().mockName('onFileUploaded'), - onDeletedFile: jest.fn().mockName('onDeletedFile'), - draftSaved: true, - }; - - it('show rubric', () => { - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); - - expect(wrapper.instance.findByType('Rubric')).toHaveLength(1); - }); - - it('hide rubric', () => { - const wrapper = shallow(); - expect(wrapper.snapshot).toMatchSnapshot(); - - expect(wrapper.instance.findByType('Rubric')).toHaveLength(0); - }); -}); diff --git a/src/views/SubmissionView/hooks.js b/src/views/SubmissionView/hooks.js index 69568e69..1c5a5ced 100644 --- a/src/views/SubmissionView/hooks.js +++ b/src/views/SubmissionView/hooks.js @@ -5,6 +5,7 @@ import { usePageData, usePrompts, useRubricConfig, + useResponseData, } from 'data/services/lms/hooks/selectors'; import { @@ -19,7 +20,7 @@ export const stateKeys = StrictDict({ }); export const useTextResponses = () => { - const { response } = usePageData().submission; + const response = useResponseData(); const prompts = usePrompts(); const [isDirty, setIsDirty] = useKeyedState(stateKeys.isDirty, false); @@ -61,7 +62,7 @@ export const useUploadedFiles = () => { const deleteFileMutation = useDeleteFile(); const uploadFilesMutation = useUploadFiles(); - const { response } = usePageData().submission; + const response = useResponseData(); const [value, setValue] = useKeyedState( stateKeys.uploadedFiles, diff --git a/src/views/SubmissionView/index.jsx b/src/views/SubmissionView/index.jsx index 52de0395..84689e19 100644 --- a/src/views/SubmissionView/index.jsx +++ b/src/views/SubmissionView/index.jsx @@ -15,6 +15,7 @@ import './index.scss'; export const SubmissionView = () => { const { actionsProps, formProps, showRubric } = useSubmissionViewData(); + console.log({ formProps }); if (!useIsPageDataLoaded()) { return null; }