Skip to content

Commit

Permalink
lk/submission actions (#43)
Browse files Browse the repository at this point in the history
* chore: remove debris

* feat: interactive submission actions

* chore: add dirty for finishing later

* chore: update fake api because of camelcase

* chore: linting

* chore: component tests for file upload

* chore: component test for prompt

* chore: component test for test response

* chore: add component tests to submission view

* chore: linting

* chore: update test for data

* chore: rename

* chore: fix rich text editor
  • Loading branch information
leangseu-edx authored Sep 22, 2023
1 parent 50d2870 commit cc33edb
Show file tree
Hide file tree
Showing 49 changed files with 1,932 additions and 174 deletions.
19 changes: 19 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Routes, Route } from 'react-router-dom';
import { ErrorPage } from '@edx/frontend-platform/react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Spinner } from '@edx/paragon';

import { useIsORAConfigLoaded, useIsPageDataLoaded } from 'data/services/lms/hooks/selectors';

import PeerAssessmentView from 'views/PeerAssessmentView';
import SelfAssessmentView from 'views/SelfAssessmentView';
Expand All @@ -12,6 +15,22 @@ import routes from './routes';
const RouterRoot = () => {
const { formatMessage } = useIntl();

const isConfigLoaded = useIsORAConfigLoaded();
const isPageLoaded = useIsPageDataLoaded();

if (!isConfigLoaded || !isPageLoaded) {
return (
<div className="h-screen d-flex justify-content-center align-items-center">
<Spinner
animation="border"
variant="primary"
className="mr-3 spinner-md"
screenReaderText="loading"
/>
</div>
);
}

return (
<Routes>
<Route path={routes.peerAssessment} element={<PeerAssessmentView />} />
Expand Down
27 changes: 27 additions & 0 deletions src/components/FileUpload/ActionCell.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { IconButton, Icon } from '@edx/paragon';

import { useIntl } from '@edx/frontend-platform/i18n';
import { Delete, Preview } from '@edx/paragon/icons';

import messages from './messages';

const ActionCell = () => {
const { formatMessage } = useIntl();
return (
<>
<IconButton
src={Delete}
alt={formatMessage(messages.deleteButtonAltText)}
iconAs={Icon}
/>
<IconButton
src={Preview}
alt={formatMessage(messages.previewButtonAltText)}
iconAs={Icon}
/>
</>
);
};

export default ActionCell;
9 changes: 9 additions & 0 deletions src/components/FileUpload/ActionCell.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { shallow } from '@edx/react-unit-test-utils';
import ActionCell from './ActionCell';

describe('<ActionCell />', () => {
it('renders', () => {
const wrapper = shallow(<ActionCell />);
expect(wrapper.snapshot).toMatchSnapshot();
});
});
1 change: 0 additions & 1 deletion src/components/FileUpload/FileMetaDisplay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import messages from './messages';

const FileMetaDisplay = ({ name, description, size }) => (
<>
{console.log({ name, description, size })}
<div className="file-meta-option">
<strong><FormattedMessage {...messages.filePopoverNameTitle} /></strong>
<br />
Expand Down
94 changes: 94 additions & 0 deletions src/components/FileUpload/UploadConfirmModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import PropTypes from 'prop-types';

import {
Form, FormLabel, ModalDialog, Button, ActionRow,
} from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';
import { useUploadConfirmModalHooks } from './hooks';

const UploadConfirmModal = ({
open, files, closeHandler, uploadHandler,
}) => {
const { formatMessage } = useIntl();

const {
errors, exitHandler, confirmUploadClickHandler, onFileDescriptionChange,
} = useUploadConfirmModalHooks({
files,
closeHandler,
uploadHandler,
});

return (
<ModalDialog
isOpen={open}
title={formatMessage(messages.uploadFileModalTitle)}
hasCloseButton={false}
onClose={exitHandler}
>
<ModalDialog.Header>
<ModalDialog.Title>
{formatMessage(messages.uploadFileModalTitle)}
</ModalDialog.Title>
</ModalDialog.Header>

<ModalDialog.Body>
<div>
{files.map((file, i) => (
// eslint-disable-next-line react/no-array-index-key
<Form.Group key={i}>
<FormLabel>
<strong>
{formatMessage(messages.uploadFileDescriptionFieldLabel)}
</strong>
<span className="file-name-ellipsis">{file.name}</span>
</FormLabel>
<Form.Control
isInvalid={errors[i]}
name={`file-${i}-description`}
onChange={onFileDescriptionChange(file)}
/>
{errors[i] && (
<Form.Control.Feedback type="invalid">
{errors[i] && formatMessage(messages.fileDescriptionMissingError)}
</Form.Control.Feedback>
)}
</Form.Group>
))}
</div>
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant="tertiary" onClick={exitHandler}>
{formatMessage(messages.cancelUploadFileButton)}
</ModalDialog.CloseButton>
<Button variant="primary" onClick={confirmUploadClickHandler}>
{formatMessage(messages.confirmUploadFileButton)}
</Button>
</ActionRow>
</ModalDialog.Footer>
</ModalDialog>
);
};

UploadConfirmModal.defaultProps = {
open: false,
files: [],
closeHandler: () => {},
uploadHandler: () => {},
};
UploadConfirmModal.propTypes = {
open: PropTypes.bool,
files: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
description: PropTypes.string,
}),
),
closeHandler: PropTypes.func,
uploadHandler: PropTypes.func,
};

