diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 653ebd6..3e34a5f 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -10,6 +10,9 @@ module.exports = { ...pathsToModuleNameMapper(compilerOptions.paths, { prefix: "/", }), + "react-spring/renderprops": + "/node_modules/react-spring/renderprops.cjs", + "react-spring": "/node_modules/react-spring/web.cjs", }, setupFilesAfterEnv: ["/src/setupTests.ts"], }; diff --git a/frontend/src/Home/Home.scss b/frontend/src/Home/Home.scss index 0f22070..3fbac21 100644 --- a/frontend/src/Home/Home.scss +++ b/frontend/src/Home/Home.scss @@ -24,6 +24,10 @@ .bp3-navbar { transition: 0.3s; } + + #uploadingStatusButton { + width: 40px; + } } #mainContainer.bp3-dark { diff --git a/frontend/src/Home/Home.tsx b/frontend/src/Home/Home.tsx index cca87aa..31f79c2 100644 --- a/frontend/src/Home/Home.tsx +++ b/frontend/src/Home/Home.tsx @@ -6,9 +6,11 @@ import { Button, Classes, IBreadcrumbProps, + Icon, Menu, Navbar, Popover, + Spinner, } from "@blueprintjs/core"; import * as React from "react"; import { connect } from "react-redux"; @@ -24,10 +26,13 @@ import { toggleDarkMode } from "~redux/localSettings/actions"; import { IAppState } from "~redux/reducers"; import { logoutUser } from "~redux/user/actions"; -interface IHomeProps extends RouteComponentProps { +export interface IHomeProps extends RouteComponentProps { allDocs: { [key: number]: IDocumentJSON }; user: IUserJSON | null; + fetching: boolean; + uploading: boolean; + darkMode: boolean; logout: () => void; @@ -72,6 +77,13 @@ export class HomeComponent extends React.PureComponent { + @@ -161,6 +173,8 @@ function mapStateToProps(state: IAppState) { allDocs: state.docs.all, user: state.user.user, darkMode: state.localSettings.darkMode, + fetching: state.docs.fetching, + uploading: state.docs.uploading, }; } diff --git a/frontend/src/Home/tests/Home.test.tsx b/frontend/src/Home/tests/Home.test.tsx new file mode 100644 index 0000000..696b7b6 --- /dev/null +++ b/frontend/src/Home/tests/Home.test.tsx @@ -0,0 +1,52 @@ +import { shallow } from "enzyme"; +import * as React from "react"; + +import { Icon, Spinner } from "@blueprintjs/core"; +import { HomeComponent, IHomeProps } from "../Home"; + +const defaultHomeProps: IHomeProps = { + allDocs: {}, + user: { id: 1, username: "test" }, + + fetching: false, + uploading: false, + + darkMode: false, + + logout: jest.fn(), + dispatchToggleDarkMode: jest.fn(), + + history: { location: { pathname: "/" } } as any, + location: { pathname: "/" } as any, + match: { + params: { + id: null, + }, + } as any, +}; + +describe("", () => { + it("should show a spinner when uploading", () => { + const wrapper = shallow( + , + ); + expect( + wrapper.find("#uploadingStatusButton").find(Spinner), + ).toHaveLength(1); + expect(wrapper.find("#uploadingStatusButton").find(Icon)).toHaveLength( + 0, + ); + }); + + it("should show a saved icon when not uploading", () => { + const wrapper = shallow( + , + ); + expect( + wrapper.find("#uploadingStatusButton").find(Spinner), + ).toHaveLength(0); + expect(wrapper.find("#uploadingStatusButton").find(Icon)).toHaveLength( + 1, + ); + }); +}); diff --git a/frontend/src/redux/docs/reducer.ts b/frontend/src/redux/docs/reducer.ts index f673842..b072a36 100644 --- a/frontend/src/redux/docs/reducer.ts +++ b/frontend/src/redux/docs/reducer.ts @@ -7,6 +7,7 @@ import { DocsAction, DocsTypes } from "./actions"; export interface IDocsState { all: { [key: number]: IDocumentJSON }; fetching: boolean; + uploading: boolean; error: string | null; spinner: boolean; @@ -18,6 +19,7 @@ export interface IDocsState { const defaultDocsState: IDocsState = { all: null, fetching: false, + uploading: false, error: null, spinner: false, newDocumentID: null, @@ -64,11 +66,17 @@ export const docsReducer: Reducer = ( all[deletedDocument.id] = deletedDocument; return { ...state, deletedDocument: null, all }; } + case DocsTypes.DOC_UPDATE_START: { + return { ...state, uploading: true }; + } + case DocsTypes.DOC_UPDATE_FAIL: { + return { ...state, uploading: false }; + } case DocsTypes.DOC_UPDATE_SUCCESS: { const all = { ...state.all }; const doc = action.payload.doc; all[doc.id] = doc; - return { ...state, all }; + return { ...state, all, uploading: false }; } case DocsTypes.DOCS_FETCH_FAIL: return { ...defaultDocsState, ...action.payload };