Enable file upload form

Enable error boundary to catch React errors (#7)
Fix warning for link noreferrer
Include authorization header with file upload
This commit is contained in:
John Wu
2021-01-20 13:36:40 -08:00
committed by GitHub
parent ec4dd7d35d
commit fcde299197
9 changed files with 43 additions and 24 deletions

View File

@@ -24,10 +24,10 @@ export const FileUploadProvider = ({ children }) => {
done();
};
const upload = async (files) => {
const upload = async (files, accessToken) => {
try {
if (!files || files.length === 0) throw new Error("No file provided");
if (!files || files.length === 0) throw new Error("File required");
if (!accessToken || accessToken.length === 0) throw new Error("Access token required")
const file = files[0].file;
const filename = file.name;
@@ -37,7 +37,7 @@ export const FileUploadProvider = ({ children }) => {
setStatus(`Uploading ${filename}`);
setCancelUpload(getCancelToken());
const { data } = await uploadFile(file, setProgress, cancelUpload);
const { data } = await uploadFile(file, accessToken, setProgress, cancelUpload);
if (data.message) throw new Error(`${data.error}. ${data.message}`);
const url = ((data && data.link) ? data.link : "No URL available");
setLinkURL(url);

View File

@@ -1,6 +1,6 @@
jest.mock("../../services/uploadFile");
import {uploadFile, getCancelToken, setUploadFileResponse, setUploadFileDelay, getIssuedCancelToken } from "../../services/uploadFile"
import { setUploadFileDelay } from "../../services/uploadFile"
import { FileUploadProvider, useFileUploadContext } from "../Contexts/FileUploadContext";
import {render, cleanup, screen, fireEvent, waitFor} from "@testing-library/react"
@@ -9,6 +9,8 @@ describe("FileUploadContext", () => {
beforeEach(() => {
const TestComp = () => {
const { progress, uploading, status, linkURL, upload, cancel } = useFileUploadContext();
const TEST_FILE = [{ file: { name: "test.jpg" }}];
const TEST_ACCESSTOKEN = "ACCESSTOKEN";
return (
<>
<div data-testid="uploading">{uploading.toString()}</div>
@@ -16,7 +18,8 @@ describe("FileUploadContext", () => {
<div data-testid="status">{status}</div>
<div data-testid="linkURL">{linkURL}</div>
<button data-testid="uploadNoFile" onClick={() => upload()}/>
<button data-testid="upload" onClick={() => upload([{ file: { name: "test.jpg" }}])}/>
<button data-testid="uploadNoToken" onClick={() => upload(TEST_FILE)}/>
<button data-testid="upload" onClick={() => upload(TEST_FILE, TEST_ACCESSTOKEN)}/>
<button data-testid="cancel" onClick={() => cancel()}/>
</>
);
@@ -39,7 +42,15 @@ describe("FileUploadContext", () => {
fireEvent.click(screen.getByTestId("uploadNoFile"));
expect(screen.getByTestId("uploading").innerHTML).toEqual("false");
expect(screen.getByTestId("progress").innerHTML).toEqual("-1");
expect(screen.getByTestId("status").innerHTML).toEqual("Error occured: No file provided");
expect(screen.getByTestId("status").innerHTML).toEqual("Error occured: File required");
expect(screen.getByTestId("linkURL").innerHTML).toEqual("");
})
it("Upload no access token", async () => {
fireEvent.click(screen.getByTestId("uploadNoToken"));
expect(screen.getByTestId("uploading").innerHTML).toEqual("false");
expect(screen.getByTestId("progress").innerHTML).toEqual("-1");
expect(screen.getByTestId("status").innerHTML).toEqual("Error occured: Access token required");
expect(screen.getByTestId("linkURL").innerHTML).toEqual("");
})

View File

@@ -1,5 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Typography } from '@material-ui/core';
export default class ErrorBoundary extends Component {
state = {
@@ -14,7 +15,7 @@ export default class ErrorBoundary extends Component {
this.setState({ errorInfo });
}
render() {
if (this.state.hasError) return (<h1>Oops. An Error Occured</h1>);
if (this.state.hasError) return (<Typography variant="h3" align="center" >Oops. An React JS Error Occured.</Typography>);
return this.props.children;
}
}

View File

@@ -1,13 +1,15 @@
jest.mock("../Contexts/UserContext");
jest.mock("../Contexts/FileUploadContext");
import { BrowserRouter } from 'react-router-dom';
import { render, cleanup } from "@testing-library/react"
import FileUploadForm from './index';
import { BrowserRouter } from "react-router-dom";
import { render, cleanup } from "@testing-library/react";
import FileUploadForm from "./index";
import { setToken } from "../Contexts/UserContext";
describe("File Upload Form", () => {
it("Should render", () => {
setToken({ accessToken: { jwtToken: "TEST" } });
const { container } = render(<BrowserRouter><FileUploadForm /></BrowserRouter>);
expect(container).toMatchSnapshot();
cleanup();

View File

@@ -6,17 +6,18 @@ import { useFileUploadContext, FileUploadProvider } from "../Contexts/FileUploa
import ModalProgressBar from "../ModalProgressBar";
import useStyles from "../Styles";
const FileUploadZone = ({ classes }) => {
const { uploading, progress, status, linkURL, upload, cancel } = useFileUploadContext();
const FileUploadZone = ({ classes, token }) => {
const { upload } = useFileUploadContext();
const { token: { accessToken: { jwtToken : authToken } } } = useUserContext();
return (
<form className={classes.form} noValidate>
<DropzoneAreaBase
maxFileSize={5e+7}
showAlerts={false}
onAdd={upload}
onAdd={(files) => upload(files, authToken)}
/>
<ModalProgressBar uploading={uploading} progress={progress} onCancel={cancel} status={status} linkURL={linkURL} />
<ModalProgressBar />
</form>
);
};

View File

@@ -2,6 +2,7 @@ import React from "react";
import Modal from '@material-ui/core/Modal';
import { Button, LinearProgress } from "@material-ui/core";
import { useFileUploadContext } from "../Contexts/FileUploadContext";
const getModalStyle = () => {
const top = 30;
@@ -21,17 +22,17 @@ const getModalStyle = () => {
};
};
const ModalProgressBar = ({ onCancel, uploading, progress, status, linkURL }) => {
const ModalProgressBar = () => {
const { uploading, progress, status, linkURL, cancel } = useFileUploadContext();
const modalStyle = getModalStyle();
const onClickCancel = () => {
if (onCancel) onCancel();
}
const onClickCancel = cancel;
return (
<Modal open={uploading}>
<div style={modalStyle}>
{status && <p>{status}</p>}
{linkURL && <p><a href={linkURL} target="_blank">View</a></p>}
{linkURL && <p><a href={linkURL} target="_blank" rel="noreferrer">View</a></p>}
<LinearProgress variant="determinate" value={progress} />
<Button onClick={onClickCancel}>
{ progress === 100 || progress === -1 ? "Done" : "Cancel" }

View File

@@ -2,12 +2,14 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
// import ErrorBoundary from './components/ErrorBoundary';
import ErrorBoundary from './components/ErrorBoundary';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</React.StrictMode>,
document.getElementById('root')
);

View File

@@ -11,7 +11,7 @@ export const getCancelToken = () => {
return issuedCancelToken;
}
export const uploadFile = async (file, onProgress, cancelToken) => {
export const uploadFile = async (file, token, onProgress, cancelToken) => {
if (!uploadFileDelay) return uploadFileResponse;
onProgress(50);
await delay(10000);

View File

@@ -7,12 +7,13 @@ export const getCancelToken = () => {
return token.source();
}
export const uploadFile = (file, onProgress, cancelToken) => {
export const uploadFile = (file, token, onProgress, cancelToken) => {
const form = new FormData();
let options = {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': `Bearer ${token}`,
},
cancelToken,
};