export default UploadConfirmModal;
57 changes: 57 additions & 0 deletions src/components/FileUpload/UploadConfirmModal.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { shallow } from '@edx/react-unit-test-utils';
import UploadConfirmModal from './UploadConfirmModal';

import { useUploadConfirmModalHooks } from './hooks';

jest.mock('./hooks', () => ({
useUploadConfirmModalHooks: jest.fn(),
}));

describe('<UploadConfirmModal />', () => {
const props = {
open: true,
files: [],
closeHandler: jest.fn().mockName('closeHandler'),
uploadHandler: jest.fn().mockName('uploadHandler'),
};

const mockHooks = (overrides) => {
useUploadConfirmModalHooks.mockReturnValueOnce({
errors: [],
exitHandler: jest.fn().mockName('exitHandler'),
confirmUploadClickHandler: jest.fn().mockName('confirmUploadClickHandler'),
onFileDescriptionChange: () => jest.fn().mockName('onFileDescriptionChange'),
...overrides,
});
};
describe('renders', () => {
test('no files', () => {
mockHooks();
const wrapper = shallow(<UploadConfirmModal {...props} />);
expect(wrapper.snapshot).toMatchSnapshot();

expect(wrapper.instance.findByType('Form.Group').length).toBe(0);
});

test('multiple files', () => {
mockHooks(
{ errors: new Array(2) },
);
const wrapper = shallow(<UploadConfirmModal {...props} files={[{ name: 'file1' }, { name: 'file2' }]} />);
expect(wrapper.snapshot).toMatchSnapshot();

expect(wrapper.instance.findByType('Form.Group').length).toBe(2);
expect(wrapper.instance.findByType('Form.Control.Feedback').length).toBe(0);
});

test('with errors', () => {
mockHooks({ errors: [true, false] });
const wrapper = shallow(<UploadConfirmModal {...props} files={[{ name: 'file1' }, { name: 'file2' }]} />);
// wrapper.setState({ errors: [true, false] });
expect(wrapper.snapshot).toMatchSnapshot();

expect(wrapper.instance.findByType('Form.Group').length).toBe(2);
expect(wrapper.instance.findByType('Form.Control.Feedback').length).toBe(1);
});
});
});
16 changes: 16 additions & 0 deletions src/components/FileUpload/__snapshots__/ActionCell.test.jsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<ActionCell /> renders 1`] = `
<Fragment>
<IconButton
alt="Delete"
iconAs="Icon"
src={[Function]}
/>
<IconButton
alt="Preview"
iconAs="Icon"
src={[Function]}
/>
</Fragment>
`;
Loading

0 comments on commit cc33edb

Please sign in to comment.