mirror of
https://github.com/usatiuk/writer.git
synced 2025-10-29 00:17:48 +01:00
add a document upload indicator
This commit is contained in:
@@ -10,6 +10,9 @@ module.exports = {
|
|||||||
...pathsToModuleNameMapper(compilerOptions.paths, {
|
...pathsToModuleNameMapper(compilerOptions.paths, {
|
||||||
prefix: "<rootDir>/",
|
prefix: "<rootDir>/",
|
||||||
}),
|
}),
|
||||||
|
"react-spring/renderprops":
|
||||||
|
"<rootDir>/node_modules/react-spring/renderprops.cjs",
|
||||||
|
"react-spring": "<rootDir>/node_modules/react-spring/web.cjs",
|
||||||
},
|
},
|
||||||
setupFilesAfterEnv: ["<rootDir>/src/setupTests.ts"],
|
setupFilesAfterEnv: ["<rootDir>/src/setupTests.ts"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,10 @@
|
|||||||
.bp3-navbar {
|
.bp3-navbar {
|
||||||
transition: 0.3s;
|
transition: 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#uploadingStatusButton {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#mainContainer.bp3-dark {
|
#mainContainer.bp3-dark {
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Classes,
|
Classes,
|
||||||
IBreadcrumbProps,
|
IBreadcrumbProps,
|
||||||
|
Icon,
|
||||||
Menu,
|
Menu,
|
||||||
Navbar,
|
Navbar,
|
||||||
Popover,
|
Popover,
|
||||||
|
Spinner,
|
||||||
} from "@blueprintjs/core";
|
} from "@blueprintjs/core";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -24,10 +26,13 @@ import { toggleDarkMode } from "~redux/localSettings/actions";
|
|||||||
import { IAppState } from "~redux/reducers";
|
import { IAppState } from "~redux/reducers";
|
||||||
import { logoutUser } from "~redux/user/actions";
|
import { logoutUser } from "~redux/user/actions";
|
||||||
|
|
||||||
interface IHomeProps extends RouteComponentProps {
|
export interface IHomeProps extends RouteComponentProps {
|
||||||
allDocs: { [key: number]: IDocumentJSON };
|
allDocs: { [key: number]: IDocumentJSON };
|
||||||
user: IUserJSON | null;
|
user: IUserJSON | null;
|
||||||
|
|
||||||
|
fetching: boolean;
|
||||||
|
uploading: boolean;
|
||||||
|
|
||||||
darkMode: boolean;
|
darkMode: boolean;
|
||||||
|
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
@@ -72,6 +77,13 @@ export class HomeComponent extends React.PureComponent<IHomeProps> {
|
|||||||
<Breadcrumbs items={breadcrumbs} />
|
<Breadcrumbs items={breadcrumbs} />
|
||||||
</Navbar.Group>
|
</Navbar.Group>
|
||||||
<Navbar.Group align={Alignment.RIGHT}>
|
<Navbar.Group align={Alignment.RIGHT}>
|
||||||
|
<Button id="uploadingStatusButton">
|
||||||
|
{this.props.uploading ? (
|
||||||
|
<Spinner size={20} />
|
||||||
|
) : (
|
||||||
|
<Icon icon="saved" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
<Popover
|
<Popover
|
||||||
target={
|
target={
|
||||||
<Button id="userButton">
|
<Button id="userButton">
|
||||||
@@ -161,6 +173,8 @@ function mapStateToProps(state: IAppState) {
|
|||||||
allDocs: state.docs.all,
|
allDocs: state.docs.all,
|
||||||
user: state.user.user,
|
user: state.user.user,
|
||||||
darkMode: state.localSettings.darkMode,
|
darkMode: state.localSettings.darkMode,
|
||||||
|
fetching: state.docs.fetching,
|
||||||
|
uploading: state.docs.uploading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
frontend/src/Home/tests/Home.test.tsx
Normal file
52
frontend/src/Home/tests/Home.test.tsx
Normal file
@@ -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("<Home />", () => {
|
||||||
|
it("should show a spinner when uploading", () => {
|
||||||
|
const wrapper = shallow(
|
||||||
|
<HomeComponent {...defaultHomeProps} uploading={true} />,
|
||||||
|
);
|
||||||
|
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(
|
||||||
|
<HomeComponent {...defaultHomeProps} uploading={false} />,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
wrapper.find("#uploadingStatusButton").find(Spinner),
|
||||||
|
).toHaveLength(0);
|
||||||
|
expect(wrapper.find("#uploadingStatusButton").find(Icon)).toHaveLength(
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -7,6 +7,7 @@ import { DocsAction, DocsTypes } from "./actions";
|
|||||||
export interface IDocsState {
|
export interface IDocsState {
|
||||||
all: { [key: number]: IDocumentJSON };
|
all: { [key: number]: IDocumentJSON };
|
||||||
fetching: boolean;
|
fetching: boolean;
|
||||||
|
uploading: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
spinner: boolean;
|
spinner: boolean;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ export interface IDocsState {
|
|||||||
const defaultDocsState: IDocsState = {
|
const defaultDocsState: IDocsState = {
|
||||||
all: null,
|
all: null,
|
||||||
fetching: false,
|
fetching: false,
|
||||||
|
uploading: false,
|
||||||
error: null,
|
error: null,
|
||||||
spinner: false,
|
spinner: false,
|
||||||
newDocumentID: null,
|
newDocumentID: null,
|
||||||
@@ -64,11 +66,17 @@ export const docsReducer: Reducer<IDocsState, DocsAction> = (
|
|||||||
all[deletedDocument.id] = deletedDocument;
|
all[deletedDocument.id] = deletedDocument;
|
||||||
return { ...state, deletedDocument: null, all };
|
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: {
|
case DocsTypes.DOC_UPDATE_SUCCESS: {
|
||||||
const all = { ...state.all };
|
const all = { ...state.all };
|
||||||
const doc = action.payload.doc;
|
const doc = action.payload.doc;
|
||||||
all[doc.id] = doc;
|
all[doc.id] = doc;
|
||||||
return { ...state, all };
|
return { ...state, all, uploading: false };
|
||||||
}
|
}
|
||||||
case DocsTypes.DOCS_FETCH_FAIL:
|
case DocsTypes.DOCS_FETCH_FAIL:
|
||||||
return { ...defaultDocsState, ...action.payload };
|
return { ...defaultDocsState, ...action.payload };
|
||||||
|
|||||||
Reference in New Issue
Block a user