Skip to content

Commit e2a2b91

Browse files
authored
update swapi to use GraphiQL 5 with Monaco editor and React 19 (#247)
2 parents b64c2d4 + eeea91b commit e2a2b91

File tree

3 files changed

+166
-122
lines changed

3 files changed

+166
-122
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ npm-debug.log
77
/cache/data.json
88

99
# Local Netlify folder
10-
.netlify
10+
.netlify/
11+
.idea/

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"build": "rimraf lib && babel src --ignore __tests__,public,handler --out-dir lib/",
4040
"download": "babel-node scripts/download.js cache/data.json",
4141
"serve-public": "babel-node scripts/serve-public",
42-
"prettier": "prettier --write 'src/**/*.js'",
42+
"prettier": "prettier --write 'src/**/*.{js,html}'",
4343
"print-schema": "babel-node scripts/print-schema.js",
4444
"store-schema": "babel-node scripts/store-schema.js"
4545
},

public/index.html

Lines changed: 163 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,172 @@
11
<!doctype html>
22
<html lang="en">
3-
<head>
4-
<meta charset="utf-8" />
5-
<meta name="robots" content="noindex" />
6-
<meta name="referrer" content="origin" />
7-
<meta name="viewport" content="width=device-width, initial-scale=1" />
8-
<title>SWAPI GraphQL API</title>
9-
<style>
10-
body {
11-
height: 100vh;
12-
margin: 0;
13-
overflow: hidden;
14-
}
15-
#splash {
16-
color: #333;
17-
display: flex;
18-
flex-direction: column;
19-
font-family: system, -apple-system, "San Francisco",
20-
".SFNSDisplay-Regular", "Segoe UI", Segoe, "Segoe WP",
21-
"Helvetica Neue", helvetica, "Lucida Grande", arial, sans-serif;
22-
height: 100vh;
23-
justify-content: center;
24-
text-align: center;
25-
}
26-
</style>
27-
<link rel="icon" href="favicon.ico" />
28-
<link
29-
type="text/css"
30-
href="//unpkg.com/graphiql@4/graphiql.min.css"
31-
rel="stylesheet"
32-
/>
33-
<link
34-
type="text/css"
35-
href="//unpkg.com/@graphiql/plugin-explorer@4/dist/style.css"
36-
rel="stylesheet"
37-
/>
38-
</head>
39-
<body>
40-
<div id="splash">Loading&hellip;</div>
41-
<script src="//unpkg.com/[email protected]/umd/react.production.min.js"></script>
42-
<script src="//unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
43-
<script src="//unpkg.com/graphiql@4/graphiql.min.js"></script>
44-
<script src="//unpkg.com/@graphiql/plugin-explorer@4/dist/index.umd.js"></script>
45-
<script>
46-
// Parse the search string to get url parameters.
47-
var search = window.location.search;
48-
var parameters = {};
49-
search
50-
.substr(1)
51-
.split("&")
52-
.forEach(function (entry) {
53-
var eq = entry.indexOf("=");
54-
if (eq >= 0) {
55-
parameters[decodeURIComponent(entry.slice(0, eq))] =
56-
decodeURIComponent(entry.slice(eq + 1));
57-
}
58-
});
59-
60-
// if variables was provided, try to format it.
61-
if (parameters.variables) {
62-
try {
63-
parameters.variables = JSON.stringify(
64-
JSON.parse(parameters.variables),
65-
null,
66-
2
67-
);
68-
} catch (e) {
69-
// Do nothing, we want to display the invalid JSON as a string, rather
70-
// than present an error.
71-
}
72-
}
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="robots" content="noindex" />
6+
<meta name="referrer" content="origin" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1" />
8+
<title>SWAPI GraphQL API</title>
9+
<style>
10+
body {
11+
margin: 0;
12+
}
7313

