Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add boundary for connection #83

Merged
merged 4 commits into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ module.exports = {
},
setupFilesAfterEnv: ["<rootDir>src/setupTests.ts"],
snapshotSerializers: ["enzyme-to-json/serializer"],
moduleNameMapper: {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|css)$":
"identity-obj-proxy"
},
testEnvironment: "jsdom"
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"graphql-tools": "^4.0.5",
"html-webpack-plugin": "^3.2.0",
"husky": "^3.0.4",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.9.0",
"jest-styled-components": "^6.3.4",
"lint-staged": "^9.2.4",
Expand All @@ -120,6 +121,7 @@
"terser-webpack-plugin": "^1.4.1",
"ts-jest": "^24.1.0",
"typescript": "^3.5.2",
"url-loader": "^2.3.0",
"urql": "^1.4.1",
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5"
Expand Down
6 changes: 6 additions & 0 deletions src/definitions.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
declare module "*.svg";
declare module "nanoid";

declare namespace NodeJS {
interface Global {
matchMedia: jest.Mock;
}
}
47 changes: 47 additions & 0 deletions src/panel/App.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
jest.mock("./context/Devtools.tsx", () => {
return {
...jest.requireActual("./context/Devtools.tsx"),
useDevtoolsContext: jest.fn()
};
});
import React from "react";
import { shallow } from "enzyme";
import { mocked } from "ts-jest/utils";
import { App, AppRoutes } from "./App";
import { useDevtoolsContext } from "./context";

describe("App", () => {
describe("on mount", () => {
it("matches snapshot", () => {
expect(shallow(<App />)).toMatchSnapshot();
});
});
});

describe("App routes", () => {
describe("on mount", () => {
describe("on connected", () => {
beforeEach(() => {
mocked(useDevtoolsContext).mockReturnValue({
clientConnected: true
} as any);
});

it("matches snapshot", () => {
expect(shallow(<AppRoutes />)).toMatchSnapshot();
});
});

describe("on disconnected", () => {
beforeEach(() => {
mocked(useDevtoolsContext).mockReturnValue({
clientConnected: false
} as any);
});

it("matches snapshot", () => {
expect(shallow(<AppRoutes />)).toMatchSnapshot();
});
});
});
});
52 changes: 33 additions & 19 deletions src/panel/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import "./App.css";
import React from "react";
import React, { FC } from "react";
import { HashRouter, Route, Redirect } from "react-router-dom";
import { ThemeProvider } from "styled-components";
import { Events } from "./events";
import { Explorer } from "./explorer";
import { Disconnected } from "./disconnected";
import { Navigation } from "./Navigation";
import { Request } from "./request/Request";
import { theme } from "./theme";
Expand All @@ -12,27 +13,40 @@ import {
DevtoolsProvider,
EventsProvider,
RequestProvider,
ExplorerContextProvider
ExplorerContextProvider,
useDevtoolsContext
} from "./context";

export const App = () => {
return (
<DevtoolsProvider>
<ThemeProvider theme={theme}>
<HashRouter>
<EventsProvider>
<Route path="/events" component={Events} />
</EventsProvider>
<RequestProvider>
<Route path="/request" component={Request} />
</RequestProvider>
<ExplorerContextProvider>
<Route path="/explorer" exact component={Explorer} />
</ExplorerContextProvider>
<Route path="/" exact component={() => <Redirect to="/explorer" />} />
<Navigation />
</HashRouter>
</ThemeProvider>
</DevtoolsProvider>
<ThemeProvider theme={theme}>
<DevtoolsProvider>
<AppRoutes />
</DevtoolsProvider>
</ThemeProvider>
);
};

export const AppRoutes: FC = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally prefer having one component in one file, don't know how you perceive this. (more to spark a conversation about this)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally prefer having one component in one file

For the most part we've been taking this approach.

In this case I'm not sure there's much value in breaking it down into separate files - there's only two components and they would be (and were) one if it wasn't for the use of context/hooks.

const { clientConnected } = useDevtoolsContext();

if (!clientConnected) {
return <Disconnected />;
}

return (
<HashRouter>
<EventsProvider>
<Route path="/events" component={Events} />
</EventsProvider>
<RequestProvider>
<Route path="/request" component={Request} />
</RequestProvider>
<ExplorerContextProvider>
<Route path="/explorer" exact component={Explorer} />
</ExplorerContextProvider>
<Route path="/" exact component={() => <Redirect to="/explorer" />} />
<Navigation />
</HashRouter>
);
};
84 changes: 84 additions & 0 deletions src/panel/__snapshots__/App.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`App on mount matches snapshot 1`] = `
<ThemeProvider
theme={
Object {
"blue": Object {
"-1": "#0084D1",
"-2": "#0067A3",
"0": "#00A1FF",
},
"border": "#CAE3F212",
"dark": Object {
"+1": "#2e3240",
"+2": "#3e4270",
"-1": "#15161c",
"-2": "#12141a",
"-3": "#11151c",
"0": "#1C1E26",
},
"green": Object {
"0": "#2DAF7E",
},
"grey": Object {
"+1": "#A5B0B7",
"+2": "#b4bfd1",
"-1": "#607E90",
"0": "#AAADB1",
},
"orange": Object {
"0": "#EB9028",
},
"pink": Object {
"+2": "#EC3E66",
"0": "#B877DB",
},
"purple": Object {
"+1": "#8899E3",
"0": "#7776d2",
},
"red": Object {
"+1": "#EC8275",
"0": "#EE6352",
},
}
}
>
<DevtoolsProvider>
<AppRoutes />
</DevtoolsProvider>
</ThemeProvider>
`;

