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:
@@ -24,10 +24,10 @@ export const FileUploadProvider = ({ children }) => {
|
|||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
const upload = async (files) => {
|
const upload = async (files, accessToken) => {
|
||||||
try {
|
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 file = files[0].file;
|
||||||
const filename = file.name;
|
const filename = file.name;
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export const FileUploadProvider = ({ children }) => {
|
|||||||
setStatus(`Uploading ${filename}`);
|
setStatus(`Uploading ${filename}`);
|
||||||
setCancelUpload(getCancelToken());
|
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}`);
|
if (data.message) throw new Error(`${data.error}. ${data.message}`);
|
||||||
const url = ((data && data.link) ? data.link : "No URL available");
|
const url = ((data && data.link) ? data.link : "No URL available");
|
||||||
setLinkURL(url);
|
setLinkURL(url);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
jest.mock("../../services/uploadFile");
|
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 { FileUploadProvider, useFileUploadContext } from "../Contexts/FileUploadContext";
|
||||||
import {render, cleanup, screen, fireEvent, waitFor} from "@testing-library/react"
|
import {render, cleanup, screen, fireEvent, waitFor} from "@testing-library/react"
|
||||||
|
|
||||||
@@ -9,6 +9,8 @@ describe("FileUploadContext", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const TestComp = () => {
|
const TestComp = () => {
|
||||||
const { progress, uploading, status, linkURL, upload, cancel } = useFileUploadContext();
|
const { progress, uploading, status, linkURL, upload, cancel } = useFileUploadContext();
|
||||||
|
const TEST_FILE = [{ file: { name: "test.jpg" }}];
|
||||||
|
const TEST_ACCESSTOKEN = "ACCESSTOKEN";
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div data-testid="uploading">{uploading.toString()}</div>
|
<div data-testid="uploading">{uploading.toString()}</div>
|
||||||
@@ -16,7 +18,8 @@ describe("FileUploadContext", () => {
|
|||||||
<div data-testid="status">{status}</div>
|
<div data-testid="status">{status}</div>
|
||||||
<div data-testid="linkURL">{linkURL}</div>
|
<div data-testid="linkURL">{linkURL}</div>
|
||||||
<button data-testid="uploadNoFile" onClick={() => upload()}/>
|
<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()}/>
|
<button data-testid="cancel" onClick={() => cancel()}/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -39,7 +42,15 @@ describe("FileUploadContext", () => {
|
|||||||
fireEvent.click(screen.getByTestId("uploadNoFile"));
|
fireEvent.click(screen.getByTestId("uploadNoFile"));
|
||||||
expect(screen.getByTestId("uploading").innerHTML).toEqual("false");
|
expect(screen.getByTestId("uploading").innerHTML).toEqual("false");
|
||||||
expect(screen.getByTestId("progress").innerHTML).toEqual("-1");
|
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("");
|
expect(screen.getByTestId("linkURL").innerHTML).toEqual("");
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Typography } from '@material-ui/core';
|
||||||
|
|
||||||
export default class ErrorBoundary extends Component {
|
export default class ErrorBoundary extends Component {
|
||||||
state = {
|
state = {
|
||||||
@@ -14,7 +15,7 @@ export default class ErrorBoundary extends Component {
|
|||||||
this.setState({ errorInfo });
|
this.setState({ errorInfo });
|
||||||
}
|
}
|
||||||
render() {
|
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;
|
return this.props.children;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
jest.mock("../Contexts/UserContext");
|
jest.mock("../Contexts/UserContext");
|
||||||
jest.mock("../Contexts/FileUploadContext");
|
jest.mock("../Contexts/FileUploadContext");
|
||||||
|
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from "react-router-dom";
|
||||||
import { render, cleanup } from "@testing-library/react"
|
import { render, cleanup } from "@testing-library/react";
|
||||||
import FileUploadForm from './index';
|
import FileUploadForm from "./index";
|
||||||
|
import { setToken } from "../Contexts/UserContext";
|
||||||
|
|
||||||
describe("File Upload Form", () => {
|
describe("File Upload Form", () => {
|
||||||
|
|
||||||
it("Should render", () => {
|
it("Should render", () => {
|
||||||
|
setToken({ accessToken: { jwtToken: "TEST" } });
|
||||||
const { container } = render(<BrowserRouter><FileUploadForm /></BrowserRouter>);
|
const { container } = render(<BrowserRouter><FileUploadForm /></BrowserRouter>);
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|||||||
@@ -6,17 +6,18 @@ import { useFileUploadContext, FileUploadProvider } from "../Contexts/FileUploa
|
|||||||
import ModalProgressBar from "../ModalProgressBar";
|
import ModalProgressBar from "../ModalProgressBar";
|
||||||
import useStyles from "../Styles";
|
import useStyles from "../Styles";
|
||||||
|
|
||||||
const FileUploadZone = ({ classes }) => {
|
const FileUploadZone = ({ classes, token }) => {
|
||||||
const { uploading, progress, status, linkURL, upload, cancel } = useFileUploadContext();
|
const { upload } = useFileUploadContext();
|
||||||
|
const { token: { accessToken: { jwtToken : authToken } } } = useUserContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className={classes.form} noValidate>
|
<form className={classes.form} noValidate>
|
||||||
<DropzoneAreaBase
|
<DropzoneAreaBase
|
||||||
maxFileSize={5e+7}
|
maxFileSize={5e+7}
|
||||||
showAlerts={false}
|
showAlerts={false}
|
||||||
onAdd={upload}
|
onAdd={(files) => upload(files, authToken)}
|
||||||
/>
|
/>
|
||||||
<ModalProgressBar uploading={uploading} progress={progress} onCancel={cancel} status={status} linkURL={linkURL} />
|
<ModalProgressBar />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from "react";
|
|||||||
import Modal from '@material-ui/core/Modal';
|
import Modal from '@material-ui/core/Modal';
|
||||||
|
|
||||||
import { Button, LinearProgress } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
|
import { useFileUploadContext } from "../Contexts/FileUploadContext";
|
||||||
|
|
||||||
const getModalStyle = () => {
|
const getModalStyle = () => {
|
||||||
const top = 30;
|
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 modalStyle = getModalStyle();
|
||||||
const onClickCancel = () => {
|
const onClickCancel = cancel;
|
||||||
if (onCancel) onCancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={uploading}>
|
<Modal open={uploading}>
|
||||||
<div style={modalStyle}>
|
<div style={modalStyle}>
|
||||||
{status && <p>{status}</p>}
|
{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} />
|
<LinearProgress variant="determinate" value={progress} />
|
||||||
<Button onClick={onClickCancel}>
|
<Button onClick={onClickCancel}>
|
||||||
{ progress === 100 || progress === -1 ? "Done" : "Cancel" }
|
{ progress === 100 || progress === -1 ? "Done" : "Cancel" }
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './components/App';
|
import App from './components/App';
|
||||||
// import ErrorBoundary from './components/ErrorBoundary';
|
import ErrorBoundary from './components/ErrorBoundary';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
<ErrorBoundary>
|
||||||
<App />
|
<App />
|
||||||
|
</ErrorBoundary>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const getCancelToken = () => {
|
|||||||
return issuedCancelToken;
|
return issuedCancelToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const uploadFile = async (file, onProgress, cancelToken) => {
|
export const uploadFile = async (file, token, onProgress, cancelToken) => {
|
||||||
if (!uploadFileDelay) return uploadFileResponse;
|
if (!uploadFileDelay) return uploadFileResponse;
|
||||||
onProgress(50);
|
onProgress(50);
|
||||||
await delay(10000);
|
await delay(10000);
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ export const getCancelToken = () => {
|
|||||||
return token.source();
|
return token.source();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const uploadFile = (file, onProgress, cancelToken) => {
|
export const uploadFile = (file, token, onProgress, cancelToken) => {
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
let options = {
|
let options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data',
|
'Content-Type': 'multipart/form-data',
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
cancelToken,
|
cancelToken,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user