Skip to content

Commit 345dbbd

Browse files
authored
fix: Fixes issue where user is unable to navigate to new project screen (microsoft#629)
Resolves the issue where user is unable to navigate to new project screen from the homepage. This also addresses other strange behavior related to routing in the app. -User being navigated to the homepage after creating a connection -Refresh application button not working Resolves AB#17258, 17263, 17058
1 parent 886d5e5 commit 345dbbd

File tree

9 files changed

+93
-75
lines changed

9 files changed

+93
-75
lines changed

.env

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# react-scripts build use this to generate the right path for assets
1+
# react-scripts build use this to generate the right path for assets
22
# relative to index.html
33
# without it, you'll see error like this
4-
# Failed to load resource: net::ERR_FILE_NOT_FOUND /favicon.ico:1
5-
PUBLIC_URL=.
4+
# Failed to load resource: net::ERR_FILE_NOT_FOUND /favicon.ico:1
5+
PUBLIC_URL=.

config/webpack.common.js

+28-25
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
const path = require("path");
22

33
module.exports = {
4-
target: "electron-main",
5-
entry: "./src/electron/main.ts",
6-
module: {
7-
rules: [
8-
{
9-
test: /\.ts?$/,
10-
use: [{
11-
loader: "ts-loader",
12-
options: {
13-
compilerOptions: {
14-
noEmit: false
4+
node: {
5+
__dirname: false,
6+
},
7+
target: "electron-main",
8+
entry: "./src/electron/main.ts",
9+
module: {
10+
rules: [
11+
{
12+
test: /\.ts?$/,
13+
use: [{
14+
loader: "ts-loader",
15+
options: {
16+
compilerOptions: {
17+
noEmit: false
18+
}
19+
}
20+
}],
21+
exclude: /node_modules/
1522
}
16-
}
17-
}],
18-
exclude: /node_modules/
19-
}
20-
]
21-
},
22-
resolve: {
23-
extensions: [".ts", ".js"]
24-
},
25-
output: {
26-
filename: "main.js",
27-
path: path.resolve(__dirname, "../build")
28-
}
29-
};
23+
]
24+
},
25+
resolve: {
26+
extensions: [".ts", ".js"]
27+
},
28+
output: {
29+
filename: "main.js",
30+
path: path.resolve(__dirname, "../build")
31+
}
32+
};

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
{
22
"name": "vott",
33
"version": "2.0.0-preview.1",
4+
"author": {
5+
"name": "Microsoft",
6+
"url": "https://github.com/Microsoft/VoTT"
7+
},
48
"description": "Visual Object Tagging Tool (VoTT) - an annotation and labeling tool for images and video.",
59
"homepage": "https://github.com/Microsoft/VoTT",
610
"repository": {

src/App.test.tsx

+23-8
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
import React from "react";
2-
import ReactDOM from "react-dom";
32
import App from "./App";
43
import { Provider } from "react-redux";
54
import createReduxStore from "./redux/store/store";
65
import initialState from "./redux/store/initialState";
76
import { IApplicationState } from "./models//applicationState";
7+
import { mount } from "enzyme";
8+
import { HashRouter } from "react-router-dom";
9+
import { KeyboardManager } from "./react/components/common/keyboardManager/keyboardManager";
10+
import { ErrorHandler } from "./react/components/common/errorHandler/errorHandler";
811

9-
it("renders without crashing", () => {
12+
describe("App Component", () => {
1013
const defaultState: IApplicationState = initialState;
1114
const store = createReduxStore(defaultState);
12-
const div = document.createElement("div");
1315

14-
ReactDOM.render(
15-
<Provider store={store}>
16-
<App />
17-
</Provider>, div);
18-
ReactDOM.unmountComponentAtNode(div);
16+
function createComponent() {
17+
return mount(
18+
<Provider store={store}>
19+
<App />
20+
</Provider>,
21+
);
22+
}
23+
24+
it("renders without crashing", () => {
25+
createComponent();
26+
});
27+
28+
it("renders required top level components", () => {
29+
const wrapper = createComponent();
30+
expect(wrapper.find(HashRouter).exists()).toBe(true);
31+
expect(wrapper.find(KeyboardManager).exists()).toEqual(true);
32+
expect(wrapper.find(ErrorHandler).exists()).toEqual(true);
33+
});
1934
});

src/App.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { Fragment } from "react";
22
import { connect } from "react-redux";
3-
import { BrowserRouter as Router } from "react-router-dom";
3+
import { HashRouter as Router } from "react-router-dom";
44
import { ToastContainer } from "react-toastify";
55
import Navbar from "./react/components/shell/navbar";
66
import Sidebar from "./react/components/shell/sidebar";
77
import MainContentRouter from "./react/components/shell/mainContentRouter";
8-
import { IAppError, IApplicationState, IProject, AppError, ErrorCode } from "./models/applicationState";
8+
import { IAppError, IApplicationState, IProject, ErrorCode } from "./models/applicationState";
99
import "./App.scss";
1010
import "react-toastify/dist/ReactToastify.css";
1111
import IAppErrorActions, * as appErrorActions from "./redux/actions/appErrorActions";

src/electron/main.ts

+7-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { app, ipcMain, BrowserWindow, dialog, BrowserWindowConstructorOptions, Menu } from "electron";
1+
import { app, ipcMain, BrowserWindow, BrowserWindowConstructorOptions, Menu } from "electron";
22
import { IpcMainProxy } from "./common/ipcMainProxy";
33
import LocalFileSystem from "./providers/storage/localFileSystem";
44

@@ -8,28 +8,21 @@ let mainWindow: BrowserWindow;
88
let ipcMainProxy: IpcMainProxy;
99

1010
function createWindow() {
11-
// and load the index.html of the app.
12-
1311
const windowOptions: BrowserWindowConstructorOptions = {
1412
width: 1024,
1513
height: 768,
1614
};
17-
// Create the browser window.
1815

16+
const staticUrl = process.env.ELECTRON_START_URL || `file:///${__dirname}/index.html`;
1917
if (process.env.ELECTRON_START_URL) {
20-
// Disable web security to support loading in local file system resources
21-
// TODO: Look into defined local security policy
2218
windowOptions.webPreferences = {
2319
webSecurity: false,
2420
};
25-
mainWindow = new BrowserWindow(windowOptions);
26-
mainWindow.loadURL(process.env.ELECTRON_START_URL);
27-
} else {
28-
// When running in production mode or with static files use loadFile api vs. loadUrl api.
29-
mainWindow = new BrowserWindow(windowOptions);
30-
mainWindow.loadFile("build/index.html");
3121
}
3222

23+
mainWindow = new BrowserWindow(windowOptions);
24+
mainWindow.loadURL(staticUrl);
25+
3326
// Emitted when the window is closed.
3427
mainWindow.on("closed", () => {
3528
// Dereference the window object, usually you would store windows
@@ -53,12 +46,8 @@ function onReloadApp() {
5346
return true;
5447
}
5548

56-
function onToggleDevTools(sender: any, show: boolean) {
57-
if (show) {
58-
mainWindow.webContents.openDevTools();
59-
} else {
60-
mainWindow.webContents.closeDevTools();
61-
}
49+
function onToggleDevTools() {
50+
mainWindow.webContents.toggleDevTools();
6251
}
6352

6453
/**

src/react/components/pages/homepage/homePage.test.tsx

+13-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import createReduxStore from "../../../../redux/store/store";
1010
import ProjectService from "../../../../services/projectService";
1111
import CondensedList from "../../common/condensedList/condensedList";
1212
import FilePicker, { IFilePickerProps } from "../../common/filePicker/filePicker";
13-
import HomePage, { IHomepageProps, IHomepageState } from "./homePage";
13+
import HomePage, { IHomePageProps, IHomePageState } from "./homePage";
1414

1515
jest.mock("../../common/cloudFilePicker/cloudFilePicker");
1616
import { CloudFilePicker, ICloudFilePickerProps } from "../../common/cloudFilePicker/cloudFilePicker";
@@ -19,13 +19,13 @@ jest.mock("../../../../services/projectService");
1919

2020
describe("Homepage Component", () => {
2121
let store: Store<IApplicationState> = null;
22-
let props: IHomepageProps = null;
22+
let props: IHomePageProps = null;
2323
let wrapper: ReactWrapper = null;
2424
let deleteProjectSpy: jest.SpyInstance = null;
2525
let closeProjectSpy: jest.SpyInstance = null;
2626
const recentProjects = MockFactory.createTestProjects(2);
2727

28-
function createComponent(store, props: IHomepageProps): ReactWrapper {
28+
function createComponent(store, props: IHomePageProps): ReactWrapper {
2929
return mount(
3030
<Provider store={store}>
3131
<Router>
@@ -79,7 +79,7 @@ describe("Homepage Component", () => {
7979

8080
it("should render a list of recent projects", () => {
8181
expect(wrapper).not.toBeNull();
82-
const homePage = wrapper.find(HomePage).childAt(0) as ReactWrapper<IHomepageProps>;
82+
const homePage = wrapper.find(HomePage).childAt(0) as ReactWrapper<IHomePageProps>;
8383
if (homePage.props().recentProjects && homePage.props().recentProjects.length > 0) {
8484
expect(wrapper.find(CondensedList).exists()).toBeTruthy();
8585
}
@@ -99,7 +99,7 @@ describe("Homepage Component", () => {
9999
await MockFactory.flushUi();
100100
wrapper.update();
101101

102-
const homePage = wrapper.find(HomePage).childAt(0) as ReactWrapper<IHomepageProps>;
102+
const homePage = wrapper.find(HomePage).childAt(0) as ReactWrapper<IHomePageProps>;
103103

104104
expect(deleteProjectSpy).toBeCalledWith(recentProjects[0]);
105105
expect(homePage.props().recentProjects.length).toEqual(recentProjects.length - 1);
@@ -148,13 +148,18 @@ describe("Homepage Component", () => {
148148
});
149149

150150
it("closes any open project and navigates to the new project screen", () => {
151-
const homepage = wrapper.find(HomePage).childAt(0) as ReactWrapper<IHomepageProps, IHomepageState>;
152-
homepage.find("a.new-project").simulate("click");
151+
const eventMock = {
152+
preventDefault: jest.fn(),
153+
};
154+
155+
const homepage = wrapper.find(HomePage).childAt(0) as ReactWrapper<IHomePageProps, IHomePageState>;
156+
homepage.find("a.new-project").simulate("click", eventMock);
153157
expect(closeProjectSpy).toBeCalled();
154158
expect(homepage.props().history.push).toBeCalledWith("/projects/create");
159+
expect(eventMock.preventDefault).toBeCalled();
155160
});
156161

157-
function createProps(): IHomepageProps {
162+
function createProps(): IHomePageProps {
158163
return {
159164
recentProjects: [],
160165
connections: MockFactory.createTestConnections(),

src/react/components/pages/homepage/homePage.tsx

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React from "react";
1+
import React, { SyntheticEvent } from "react";
22
import { connect } from "react-redux";
3-
import { Link, RouteComponentProps } from "react-router-dom";
3+
import { RouteComponentProps } from "react-router-dom";
44
import { bindActionCreators } from "redux";
55
import { strings } from "../../../../common/strings";
66
import IProjectActions, * as projectActions from "../../../../redux/actions/projectActions";
@@ -13,16 +13,16 @@ import RecentProjectItem from "./recentProjectItem";
1313
import { constants } from "../../../../common/constants";
1414
import {
1515
IApplicationState, IConnection, IProject,
16-
ErrorCode, AppError, IAppError,
16+
ErrorCode, AppError,
1717
} from "../../../../models/applicationState";
1818

19-
export interface IHomepageProps extends RouteComponentProps, React.Props<HomePage> {
19+
export interface IHomePageProps extends RouteComponentProps, React.Props<HomePage> {
2020
recentProjects: IProject[];
2121
connections: IConnection[];
2222
actions: IProjectActions;
2323
}
2424

25-
export interface IHomepageState {
25+
export interface IHomePageState {
2626
cloudPickerOpen: boolean;
2727
}
2828

@@ -40,8 +40,8 @@ function mapDispatchToProps(dispatch) {
4040
}
4141

4242
@connect(mapStateToProps, mapDispatchToProps)
43-
export default class HomePage extends React.Component<IHomepageProps> {
44-
public state: IHomepageState = {
43+
export default class HomePage extends React.Component<IHomePageProps, IHomePageState> {
44+
public state: IHomePageState = {
4545
cloudPickerOpen: false,
4646
};
4747

@@ -103,9 +103,11 @@ export default class HomePage extends React.Component<IHomepageProps> {
103103
);
104104
}
105105

106-
private createNewProject = () => {
106+
private createNewProject = (e: SyntheticEvent) => {
107107
this.props.actions.closeProject();
108108
this.props.history.push("/projects/create");
109+
110+
e.preventDefault();
109111
}
110112

111113
private handleOpenCloudProjectClick = () => {

src/react/components/shell/mainContentRouter.test.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { AnyAction, Store } from "redux";
88
import createReduxStore from "../../../redux/store/store";
99

1010
import MainContentRouter from "./mainContentRouter";
11-
import HomePage, { IHomepageProps } from "./../pages/homepage/homePage";
11+
import HomePage, { IHomePageProps } from "./../pages/homepage/homePage";
1212
import SettingsPage from "./../pages/appSettings/appSettingsPage";
1313
import ConnectionsPage from "./../pages/connections/connectionsPage";
1414
import ProfilePage from "./../pages/profileSettingsPage";
@@ -17,7 +17,7 @@ import { IApplicationState } from "./../../../models/applicationState";
1717
describe("Main Content Router", () => {
1818
const badRoute: string = "/index.html";
1919

20-
function createComponent(routerContext, route, store, props: IHomepageProps): ReactWrapper {
20+
function createComponent(routerContext, route, store, props: IHomePageProps): ReactWrapper {
2121
return mount(
2222
<Provider store={store}>
2323
<Router location={route} context={routerContext}>
@@ -51,7 +51,7 @@ describe("Main Content Router", () => {
5151

5252
const homePage = wrapper.find(HomePage);
5353
expect(homePage.find(".app-homepage").exists()).toEqual(true);
54-
});
54+
});
5555
});
5656

5757
function createStore(state?: IApplicationState): Store<any, AnyAction> {

0 commit comments

Comments
 (0)