exports[`App routes on mount on connected matches snapshot 1`] = `
<HashRouter>
<EventsProvider>
<Route
component={[Function]}
path="/events"
/>
</EventsProvider>
<RequestProvider>
<Route
component={[Function]}
path="/request"
/>
</RequestProvider>
<ExplorerContextProvider>
<Route
component={[Function]}
exact={true}
path="/explorer"
/>
</ExplorerContextProvider>
<Route
component={[Function]}
exact={true}
path="/"
/>
<Navigation />
</HashRouter>
`;

exports[`App routes on mount on disconnected matches snapshot 1`] = `<Disconnected />`;
5 changes: 4 additions & 1 deletion src/panel/context/Devtools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import React, {
FC,
useRef,
useCallback,
useState
useState,
useContext
} from "react";
import { DevtoolsPanelConnectionName, PanelOutgoingMessage } from "../../types";

Expand All @@ -19,6 +20,8 @@ interface DevtoolsContextType {

export const DevtoolsContext = createContext<DevtoolsContextType>(null as any);

export const useDevtoolsContext = () => useContext(DevtoolsContext);

export const DevtoolsProvider: FC = ({ children }) => {
const [clientConnected, setClientConnected] = useState(false);
const connection = useRef(
Expand Down
7 changes: 0 additions & 7 deletions src/panel/context/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,6 @@ export const EventsProvider: FC = ({ children }) => {
/** Handle incoming events */
useEffect(() => {
return addMessageHandler(msg => {
// When using an effect on client connection true --> false
// something in the lifecycle seems to get lost... That's why
// I opted for this solution.
if (msg.type === "disconnect") {
setRawEvents(() => []);
}

if (!["operation", "response", "error"].includes(msg.type)) {
return;
}
Expand Down
56 changes: 29 additions & 27 deletions src/panel/context/Request.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,45 @@ export const RequestContext = createContext<RequestContextValue>(null as any);

export const RequestProvider: FC = ({ children }) => {
const { sendMessage, addMessageHandler } = useContext(DevtoolsContext);
const [fetching, setFetching] = useState(false);
const [state, setState] = useState<{
fetching: boolean;
response?: object;
error?: object;
}>({ fetching: false, response: undefined, error: undefined });
const [query, setQuery] = useState<string>();
const [response, setResponse] = useState<object | undefined>();
const [error, setError] = useState<object | undefined>();
const [schema, setSchema] = useState<GraphQLSchema>();

const execute = useCallback(() => {
setFetching(true);
setResponse(undefined);
setError(undefined);
setState({
fetching: true,
response: undefined,
error: undefined
});
sendMessage({ type: "request", query: query || "" });
}, [query, sendMessage]);

// Listen for response for devtools
useEffect(() => {
return addMessageHandler(e => {
if (
!fetching ||
e.type === "operation" ||
e.type === "init" ||
e.type === "disconnect" ||
(e.data.operation.context.meta as any).source !== "Devtools"
) {
return;
}

if (e.data.error !== undefined) {
setError(e.data.error);
} else {
setResponse(e.data.data);
}
setState(s => {
if (
!s.fetching ||
andyrichardson marked this conversation as resolved.
Show resolved Hide resolved
e.type === "operation" ||
e.type === "init" ||
e.type === "disconnect" ||
(e.data.operation.context.meta as any).source !== "Devtools"
) {
return s;
}

setFetching(false);
return {
fetching: false,
error: e.data.error,
response: e.data.data
};
});
});
}, [fetching, addMessageHandler]);
}, [addMessageHandler]);

// Get schema
useEffect(() => {
Expand Down Expand Up @@ -88,13 +92,11 @@ export const RequestProvider: FC = ({ children }) => {
() => ({
query,
setQuery,
fetching,
response,
error,
...state,
execute,
schema
}),
[query, fetching, response, error, execute, schema]
[query, state, execute, schema]
);

return <RequestContext.Provider value={value} children={children} />;
Expand Down
9 changes: 9 additions & 0 deletions src/panel/disconnected/Disconnected.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from "react";
import { shallow } from "enzyme";
import { Disconnected } from "./Disconnected";

describe("on mount", () => {
it("matches snapshot", () => {
expect(shallow(<Disconnected />)).toMatchSnapshot();
});
});
Loading