74-
// When the query and variables string is edited, update the URL bar so
75-
// that it can be easily shared
76-
function onEditQuery(newQuery) {
77-
parameters.query = newQuery;
78-
updateURL();
79-
}
80-
function onEditVariables(newVariables) {
81-
parameters.variables = newVariables;
82-
updateURL();
14+
#graphiql {
15+
height: 100dvh;
16+
}
17+
18+
.loading {
19+
height: 100%;
20+
display: flex;
21+
align-items: center;
22+
justify-content: center;
23+
font-size: 4rem;
24+
}
25+
</style>
26+
<link rel="icon" href="favicon.ico" />
27+
<link rel="stylesheet" href="https://esm.sh/graphiql/dist/style.css" />
28+
<link
29+
rel="stylesheet"
30+
href="https://esm.sh/@graphiql/plugin-explorer/dist/style.css"
31+
/>
32+
<!-- Note: the ?standalone flag bundles the module along with all of its `dependencies`, excluding `peerDependencies`, into a single JavaScript file. -->
33+
<!-- `@emotion/is-prop-valid` is a shim to remove the console error `module "@emotion /is-prop-valid" not found`. Upstream issue: https://github.com/motiondivision/motion/issues/3126 -->
34+
<script type="importmap">
35+
{
36+
"imports": {
37+
"react": "https://esm.sh/[email protected]",
38+
"react/jsx-runtime": "https://esm.sh/[email protected]/jsx-runtime",
39+
40+
"react-dom": "https://esm.sh/[email protected]",
41+
"react-dom/client": "https://esm.sh/[email protected]/client",
42+
"@emotion/is-prop-valid": "data:text/javascript,",
43+
44+
"graphiql": "https://esm.sh/graphiql?standalone&external=react,react-dom,@graphiql/react,graphql",
45+
"@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer?standalone&external=react,@graphiql/react,graphql",
46+
"@graphiql/react": "https://esm.sh/@graphiql/react?standalone&external=react,react-dom,graphql,@emotion/is-prop-valid",
47+
48+
"@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit?standalone&external=graphql",
49+
"graphql": "https://esm.sh/[email protected]"
8350
}
84-
function onEditOperationName(newOperationName) {
85-
parameters.operationName = newOperationName;
86-
updateURL();
51+
}
52+
</script>
53+
<script type="module">
54+
// Import React and ReactDOM
55+
import React from 'react';
56+
import ReactDOM from 'react-dom/client';
57+
// Import GraphiQL and the Explorer plugin
58+
import { GraphiQL, HISTORY_PLUGIN } from 'graphiql';
59+
import { createGraphiQLFetcher } from '@graphiql/toolkit';
60+
import { explorerPlugin } from '@graphiql/plugin-explorer';
61+
62+
import createJSONWorker from 'https://esm.sh/monaco-editor/esm/vs/language/json/json.worker.js?worker';
63+
import createGraphQLWorker from 'https://esm.sh/monaco-graphql/esm/graphql.worker.js?worker';
64+
import createEditorWorker from 'https://esm.sh/monaco-editor/esm/vs/editor/editor.worker.js?worker';
65+
66+
globalThis.MonacoEnvironment = {
67+
getWorker(_workerId, label) {
68+
console.info('MonacoEnvironment.getWorker', { label });
69+
switch (label) {
70+
case 'json':
71+
return createJSONWorker();
72+
case 'graphql':
73+
return createGraphQLWorker();
8774
}
88-
function updateURL() {
89-
var newSearch =
90-
"?" +
91-
Object.keys(parameters)
92-
.filter(function (key) {
93-
return Boolean(parameters[key]);
94-
})
95-
.map(function (key) {
96-
return (
97-
encodeURIComponent(key) +
98-
"=" +
99-
encodeURIComponent(parameters[key])
100-
);
101-
})
102-
.join("&");
103-
history.replaceState(null, null, newSearch);
75+
return createEditorWorker();
76+
},
77+
};
78+
79+
// Parse the search string to get url parameters.
80+
var search = window.location.search;
81+
var parameters = {};
82+
search
83+
.substr(1)
84+
.split('&')
85+
.forEach(function(entry) {
86+
var eq = entry.indexOf('=');
87+
if (eq >= 0) {
88+
parameters[decodeURIComponent(entry.slice(0, eq))] =
89+
decodeURIComponent(entry.slice(eq + 1));
10490
}
91+
});
10592

106-
const fetcher = GraphiQL.createFetcher({
107-
url:
108-
parameters.fetchURL ||
109-
"/graphql",
110-
});
111-
112-
const explorerPlugin = GraphiQLPluginExplorer.explorerPlugin();
113-
// Render <GraphiQL /> into the body.
114-
ReactDOM.render(
115-
React.createElement(GraphiQL, {
116-
fetcher: fetcher,
117-
query: parameters.query,
118-
variables: parameters.variables,
119-
operationName: parameters.operationName,
120-
onEditQuery: onEditQuery,
121-
onEditVariables: onEditVariables,
122-
onEditOperationName: onEditOperationName,
123-
plugins: [explorerPlugin]
124-
}),
125-
document.body
93+
// if variables was provided, try to format it.
94+
if (parameters.variables) {
95+
try {
96+
parameters.variables = JSON.stringify(
97+
JSON.parse(parameters.variables),
98+
null,
99+
2,
126100
);
127-
</script>
128-
</body>
101+
} catch (e) {
102+
// Do nothing, we want to display the invalid JSON as a string, rather
103+
// than present an error.
104+
}
105+
}
106+
107+
// When the query and variables string is edited, update the URL bar so
108+
// that it can be easily shared
109+
function onEditQuery(newQuery) {
110+
parameters.query = newQuery;
111+
updateURL();
112+
}
113+
114+
function onEditVariables(newVariables) {
115+
parameters.variables = newVariables;
116+
updateURL();
117+
}
118+
119+
function onEditOperationName(newOperationName) {
120+
parameters.operationName = newOperationName;
121+
updateURL();
122+
}
123+
124+
function updateURL() {
125+
var newSearch =
126+
'?' +
127+
Object.keys(parameters)
128+
.filter(function(key) {
129+
return Boolean(parameters[key]);
130+
})
131+
.map(function(key) {
132+
return (
133+
encodeURIComponent(key) +
134+
'=' +
135+
encodeURIComponent(parameters[key])
136+
);
137+
})
138+
.join('&');
139+
history.replaceState(null, null, newSearch);
140+
}
141+
142+
const fetcher = createGraphiQLFetcher({
143+
url:
144+
parameters.fetchURL ||
145+
'/graphql',
146+
});
147+
const plugins = [HISTORY_PLUGIN, explorerPlugin()];
148+
149+
function App() {
150+
return React.createElement(GraphiQL, {
151+
fetcher: fetcher,
152+
initialQuery: parameters.query,
153+
initialVariables: parameters.variables,
154+
operationName: parameters.operationName,
155+
onEditQuery: onEditQuery,
156+
onEditVariables: onEditVariables,
157+
onEditOperationName: onEditOperationName,
158+
plugins,
159+
});
160+
}
161+
162+
const container = document.getElementById('graphiql');
163+
const root = ReactDOM.createRoot(container);
164+
root.render(React.createElement(App));
165+
</script>
166+
</head>
167+
<body>
168+
<div id="graphiql">
169+
<div class="loading">Loading…</div>
170+
</div>
171+
</body>
129172
</html>

0 commit comments

Comments
 (0)