├── .gitignore ├── README.md ├── eslint.yaml ├── graphiql.css ├── graphiql.html ├── graphiql.js ├── hapi-plugin-graphiql.js ├── local ├── README.md ├── graphiql.css ├── graphiql.diff └── graphiql.min.js ├── package.json └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | hapi-plugin-graphiql 3 | ==================== 4 | 5 | [HAPI](http://hapijs.com/) plugin for [GraphiQL](https://github.com/graphql/graphiql) integration 6 | 7 |

8 | 9 | 10 |

11 | 12 | 13 | 14 | 15 | Installation 16 | ------------ 17 | 18 | ```shell 19 | $ npm install hapi hapi-plugin-graphiql 20 | ``` 21 | 22 | About 23 | ----- 24 | 25 | This is a small plugin for the [HAPI](http://hapijs.com/) 26 | server framework for seamless integration of 27 | [GraphiQL](https://github.com/graphql/graphiql), an interactive 28 | [GraphQL](http://graphql.org/) user interface. This variant of GraphiQL 29 | especially integrates a username/password based login dialog and reports 30 | network responses in the GraphiQL status bar. In case of its 31 | downstream/local copy of GraphiQL, it also applies some [additional 32 | changes to GraphiQL's internals](local/). 33 | 34 | Usage 35 | ----- 36 | 37 | The following example shows the plugin in action. 38 | The shown options are actually the default ones: 39 | 40 | ```js 41 | server.register({ 42 | register: require("hapi-plugin-graphiql"), 43 | options: { 44 | graphiqlSource: "downstream", 45 | graphiqlGlobals: "", 46 | graphiqlURL: "/api", 47 | graphqlFetchURL: "/api/data/graph", 48 | graphqlFetchOpts: `{ 49 | method: "POST", 50 | headers: { 51 | "Content-Type": "application/json", 52 | "Accept": "application/json" 53 | }, 54 | body: JSON.stringify(params), 55 | credentials: "same-origin" 56 | }`, 57 | loginFetchURL: "/api/auth/login", 58 | loginFetchOpts: `{ 59 | method: "POST", 60 | headers: { 61 | "Content-Type": "application/json" 62 | }, 63 | body: JSON.stringify({ 64 | username: username, 65 | password: password 66 | }), 67 | credentials: "same-origin" 68 | }`, 69 | loginFetchSuccess: "", 70 | loginFetchError: "", 71 | graphqlExample: 72 | "query Example {\n" + 73 | " Session {\n" + 74 | " __typename # schema introspection\n" + 75 | " }\n" + 76 | "}\n", 77 | documentationURL: "", 78 | documentationFile: "" 79 | } 80 | }) 81 | ``` 82 | 83 | This assumes you have a REST-based authentication endpoint 84 | `/api/auth/login` and a GraphQL endpoint `/api/data/graphql`. The 85 | GraphiQL UI then can be accessed under `/api`. The authentication 86 | endpoint is assumed to accept `{ username: "...", password: "..." }` and 87 | sets the authentication token as a HTTP cookie for `/api`. If you your 88 | authentication endpoint returns `{ token: "..." }` and the token has to be passed in an 89 | `Authorization` header as a Bearer token, use the following configuration instead: 90 | 91 | ```js 92 | server.register({ 93 | register: require("hapi-plugin-graphiql"), 94 | options: { 95 | graphiqlSource: "downstream", 96 | graphiqlGlobals: `var token = null;`, 97 | graphiqlURL: "/api", 98 | graphqlFetchURL: "/api/data/graph", 99 | graphqlFetchOpts: `{ 100 | method: "POST", 101 | headers: Object.assign({ 102 | "Content-Type": "application/json", 103 | "Accept": "application/json" 104 | }, token ? { 105 | "Authorization": "Bearer " + token 106 | } : {}), 107 | body: JSON.stringify(params), 108 | credentials: "same-origin" 109 | }`, 110 | loginFetchURL: "/api/auth/login", 111 | loginFetchOpts: `{ 112 | method: "POST", 113 | headers: { 114 | "Content-Type": "application/json" 115 | }, 116 | body: JSON.stringify({ 117 | username: username, 118 | password: password 119 | }), 120 | credentials: "same-origin" 121 | }`, 122 | loginFetchSuccess: `token = JSON.parse(response.text()).token;`, 123 | loginFetchError: `token = null;`, 124 | graphqlExample: 125 | "query Example {\n" + 126 | " Session {\n" + 127 | " __typename # schema introspection\n" + 128 | " }\n" + 129 | "}\n" 130 | } 131 | }) 132 | ``` 133 | 134 | Options 135 | ------- 136 | 137 | The supported configuration options are: 138 | 139 | - `graphiqlSource`:
140 | The source for GraphiQL, either `upstream` (original vendor version) or 141 | `downstream` ([patched local version](local/)). 142 | Default: `"downstream"` 143 | 144 | - `graphiqlGlobals`:
145 | JavaScript code snippet injected into the global scope of the GraphiQL integration. 146 | Usually used for injecting a global variable for use in the other code snippets. 147 | Default: `""` 148 | 149 | - `graphiqlURL`:
150 | The URL under which the GraphiQL UI is registered for `GET` requests. 151 | This can be even the same as the GraphQL URL, as it is usually registered for `POST` requests. 152 | Default: `"/api"` 153 | 154 | - `graphqlFetchURL`:
155 | The URL under which the GraphQL API can be reached via `POST` requests. 156 | Default: `"/api/data/graph"` 157 | 158 | - `graphqlFetchOpts`:
159 | JavaScript code snippet injected into the W3C-Fetch API call as options 160 | for fetching a GraphQL query. 161 | Default: 162 | 163 | ``` 164 | `{ 165 | method: "POST", 166 | headers: { 167 | "Content-Type": "application/json", 168 | "Accept": "application/json" 169 | }, 170 | body: JSON.stringify(params), 171 | credentials: "same-origin" 172 | }` 173 | ``` 174 | 175 | - `loginFetchURL`:
176 | JavaScript code snippet injected into the W3C-Fetch API call as options 177 | for logging in. 178 | Default: `"/api/auth/login"` 179 | 180 | - `loginFetchOpts`:
181 | JavaScript code snippet injected into the W3C-Fetch API call as options 182 | for logging in. 183 | Default: 184 | 185 | ``` 186 | `{ 187 | method: "POST", 188 | headers: { 189 | "Content-Type": "application/json" 190 | }, 191 | body: JSON.stringify({ 192 | username: username, 193 | password: password 194 | }), 195 | credentials: "same-origin" 196 | }` 197 | ``` 198 | 199 | - `loginFetchSuccess`:
200 | JavaScript code snippet injected into the success handler of the W3C-Fetch API call 201 | for loggin in. 202 | Default: `""` 203 | 204 | - `loginFetchError`:
205 | JavaScript code snippet injected into the error handler of the W3C-Fetch API call 206 | for loggin in. 207 | Default: `""` 208 | 209 | - `graphqlExample`:
210 | A GraphQL query string used as the initial query source in the UI. 211 | Default: 212 | 213 | ``` 214 | "query Example {\n" + 215 | " Session {\n" + 216 | " __typename # schema introspection\n" + 217 | " }\n" + 218 | "}\n" 219 | ``` 220 | 221 | - `documentationURL`:
222 | URL (usually under `graphiqlURL`) under which you want 223 | to reference a single documentation file with the markdown 224 | directive `[name](url)` from within your GraphQL schema comments. 225 | 226 | - `documentationFile`:
227 | The local path to the file to serve if `documentationURL` is requested. 228 | 229 | License 230 | ------- 231 | 232 | Copyright (c) 2016-2019 Dr. Ralf S. Engelschall (http://engelschall.com/) 233 | 234 | Permission is hereby granted, free of charge, to any person obtaining 235 | a copy of this software and associated documentation files (the 236 | "Software"), to deal in the Software without restriction, including 237 | without limitation the rights to use, copy, modify, merge, publish, 238 | distribute, sublicense, and/or sell copies of the Software, and to 239 | permit persons to whom the Software is furnished to do so, subject to 240 | the following conditions: 241 | 242 | The above copyright notice and this permission notice shall be included 243 | in all copies or substantial portions of the Software. 244 | 245 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 246 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 247 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 248 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 249 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 250 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 251 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 252 | 253 | -------------------------------------------------------------------------------- /eslint.yaml: -------------------------------------------------------------------------------- 1 | ## 2 | ## hapi-plugin-graphiql -- HAPI plugin for GraphiQL integration 3 | ## Copyright (c) 2016-2019 Dr. Ralf S. Engelschall 4 | ## 5 | ## Permission is hereby granted, free of charge, to any person obtaining 6 | ## a copy of this software and associated documentation files (the 7 | ## "Software"), to deal in the Software without restriction, including 8 | ## without limitation the rights to use, copy, modify, merge, publish, 9 | ## distribute, sublicense, and/or sell copies of the Software, and to 10 | ## permit persons to whom the Software is furnished to do so, subject to 11 | ## the following conditions: 12 | ## 13 | ## The above copyright notice and this permission notice shall be included 14 | ## in all copies or substantial portions of the Software. 15 | ## 16 | ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | ## IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | ## CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | ## TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | ## SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | ## 24 | 25 | --- 26 | 27 | extends: 28 | - eslint:recommended 29 | - eslint-config-standard 30 | 31 | parserOptions: 32 | ecmaVersion: 8 33 | sourceType: module 34 | ecmaFeatures: 35 | jsx: false 36 | 37 | parser: babel-eslint 38 | 39 | env: 40 | browser: false 41 | node: true 42 | commonjs: true 43 | worker: true 44 | serviceworker: true 45 | 46 | globals: 47 | process: true 48 | 49 | rules: 50 | # modified rules 51 | indent: [ "error", 4, { "SwitchCase": 1 } ] 52 | linebreak-style: [ "error", "unix" ] 53 | semi: [ "error", "never" ] 54 | operator-linebreak: [ "error", "after", { "overrides": { "&&": "before", "||": "before", ":": "before" } } ] 55 | brace-style: [ "error", "stroustrup", { "allowSingleLine": true } ] 56 | quotes: [ "error", "double" ] 57 | 58 | # disabled rules 59 | no-multi-spaces: off 60 | no-multiple-empty-lines: off 61 | key-spacing: off 62 | object-property-newline: off 63 | curly: off 64 | space-in-parens: off 65 | no-unneeded-ternary: off 66 | 67 | -------------------------------------------------------------------------------- /graphiql.css: -------------------------------------------------------------------------------- 1 | /* 2 | ** hapi-plugin-graphiql -- HAPI plugin for GraphiQL integration 3 | ** Copyright (c) 2016-2019 Dr. Ralf S. Engelschall 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be included 14 | ** in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | body { 26 | margin: 0; 27 | height: 100%; 28 | width: 100%; 29 | overflow: hidden; 30 | } 31 | 32 | #graphiql { 33 | width: 100vw; 34 | height: 100vh; 35 | } 36 | 37 | .toolbar { 38 | overflow: hidden; 39 | white-space: nowrap; 40 | display: block; 41 | } 42 | 43 | #username-label, 44 | #password-label { 45 | margin-left: 10px; 46 | margin-right: 4px; 47 | } 48 | 49 | #username, #password { 50 | height: 22px; 51 | border-radius: 4px; 52 | border: 1px solid #cccccc; 53 | padding-left: 4px; 54 | font-size: 14px; 55 | width: 100px; 56 | } 57 | 58 | #status { 59 | color: #000000; 60 | } 61 | #status.error { 62 | color: #cc3333; 63 | } 64 | .graphiql-container .footer { 65 | padding: 4px 10px 4px 10px; 66 | } 67 | 68 | .CodeMirror .CodeMirror-code { font-family: "Menlo", "DejaVu Sans Mono", "Consolas", monospace; } 69 | .CodeMirror .cm-keyword { color: #408edc; font-weight: bold; } 70 | .CodeMirror .cm-atom { color: #408edc; font-weight: bold; } 71 | .CodeMirror .cm-def { color: #333333; font-style: italic; } 72 | .CodeMirror .cm-variable { color: #408edc; } 73 | .CodeMirror .cm-string { color: #b06820; } 74 | .CodeMirror .cm-number { color: #b06820; } 75 | .CodeMirror .cm-punctuation { color: #408edc; } 76 | .CodeMirror .cm-property { color: #333333; } 77 | .CodeMirror .cm-qualifier { color: #333333; } 78 | .CodeMirror .cm-attribute { color: #333333; } 79 | 80 | .CodeMirror-hints { font-family: "Menlo", "DejaVu Sans Mono", "Consolas", monospace; } 81 | .CodeMirror-hint-information .content a { color: #408edc; } 82 | .CodeMirror-hint-information .content a:visited { color: #408edc; } 83 | 84 | .doc-type-description code { font-family: "Menlo", "DejaVu Sans Mono", "Consolas", monospace; } 85 | .doc-type-description a { color: #b06820; } 86 | .doc-type-description a.type-name { color: #408edc; } 87 | .doc-value-description { margin: 0; padding: 0 0 0 20px; } 88 | .doc-value-description p { margin: 0 0 4px 0; } 89 | .doc-explorer-title { color: #333333; } 90 | .doc-category .keyword { color: #408edc; font-weight: bold; } 91 | .doc-category .field-name { color: #333333; font-weight: bold; } 92 | .doc-category .enum-value { color: #333333; font-weight: bold; } 93 | .doc-category .arg-name { color: #333333; } 94 | .doc-category .arg-default-value { color: #b06820; } 95 | .doc-category .type-name { color: #408edc; } 96 | .doc-explorer-back:before { border-left-color: #666666 !important; border-top-color: #666666 !important; } 97 | .doc-explorer-back { color: #666666 !important; } 98 | .docExplorerShow:before { border-left-color: #666666 !important; border-top-color: #666666 !important; } 99 | .docExplorerShow { color: #666666 !important; } 100 | 101 | -------------------------------------------------------------------------------- /graphiql.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 31 | 32 |

33 | 34 | 35 | -------------------------------------------------------------------------------- /graphiql.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** hapi-plugin-graphiql -- HAPI plugin for GraphiQL integration 3 | ** Copyright (c) 2016-2019 Dr. Ralf S. Engelschall 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be included 14 | ** in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | $(document).ready(function () { 26 | {{graphiqlGlobals}} 27 | 28 | /* parse the search string to get url parameters */ 29 | var search = window.location.search 30 | var parameters = {} 31 | search.substr(1).split("&").forEach(function (entry) { 32 | var eq = entry.indexOf("=") 33 | if (eq >= 0) 34 | parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(entry.slice(eq + 1)) 35 | }) 36 | 37 | /* if variables was provided, try to format it */ 38 | if (!parameters.query) 39 | parameters.query = 40 | "# press Ctrl+SHIFT for auto-completing the input\n" + 41 | "# press Cmd+ENTER for executing the query\n" + 42 | "\n" + 43 | {{graphqlExample}} 44 | if (parameters.variables) { 45 | try { 46 | parameters.variables = JSON.stringify(JSON.parse(parameters.variables), null, 4) 47 | } catch (e) { 48 | /* do nothing, as we want to display the invalid JSON 49 | as a string, rather than present an error */ 50 | } 51 | } 52 | else 53 | parameters.variables = "{}" 54 | 55 | /* when the query and variables string is edited, 56 | update the URL bar so that it can be easily shared */ 57 | function updateURL () { 58 | var newSearch = "?" + Object.keys(parameters).filter(function (key) { 59 | return Boolean(parameters[key]) 60 | }).map(function (key) { 61 | return encodeURIComponent(key) + "=" + encodeURIComponent(parameters[key]) 62 | }).join("&"); 63 | history.replaceState(null, null, newSearch) 64 | } 65 | function onEditQuery (newQuery) { 66 | parameters.query = newQuery 67 | updateURL() 68 | } 69 | function onEditVariables (newVariables) { 70 | parameters.variables = newVariables 71 | updateURL() 72 | } 73 | function onEditOperationName (newOperationName) { 74 | parameters.operationName = newOperationName 75 | updateURL() 76 | } 77 | 78 | /* support status line updating */ 79 | var statusLineTimer = null 80 | function statusLine (type, message) { 81 | if (type === "success") 82 | $("#status").removeClass("error").text(message) 83 | else 84 | $("#status").addClass("error").text(message) 85 | if (statusLineTimer !== null) 86 | clearTimeout(statusLineTimer) 87 | statusLineTimer = setTimeout(function () { 88 | $("#status").removeClass("error").html(" ") 89 | statusLineTimer = null 90 | }, 4 * 1000) 91 | } 92 | 93 | /* connect to GraphQL backend using the Fetch API */ 94 | function graphQLFetcher (params) { 95 | return fetch(window.location.origin + "{{graphqlFetchURL}}", {{graphqlFetchOpts}}) 96 | .then(function (response) { 97 | if (response.status >= 200 && response.status < 300) 98 | statusLine("success", "GraphQL request succeeded") 99 | else 100 | statusLine("error", "GraphQL request failed") 101 | return response.text() 102 | }).then(function (responseBody) { 103 | try { 104 | return JSON.parse(responseBody) 105 | } catch (error) { 106 | return responseBody 107 | } 108 | }) 109 | } 110 | 111 | /* authentication */ 112 | var username = "" 113 | var password = "" 114 | var login = function () { 115 | return fetch(window.location.origin + "{{loginFetchURL}}", {{loginFetchOpts}}) 116 | .then(function (response) { 117 | if (response.status >= 200 && response.status < 300) { 118 | {{loginFetchSuccess}} 119 | statusLine("success", "Authentication succeeded") 120 | } 121 | else { 122 | {{loginFetchError}} 123 | statusLine("error", "Authentication failed") 124 | } 125 | }) 126 | } 127 | 128 | /* GraphiQL UI rendering */ 129 | var renderUI = function () { 130 | ReactDOM.render(React.createElement(GraphiQL, { 131 | fetcher: graphQLFetcher, 132 | query: parameters.query, 133 | variables: parameters.variables, 134 | operationName: parameters.operationName, 135 | onEditQuery: onEditQuery, 136 | onEditVariables: onEditVariables, 137 | onEditOperationName: onEditOperationName 138 | }, 139 | React.createElement(GraphiQL.Toolbar, {}, 140 | ReactDOMFactories.label({ id: "username-label", htmlFor: "username" }, "Username:"), 141 | ReactDOMFactories.input({ 142 | id: "username", 143 | type: "text", 144 | placeholder: "Username...", 145 | onChange: function (arg) { username = arg.target.value } 146 | }), 147 | ReactDOMFactories.label({ id: "password-label", htmlFor: "password" }, "Password:"), 148 | ReactDOMFactories.input({ 149 | id: "password", 150 | placeholder: "Password...", 151 | type: "password", 152 | onChange: function (arg) { password = arg.target.value } 153 | }), 154 | React.createElement(GraphiQL.ToolbarButton, { 155 | label: "Authenticate", 156 | title: "Authenticate at the service backend", 157 | onClick: function () { login(); return true } 158 | }) 159 | ), 160 | React.createElement(GraphiQL.Footer, {}, 161 | ReactDOMFactories.div({ id: "status" }, "") 162 | ) 163 | ), document.getElementById("graphiql")) 164 | } 165 | 166 | /* login and then render UI */ 167 | login().then(() => renderUI()) 168 | }) 169 | 170 | -------------------------------------------------------------------------------- /hapi-plugin-graphiql.js: -------------------------------------------------------------------------------- 1 | /* 2 | ** hapi-plugin-graphiql -- HAPI plugin for GraphiQL integration 3 | ** Copyright (c) 2016-2019 Dr. Ralf S. Engelschall 4 | ** 5 | ** Permission is hereby granted, free of charge, to any person obtaining 6 | ** a copy of this software and associated documentation files (the 7 | ** "Software"), to deal in the Software without restriction, including 8 | ** without limitation the rights to use, copy, modify, merge, publish, 9 | ** distribute, sublicense, and/or sell copies of the Software, and to 10 | ** permit persons to whom the Software is furnished to do so, subject to 11 | ** the following conditions: 12 | ** 13 | ** The above copyright notice and this permission notice shall be included 14 | ** in all copies or substantial portions of the Software. 15 | ** 16 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /* built-in dependencies */ 26 | const path = require("path") 27 | 28 | /* external dependencies */ 29 | const fs = require("mz/fs") 30 | const Boom = require("@hapi/boom") 31 | const nunjucks = require("nunjucks") 32 | const Promise = require("bluebird") 33 | 34 | /* internal dependencies */ 35 | const pkg = require("./package.json") 36 | 37 | /* the HAPI plugin register function */ 38 | const register = async (server, options) => { 39 | /* determine options */ 40 | options = Object.assign({}, { 41 | graphiqlSource: "downstream", 42 | graphiqlGlobals: "", 43 | graphiqlURL: "/graphiql", 44 | graphqlFetchURL: "/graphql", 45 | graphqlFetchOpts: 46 | "{\n" + 47 | " method: \"POST\",\n" + 48 | " headers: {\n" + 49 | " \"Content-Type\": \"application/json\",\n" + 50 | " \"Accept\": \"application/json\"\n" + 51 | " },\n" + 52 | " body: JSON.stringify(params),\n" + 53 | " credentials: \"same-origin\"\n" + 54 | "}\n", 55 | loginFetchURL: "/login", 56 | loginFetchOpts: 57 | "{\n" + 58 | " method: \"POST\",\n" + 59 | " headers: {\n" + 60 | " \"Content-Type\": \"application/json\"\n" + 61 | " },\n" + 62 | " body: JSON.stringify({\n" + 63 | " username: username,\n" + 64 | " password: password\n" + 65 | " }),\n" + 66 | " credentials: \"same-origin\"\n" + 67 | "}\n", 68 | loginFetchSuccess: "", 69 | loginFetchError: "", 70 | graphqlExample: 71 | "query Example {\n" + 72 | " Session {\n" + 73 | " __typename # schema introspection\n" + 74 | " }\n" + 75 | "}\n", 76 | documentationURL: "", 77 | documentationFile: "" 78 | }, options) 79 | 80 | /* convenience redirect */ 81 | server.route({ 82 | method: "GET", 83 | path: options.graphiqlURL, 84 | handler: async (request, h) => { 85 | return h.redirect(options.graphiqlURL + "/") 86 | } 87 | }) 88 | 89 | /* static delivery of GraphiQL tool */ 90 | server.route({ 91 | method: "GET", 92 | path: options.graphiqlURL + "/{name*}", 93 | handler: async (request, h) => { 94 | let name = request.params.name 95 | let files, content 96 | let loadFiles = async (files) => { 97 | return (await (Promise.map(files, async (file) => { 98 | let m 99 | let isTemplate = false 100 | if ((m = file.match(/^%(.+)$/)) !== null) { 101 | isTemplate = true 102 | file = m[1] 103 | } 104 | if ((m = file.match(/^@([^/]+)\/(.+)$/)) !== null) { 105 | file = require.resolve(path.join(m[1], "package.json")) 106 | file = path.resolve(file.replace(/package\.json$/, ""), m[2]) 107 | } 108 | else 109 | file = path.join(__dirname, file) 110 | let data = await fs.readFile(file, "utf8") 111 | if (isTemplate) { 112 | let env = nunjucks.configure({ autoescape: false }) 113 | data = (new nunjucks.Template(data, env)).render({ 114 | graphiqlGlobals: options.graphiqlGlobals, 115 | graphqlFetchURL: options.graphqlFetchURL, 116 | graphqlFetchOpts: options.graphqlFetchOpts, 117 | loginFetchURL: options.loginFetchURL, 118 | loginFetchOpts: options.loginFetchOpts, 119 | loginFetchSuccess: options.loginFetchSuccess, 120 | loginFetchError: options.loginFetchError, 121 | graphqlExample: JSON.stringify(options.graphqlExample) 122 | }) 123 | } 124 | return data 125 | }))).join("") 126 | } 127 | if (name === undefined || name === "" || name === "graphiql.html") { 128 | /* deliver HTML */ 129 | files = [ 130 | "graphiql.html" 131 | ] 132 | content = await loadFiles(files) 133 | return h.response(content).type("text/html") 134 | } 135 | else if (name === "graphiql.js") { 136 | /* deliver JS */ 137 | files = [ 138 | "@jquery/dist/jquery.min.js", 139 | "@whatwg-fetch/dist/fetch.umd.js", 140 | "@react/umd/react.production.min.js", 141 | "@react-dom/umd/react-dom.production.min.js", 142 | "@react-dom-factories/index.js", 143 | (options.graphiqlSource === "downstream" ? "./local/graphiql.min.js" : "@graphiql/graphiql.min.js"), 144 | "%graphiql.js" 145 | ] 146 | content = await loadFiles(files) 147 | return h.response(content).type("text/javascript") 148 | } 149 | else if (name === "graphiql.css") { 150 | /* deliver CSS */ 151 | files = [ 152 | (options.graphiqlSource === "downstream" ? "./local/graphiql.css" : "@graphiql/graphiql.css"), 153 | "graphiql.css" 154 | ] 155 | content = await loadFiles(files) 156 | return h.response(content).type("text/css") 157 | } 158 | else 159 | return Boom.badRequest("invalid path") 160 | } 161 | }) 162 | 163 | /* optional static delivery of documentation */ 164 | if (options.documentationURL !== "" && options.documentationFile !== "") { 165 | server.route({ 166 | method: "GET", 167 | path: options.documentationURL, 168 | handler: async (request, h) => { 169 | return h.file(options.documentationFile, { confine: false }) 170 | } 171 | }) 172 | } 173 | } 174 | 175 | /* export register function, wrapped in a plugin object */ 176 | module.exports = { 177 | plugin: { 178 | register: register, 179 | pkg: pkg 180 | } 181 | } 182 | 183 | -------------------------------------------------------------------------------- /local/README.md: -------------------------------------------------------------------------------- 1 | 2 | Downstream/Local GraphiQL Version 3 | ================================= 4 | 5 | This is a snapshot of the pre-built upstream [GraphiQL](https://github.com/graphql/graphiql) code as of 2019-06-16. 6 | It was [patched](graphiql.diff) to provide the following distinct changes against the upstream version (in order 7 | of importance for me): 8 | 9 | - In the "Docs" (DocExplorer) view, a reference like `[Type]()` is 10 | rendered as a hyperlinked type reference in case `Type` is a defined 11 | GraphQL standard or custom type. 12 | Rationale: I want to hyperlink types in type descriptions. 13 | 14 | - In the "Docs" (DocExplorer) view, add the kind of content (type or field). 15 | Rationale: it should be more clear what the users sees. 16 | 17 | - On pressing the button "Prettify", in addition to pretty-printed the Query source, 18 | also the Variables source is pretty-printed. 19 | Rationale: The variables should be also pretty-printed, of course. 20 | 21 | - On pressing the button "Prettify", the Query source is pretty-printed, 22 | but instead of an indentation of 2 spaces it uses an indentation of 4 spaces. 23 | Rationale: I just prefer 4 spaces in all source codes. 24 | 25 | - All built-in foreign module dependencies were upgraded to their latest versions. 26 | Rationale: I want to have the latest and greatest bugfixed versions, of course. 27 | 28 | -------------------------------------------------------------------------------- /local/graphiql.css: -------------------------------------------------------------------------------- 1 | .graphiql-container, 2 | .graphiql-container button, 3 | .graphiql-container input { 4 | color: #141823; 5 | font-family: 6 | system, 7 | -apple-system, 8 | 'San Francisco', 9 | '.SFNSDisplay-Regular', 10 | 'Segoe UI', 11 | Segoe, 12 | 'Segoe WP', 13 | 'Helvetica Neue', 14 | helvetica, 15 | 'Lucida Grande', 16 | arial, 17 | sans-serif; 18 | font-size: 14px; 19 | } 20 | 21 | .graphiql-container { 22 | display: -webkit-box; 23 | display: -ms-flexbox; 24 | display: flex; 25 | -webkit-box-orient: horizontal; 26 | -webkit-box-direction: normal; 27 | -ms-flex-direction: row; 28 | flex-direction: row; 29 | height: 100%; 30 | margin: 0; 31 | overflow: hidden; 32 | width: 100%; 33 | } 34 | 35 | .graphiql-container .editorWrap { 36 | display: -webkit-box; 37 | display: -ms-flexbox; 38 | display: flex; 39 | -webkit-box-orient: vertical; 40 | -webkit-box-direction: normal; 41 | -ms-flex-direction: column; 42 | flex-direction: column; 43 | -webkit-box-flex: 1; 44 | -ms-flex: 1; 45 | flex: 1; 46 | overflow-x: hidden; 47 | } 48 | 49 | .graphiql-container .title { 50 | font-size: 18px; 51 | } 52 | 53 | .graphiql-container .title em { 54 | font-family: georgia; 55 | font-size: 19px; 56 | } 57 | 58 | .graphiql-container .topBarWrap { 59 | display: -webkit-box; 60 | display: -ms-flexbox; 61 | display: flex; 62 | -webkit-box-orient: horizontal; 63 | -webkit-box-direction: normal; 64 | -ms-flex-direction: row; 65 | flex-direction: row; 66 | } 67 | 68 | .graphiql-container .topBar { 69 | -webkit-box-align: center; 70 | -ms-flex-align: center; 71 | align-items: center; 72 | background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e2e2e2)); 73 | background: linear-gradient(#f7f7f7, #e2e2e2); 74 | border-bottom: 1px solid #d0d0d0; 75 | cursor: default; 76 | display: -webkit-box; 77 | display: -ms-flexbox; 78 | display: flex; 79 | -webkit-box-orient: horizontal; 80 | -webkit-box-direction: normal; 81 | -ms-flex-direction: row; 82 | flex-direction: row; 83 | -webkit-box-flex: 1; 84 | -ms-flex: 1; 85 | flex: 1; 86 | height: 34px; 87 | overflow-y: visible; 88 | padding: 7px 14px 6px; 89 | -webkit-user-select: none; 90 | -moz-user-select: none; 91 | -ms-user-select: none; 92 | user-select: none; 93 | } 94 | 95 | .graphiql-container .toolbar { 96 | overflow-x: visible; 97 | display: -webkit-box; 98 | display: -ms-flexbox; 99 | display: flex; 100 | } 101 | 102 | .graphiql-container .docExplorerShow, 103 | .graphiql-container .historyShow { 104 | background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e2e2e2)); 105 | background: linear-gradient(#f7f7f7, #e2e2e2); 106 | border-radius: 0; 107 | border-bottom: 1px solid #d0d0d0; 108 | border-right: none; 109 | border-top: none; 110 | color: #3B5998; 111 | cursor: pointer; 112 | font-size: 14px; 113 | margin: 0; 114 | outline: 0; 115 | padding: 2px 20px 0 18px; 116 | } 117 | 118 | .graphiql-container .docExplorerShow { 119 | border-left: 1px solid rgba(0, 0, 0, 0.2); 120 | } 121 | 122 | .graphiql-container .historyShow { 123 | border-right: 1px solid rgba(0, 0, 0, 0.2); 124 | border-left: 0; 125 | } 126 | 127 | .graphiql-container .docExplorerShow:before { 128 | border-left: 2px solid #3B5998; 129 | border-top: 2px solid #3B5998; 130 | content: ''; 131 | display: inline-block; 132 | height: 9px; 133 | margin: 0 3px -1px 0; 134 | position: relative; 135 | -webkit-transform: rotate(-45deg); 136 | transform: rotate(-45deg); 137 | width: 9px; 138 | } 139 | 140 | .graphiql-container .editorBar { 141 | display: -webkit-box; 142 | display: -ms-flexbox; 143 | display: flex; 144 | -webkit-box-orient: horizontal; 145 | -webkit-box-direction: normal; 146 | -ms-flex-direction: row; 147 | flex-direction: row; 148 | -webkit-box-flex: 1; 149 | -ms-flex: 1; 150 | flex: 1; 151 | } 152 | 153 | .graphiql-container .queryWrap { 154 | display: -webkit-box; 155 | display: -ms-flexbox; 156 | display: flex; 157 | -webkit-box-orient: vertical; 158 | -webkit-box-direction: normal; 159 | -ms-flex-direction: column; 160 | flex-direction: column; 161 | -webkit-box-flex: 1; 162 | -ms-flex: 1; 163 | flex: 1; 164 | } 165 | 166 | .graphiql-container .resultWrap { 167 | border-left: solid 1px #e0e0e0; 168 | display: -webkit-box; 169 | display: -ms-flexbox; 170 | display: flex; 171 | -webkit-box-orient: vertical; 172 | -webkit-box-direction: normal; 173 | -ms-flex-direction: column; 174 | flex-direction: column; 175 | -webkit-box-flex: 1; 176 | -ms-flex: 1; 177 | flex: 1; 178 | position: relative; 179 | } 180 | 181 | .graphiql-container .docExplorerWrap, 182 | .graphiql-container .historyPaneWrap { 183 | background: white; 184 | -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); 185 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); 186 | position: relative; 187 | z-index: 3; 188 | } 189 | 190 | .graphiql-container .historyPaneWrap { 191 | min-width: 230px; 192 | z-index: 5; 193 | } 194 | 195 | .graphiql-container .docExplorerResizer { 196 | cursor: col-resize; 197 | height: 100%; 198 | left: -5px; 199 | position: absolute; 200 | top: 0; 201 | width: 10px; 202 | z-index: 10; 203 | } 204 | 205 | .graphiql-container .docExplorerHide { 206 | cursor: pointer; 207 | font-size: 18px; 208 | margin: -7px -8px -6px 0; 209 | padding: 18px 16px 15px 12px; 210 | } 211 | 212 | .graphiql-container div .query-editor { 213 | -webkit-box-flex: 1; 214 | -ms-flex: 1; 215 | flex: 1; 216 | position: relative; 217 | } 218 | 219 | .graphiql-container .variable-editor { 220 | display: -webkit-box; 221 | display: -ms-flexbox; 222 | display: flex; 223 | -webkit-box-orient: vertical; 224 | -webkit-box-direction: normal; 225 | -ms-flex-direction: column; 226 | flex-direction: column; 227 | height: 30px; 228 | position: relative; 229 | } 230 | 231 | .graphiql-container .variable-editor-title { 232 | background: #eeeeee; 233 | border-bottom: 1px solid #d6d6d6; 234 | border-top: 1px solid #e0e0e0; 235 | color: #777; 236 | font-variant: small-caps; 237 | font-weight: bold; 238 | letter-spacing: 1px; 239 | line-height: 14px; 240 | padding: 6px 0 8px 43px; 241 | text-transform: lowercase; 242 | -webkit-user-select: none; 243 | -moz-user-select: none; 244 | -ms-user-select: none; 245 | user-select: none; 246 | } 247 | 248 | .graphiql-container .codemirrorWrap { 249 | -webkit-box-flex: 1; 250 | -ms-flex: 1; 251 | flex: 1; 252 | height: 100%; 253 | position: relative; 254 | } 255 | 256 | .graphiql-container .result-window { 257 | -webkit-box-flex: 1; 258 | -ms-flex: 1; 259 | flex: 1; 260 | height: 100%; 261 | position: relative; 262 | } 263 | 264 | .graphiql-container .footer { 265 | background: #f6f7f8; 266 | border-left: 1px solid #e0e0e0; 267 | border-top: 1px solid #e0e0e0; 268 | margin-left: 12px; 269 | position: relative; 270 | } 271 | 272 | .graphiql-container .footer:before { 273 | background: #eeeeee; 274 | bottom: 0; 275 | content: " "; 276 | left: -13px; 277 | position: absolute; 278 | top: -1px; 279 | width: 12px; 280 | } 281 | 282 | /* No `.graphiql-container` here so themes can overwrite */ 283 | .result-window .CodeMirror { 284 | background: #f6f7f8; 285 | } 286 | 287 | .graphiql-container .result-window .CodeMirror-gutters { 288 | background-color: #eeeeee; 289 | border-color: #e0e0e0; 290 | cursor: col-resize; 291 | } 292 | 293 | .graphiql-container .result-window .CodeMirror-foldgutter, 294 | .graphiql-container .result-window .CodeMirror-foldgutter-open:after, 295 | .graphiql-container .result-window .CodeMirror-foldgutter-folded:after { 296 | padding-left: 3px; 297 | } 298 | 299 | .graphiql-container .toolbar-button { 300 | background: #fdfdfd; 301 | background: -webkit-gradient(linear, left top, left bottom, from(#f9f9f9), to(#ececec)); 302 | background: linear-gradient(#f9f9f9, #ececec); 303 | border-radius: 3px; 304 | -webkit-box-shadow: 305 | inset 0 0 0 1px rgba(0,0,0,0.20), 306 | 0 1px 0 rgba(255,255,255, 0.7), 307 | inset 0 1px #fff; 308 | box-shadow: 309 | inset 0 0 0 1px rgba(0,0,0,0.20), 310 | 0 1px 0 rgba(255,255,255, 0.7), 311 | inset 0 1px #fff; 312 | color: #555; 313 | cursor: pointer; 314 | display: inline-block; 315 | margin: 0 5px; 316 | padding: 3px 11px 5px; 317 | text-decoration: none; 318 | text-overflow: ellipsis; 319 | white-space: nowrap; 320 | max-width: 150px; 321 | } 322 | 323 | .graphiql-container .toolbar-button:active { 324 | background: -webkit-gradient(linear, left top, left bottom, from(#ececec), to(#d5d5d5)); 325 | background: linear-gradient(#ececec, #d5d5d5); 326 | -webkit-box-shadow: 327 | 0 1px 0 rgba(255, 255, 255, 0.7), 328 | inset 0 0 0 1px rgba(0,0,0,0.10), 329 | inset 0 1px 1px 1px rgba(0, 0, 0, 0.12), 330 | inset 0 0 5px rgba(0, 0, 0, 0.1); 331 | box-shadow: 332 | 0 1px 0 rgba(255, 255, 255, 0.7), 333 | inset 0 0 0 1px rgba(0,0,0,0.10), 334 | inset 0 1px 1px 1px rgba(0, 0, 0, 0.12), 335 | inset 0 0 5px rgba(0, 0, 0, 0.1); 336 | } 337 | 338 | .graphiql-container .toolbar-button.error { 339 | background: -webkit-gradient(linear, left top, left bottom, from(#fdf3f3), to(#e6d6d7)); 340 | background: linear-gradient(#fdf3f3, #e6d6d7); 341 | color: #b00; 342 | } 343 | 344 | .graphiql-container .toolbar-button-group { 345 | margin: 0 5px; 346 | white-space: nowrap; 347 | } 348 | 349 | .graphiql-container .toolbar-button-group > * { 350 | margin: 0; 351 | } 352 | 353 | .graphiql-container .toolbar-button-group > *:not(:last-child) { 354 | border-top-right-radius: 0; 355 | border-bottom-right-radius: 0; 356 | } 357 | 358 | .graphiql-container .toolbar-button-group > *:not(:first-child) { 359 | border-top-left-radius: 0; 360 | border-bottom-left-radius: 0; 361 | margin-left: -1px; 362 | } 363 | 364 | .graphiql-container .execute-button-wrap { 365 | height: 34px; 366 | margin: 0 14px 0 28px; 367 | position: relative; 368 | } 369 | 370 | .graphiql-container .execute-button { 371 | background: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#d2d3d6)); 372 | background: linear-gradient(#fdfdfd, #d2d3d6); 373 | border-radius: 17px; 374 | border: 1px solid rgba(0,0,0,0.25); 375 | -webkit-box-shadow: 0 1px 0 #fff; 376 | box-shadow: 0 1px 0 #fff; 377 | cursor: pointer; 378 | fill: #444; 379 | height: 34px; 380 | margin: 0; 381 | padding: 0; 382 | width: 34px; 383 | } 384 | 385 | .graphiql-container .execute-button svg { 386 | pointer-events: none; 387 | } 388 | 389 | .graphiql-container .execute-button:active { 390 | background: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6), to(#c3c3c3)); 391 | background: linear-gradient(#e6e6e6, #c3c3c3); 392 | -webkit-box-shadow: 393 | 0 1px 0 #fff, 394 | inset 0 0 2px rgba(0, 0, 0, 0.2), 395 | inset 0 0 6px rgba(0, 0, 0, 0.1); 396 | box-shadow: 397 | 0 1px 0 #fff, 398 | inset 0 0 2px rgba(0, 0, 0, 0.2), 399 | inset 0 0 6px rgba(0, 0, 0, 0.1); 400 | } 401 | 402 | .graphiql-container .execute-button:focus { 403 | outline: 0; 404 | } 405 | 406 | .graphiql-container .toolbar-menu, 407 | .graphiql-container .toolbar-select { 408 | position: relative; 409 | } 410 | 411 | .graphiql-container .execute-options, 412 | .graphiql-container .toolbar-menu-items, 413 | .graphiql-container .toolbar-select-options { 414 | background: #fff; 415 | -webkit-box-shadow: 416 | 0 0 0 1px rgba(0,0,0,0.1), 417 | 0 2px 4px rgba(0,0,0,0.25); 418 | box-shadow: 419 | 0 0 0 1px rgba(0,0,0,0.1), 420 | 0 2px 4px rgba(0,0,0,0.25); 421 | margin: 0; 422 | padding: 6px 0; 423 | position: absolute; 424 | z-index: 100; 425 | } 426 | 427 | .graphiql-container .execute-options { 428 | min-width: 100px; 429 | top: 37px; 430 | left: -1px; 431 | } 432 | 433 | .graphiql-container .toolbar-menu-items { 434 | left: 1px; 435 | margin-top: -1px; 436 | min-width: 110%; 437 | top: 100%; 438 | visibility: hidden; 439 | } 440 | 441 | .graphiql-container .toolbar-menu-items.open { 442 | visibility: visible; 443 | } 444 | 445 | .graphiql-container .toolbar-select-options { 446 | left: 0; 447 | min-width: 100%; 448 | top: -5px; 449 | visibility: hidden; 450 | } 451 | 452 | .graphiql-container .toolbar-select-options.open { 453 | visibility: visible; 454 | } 455 | 456 | .graphiql-container .execute-options > li, 457 | .graphiql-container .toolbar-menu-items > li, 458 | .graphiql-container .toolbar-select-options > li { 459 | cursor: pointer; 460 | display: block; 461 | margin: none; 462 | max-width: 300px; 463 | overflow: hidden; 464 | padding: 2px 20px 4px 11px; 465 | text-overflow: ellipsis; 466 | white-space: nowrap; 467 | } 468 | 469 | .graphiql-container .execute-options > li.selected, 470 | .graphiql-container .toolbar-menu-items > li.hover, 471 | .graphiql-container .toolbar-menu-items > li:active, 472 | .graphiql-container .toolbar-menu-items > li:hover, 473 | .graphiql-container .toolbar-select-options > li.hover, 474 | .graphiql-container .toolbar-select-options > li:active, 475 | .graphiql-container .toolbar-select-options > li:hover, 476 | .graphiql-container .history-contents > p:hover, 477 | .graphiql-container .history-contents > p:active { 478 | background: #e10098; 479 | color: #fff; 480 | } 481 | 482 | .graphiql-container .toolbar-select-options > li > svg { 483 | display: inline; 484 | fill: #666; 485 | margin: 0 -6px 0 6px; 486 | pointer-events: none; 487 | vertical-align: middle; 488 | } 489 | 490 | .graphiql-container .toolbar-select-options > li.hover > svg, 491 | .graphiql-container .toolbar-select-options > li:active > svg, 492 | .graphiql-container .toolbar-select-options > li:hover > svg { 493 | fill: #fff; 494 | } 495 | 496 | .graphiql-container .CodeMirror-scroll { 497 | overflow-scrolling: touch; 498 | } 499 | 500 | .graphiql-container .CodeMirror { 501 | color: #141823; 502 | font-family: 503 | 'Consolas', 504 | 'Inconsolata', 505 | 'Droid Sans Mono', 506 | 'Monaco', 507 | monospace; 508 | font-size: 13px; 509 | height: 100%; 510 | left: 0; 511 | position: absolute; 512 | top: 0; 513 | width: 100%; 514 | } 515 | 516 | .graphiql-container .CodeMirror-lines { 517 | padding: 20px 0; 518 | } 519 | 520 | .CodeMirror-hint-information .content { 521 | box-orient: vertical; 522 | color: #141823; 523 | display: -webkit-box; 524 | display: -ms-flexbox; 525 | display: flex; 526 | font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif; 527 | font-size: 13px; 528 | line-clamp: 3; 529 | line-height: 16px; 530 | max-height: 48px; 531 | overflow: hidden; 532 | text-overflow: -o-ellipsis-lastline; 533 | } 534 | 535 | .CodeMirror-hint-information .content p:first-child { 536 | margin-top: 0; 537 | } 538 | 539 | .CodeMirror-hint-information .content p:last-child { 540 | margin-bottom: 0; 541 | } 542 | 543 | .CodeMirror-hint-information .infoType { 544 | color: #CA9800; 545 | cursor: pointer; 546 | display: inline; 547 | margin-right: 0.5em; 548 | } 549 | 550 | .autoInsertedLeaf.cm-property { 551 | -webkit-animation-duration: 6s; 552 | animation-duration: 6s; 553 | -webkit-animation-name: insertionFade; 554 | animation-name: insertionFade; 555 | border-bottom: 2px solid rgba(255, 255, 255, 0); 556 | border-radius: 2px; 557 | margin: -2px -4px -1px; 558 | padding: 2px 4px 1px; 559 | } 560 | 561 | @-webkit-keyframes insertionFade { 562 | from, to { 563 | background: rgba(255, 255, 255, 0); 564 | border-color: rgba(255, 255, 255, 0); 565 | } 566 | 567 | 15%, 85% { 568 | background: #fbffc9; 569 | border-color: #f0f3c0; 570 | } 571 | } 572 | 573 | @keyframes insertionFade { 574 | from, to { 575 | background: rgba(255, 255, 255, 0); 576 | border-color: rgba(255, 255, 255, 0); 577 | } 578 | 579 | 15%, 85% { 580 | background: #fbffc9; 581 | border-color: #f0f3c0; 582 | } 583 | } 584 | 585 | div.CodeMirror-lint-tooltip { 586 | background-color: white; 587 | border-radius: 2px; 588 | border: 0; 589 | color: #141823; 590 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); 591 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); 592 | font-size: 13px; 593 | line-height: 16px; 594 | max-width: 430px; 595 | opacity: 0; 596 | padding: 8px 10px; 597 | -webkit-transition: opacity 0.15s; 598 | transition: opacity 0.15s; 599 | white-space: pre-wrap; 600 | } 601 | 602 | div.CodeMirror-lint-tooltip > * { 603 | padding-left: 23px; 604 | } 605 | 606 | div.CodeMirror-lint-tooltip > * + * { 607 | margin-top: 12px; 608 | } 609 | 610 | /* COLORS */ 611 | 612 | .graphiql-container .CodeMirror-foldmarker { 613 | border-radius: 4px; 614 | background: #08f; 615 | background: -webkit-gradient(linear, left top, left bottom, from(#43A8FF), to(#0F83E8)); 616 | background: linear-gradient(#43A8FF, #0F83E8); 617 | -webkit-box-shadow: 618 | 0 1px 1px rgba(0, 0, 0, 0.2), 619 | inset 0 0 0 1px rgba(0, 0, 0, 0.1); 620 | box-shadow: 621 | 0 1px 1px rgba(0, 0, 0, 0.2), 622 | inset 0 0 0 1px rgba(0, 0, 0, 0.1); 623 | color: white; 624 | font-family: arial; 625 | font-size: 12px; 626 | line-height: 0; 627 | margin: 0 3px; 628 | padding: 0px 4px 1px; 629 | text-shadow: 0 -1px rgba(0, 0, 0, 0.1); 630 | } 631 | 632 | .graphiql-container div.CodeMirror span.CodeMirror-matchingbracket { 633 | color: #555; 634 | text-decoration: underline; 635 | } 636 | 637 | .graphiql-container div.CodeMirror span.CodeMirror-nonmatchingbracket { 638 | color: #f00; 639 | } 640 | 641 | /* Comment */ 642 | .cm-comment { 643 | color: #999; 644 | } 645 | 646 | /* Punctuation */ 647 | .cm-punctuation { 648 | color: #555; 649 | } 650 | 651 | /* Keyword */ 652 | .cm-keyword { 653 | color: #B11A04; 654 | } 655 | 656 | /* OperationName, FragmentName */ 657 | .cm-def { 658 | color: #D2054E; 659 | } 660 | 661 | /* FieldName */ 662 | .cm-property { 663 | color: #1F61A0; 664 | } 665 | 666 | /* FieldAlias */ 667 | .cm-qualifier { 668 | color: #1C92A9; 669 | } 670 | 671 | /* ArgumentName and ObjectFieldName */ 672 | .cm-attribute { 673 | color: #8B2BB9; 674 | } 675 | 676 | /* Number */ 677 | .cm-number { 678 | color: #2882F9; 679 | } 680 | 681 | /* String */ 682 | .cm-string { 683 | color: #D64292; 684 | } 685 | 686 | /* Boolean */ 687 | .cm-builtin { 688 | color: #D47509; 689 | } 690 | 691 | /* EnumValue */ 692 | .cm-string-2 { 693 | color: #0B7FC7; 694 | } 695 | 696 | /* Variable */ 697 | .cm-variable { 698 | color: #397D13; 699 | } 700 | 701 | /* Directive */ 702 | .cm-meta { 703 | color: #B33086; 704 | } 705 | 706 | /* Type */ 707 | .cm-atom { 708 | color: #CA9800; 709 | } 710 | /* BASICS */ 711 | 712 | .CodeMirror { 713 | /* Set height, width, borders, and global font properties here */ 714 | color: black; 715 | font-family: monospace; 716 | height: 300px; 717 | } 718 | 719 | /* PADDING */ 720 | 721 | .CodeMirror-lines { 722 | padding: 4px 0; /* Vertical padding around content */ 723 | } 724 | .CodeMirror pre { 725 | padding: 0 4px; /* Horizontal padding of content */ 726 | } 727 | 728 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 729 | background-color: white; /* The little square between H and V scrollbars */ 730 | } 731 | 732 | /* GUTTER */ 733 | 734 | .CodeMirror-gutters { 735 | border-right: 1px solid #ddd; 736 | background-color: #f7f7f7; 737 | white-space: nowrap; 738 | } 739 | .CodeMirror-linenumbers {} 740 | .CodeMirror-linenumber { 741 | color: #999; 742 | min-width: 20px; 743 | padding: 0 3px 0 5px; 744 | text-align: right; 745 | white-space: nowrap; 746 | } 747 | 748 | .CodeMirror-guttermarker { color: black; } 749 | .CodeMirror-guttermarker-subtle { color: #999; } 750 | 751 | /* CURSOR */ 752 | 753 | .CodeMirror .CodeMirror-cursor { 754 | border-left: 1px solid black; 755 | } 756 | /* Shown when moving in bi-directional text */ 757 | .CodeMirror div.CodeMirror-secondarycursor { 758 | border-left: 1px solid silver; 759 | } 760 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 761 | background: #7e7; 762 | border: 0; 763 | width: auto; 764 | } 765 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 766 | z-index: 1; 767 | } 768 | 769 | .cm-animate-fat-cursor { 770 | -webkit-animation: blink 1.06s steps(1) infinite; 771 | animation: blink 1.06s steps(1) infinite; 772 | border: 0; 773 | width: auto; 774 | } 775 | @-webkit-keyframes blink { 776 | 0% { background: #7e7; } 777 | 50% { background: none; } 778 | 100% { background: #7e7; } 779 | } 780 | @keyframes blink { 781 | 0% { background: #7e7; } 782 | 50% { background: none; } 783 | 100% { background: #7e7; } 784 | } 785 | 786 | /* Can style cursor different in overwrite (non-insert) mode */ 787 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 788 | 789 | .cm-tab { display: inline-block; text-decoration: inherit; } 790 | 791 | .CodeMirror-ruler { 792 | border-left: 1px solid #ccc; 793 | position: absolute; 794 | } 795 | 796 | /* DEFAULT THEME */ 797 | 798 | .cm-s-default .cm-keyword {color: #708;} 799 | .cm-s-default .cm-atom {color: #219;} 800 | .cm-s-default .cm-number {color: #164;} 801 | .cm-s-default .cm-def {color: #00f;} 802 | .cm-s-default .cm-variable, 803 | .cm-s-default .cm-punctuation, 804 | .cm-s-default .cm-property, 805 | .cm-s-default .cm-operator {} 806 | .cm-s-default .cm-variable-2 {color: #05a;} 807 | .cm-s-default .cm-variable-3 {color: #085;} 808 | .cm-s-default .cm-comment {color: #a50;} 809 | .cm-s-default .cm-string {color: #a11;} 810 | .cm-s-default .cm-string-2 {color: #f50;} 811 | .cm-s-default .cm-meta {color: #555;} 812 | .cm-s-default .cm-qualifier {color: #555;} 813 | .cm-s-default .cm-builtin {color: #30a;} 814 | .cm-s-default .cm-bracket {color: #997;} 815 | .cm-s-default .cm-tag {color: #170;} 816 | .cm-s-default .cm-attribute {color: #00c;} 817 | .cm-s-default .cm-header {color: blue;} 818 | .cm-s-default .cm-quote {color: #090;} 819 | .cm-s-default .cm-hr {color: #999;} 820 | .cm-s-default .cm-link {color: #00c;} 821 | 822 | .cm-negative {color: #d44;} 823 | .cm-positive {color: #292;} 824 | .cm-header, .cm-strong {font-weight: bold;} 825 | .cm-em {font-style: italic;} 826 | .cm-link {text-decoration: underline;} 827 | .cm-strikethrough {text-decoration: line-through;} 828 | 829 | .cm-s-default .cm-error {color: #f00;} 830 | .cm-invalidchar {color: #f00;} 831 | 832 | .CodeMirror-composing { border-bottom: 2px solid; } 833 | 834 | /* Default styles for common addons */ 835 | 836 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 837 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 838 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 839 | .CodeMirror-activeline-background {background: #e8f2ff;} 840 | 841 | /* STOP */ 842 | 843 | /* The rest of this file contains styles related to the mechanics of 844 | the editor. You probably shouldn't touch them. */ 845 | 846 | .CodeMirror { 847 | background: white; 848 | overflow: hidden; 849 | position: relative; 850 | } 851 | 852 | .CodeMirror-scroll { 853 | height: 100%; 854 | /* 30px is the magic margin used to hide the element's real scrollbars */ 855 | /* See overflow: hidden in .CodeMirror */ 856 | margin-bottom: -30px; margin-right: -30px; 857 | outline: none; /* Prevent dragging from highlighting the element */ 858 | overflow: scroll !important; /* Things will break if this is overridden */ 859 | padding-bottom: 30px; 860 | position: relative; 861 | } 862 | .CodeMirror-sizer { 863 | border-right: 30px solid transparent; 864 | position: relative; 865 | } 866 | 867 | /* The fake, visible scrollbars. Used to force redraw during scrolling 868 | before actual scrolling happens, thus preventing shaking and 869 | flickering artifacts. */ 870 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 871 | display: none; 872 | position: absolute; 873 | z-index: 6; 874 | } 875 | .CodeMirror-vscrollbar { 876 | overflow-x: hidden; 877 | overflow-y: scroll; 878 | right: 0; top: 0; 879 | } 880 | .CodeMirror-hscrollbar { 881 | bottom: 0; left: 0; 882 | overflow-x: scroll; 883 | overflow-y: hidden; 884 | } 885 | .CodeMirror-scrollbar-filler { 886 | right: 0; bottom: 0; 887 | } 888 | .CodeMirror-gutter-filler { 889 | left: 0; bottom: 0; 890 | } 891 | 892 | .CodeMirror-gutters { 893 | min-height: 100%; 894 | position: absolute; left: 0; top: 0; 895 | z-index: 3; 896 | } 897 | .CodeMirror-gutter { 898 | display: inline-block; 899 | height: 100%; 900 | margin-bottom: -30px; 901 | vertical-align: top; 902 | white-space: normal; 903 | /* Hack to make IE7 behave */ 904 | *zoom:1; 905 | *display:inline; 906 | } 907 | .CodeMirror-gutter-wrapper { 908 | background: none !important; 909 | border: none !important; 910 | position: absolute; 911 | z-index: 4; 912 | } 913 | .CodeMirror-gutter-background { 914 | position: absolute; 915 | top: 0; bottom: 0; 916 | z-index: 4; 917 | } 918 | .CodeMirror-gutter-elt { 919 | cursor: default; 920 | position: absolute; 921 | z-index: 4; 922 | } 923 | .CodeMirror-gutter-wrapper { 924 | -webkit-user-select: none; 925 | -moz-user-select: none; 926 | -ms-user-select: none; 927 | user-select: none; 928 | } 929 | 930 | .CodeMirror-lines { 931 | cursor: text; 932 | min-height: 1px; /* prevents collapsing before first draw */ 933 | } 934 | .CodeMirror pre { 935 | -webkit-tap-highlight-color: transparent; 936 | /* Reset some styles that the rest of the page might have set */ 937 | background: transparent; 938 | border-radius: 0; 939 | border-width: 0; 940 | color: inherit; 941 | font-family: inherit; 942 | font-size: inherit; 943 | -webkit-font-variant-ligatures: none; 944 | font-variant-ligatures: none; 945 | line-height: inherit; 946 | margin: 0; 947 | overflow: visible; 948 | position: relative; 949 | white-space: pre; 950 | word-wrap: normal; 951 | z-index: 2; 952 | } 953 | .CodeMirror-wrap pre { 954 | word-wrap: break-word; 955 | white-space: pre-wrap; 956 | word-break: normal; 957 | } 958 | 959 | .CodeMirror-linebackground { 960 | position: absolute; 961 | left: 0; right: 0; top: 0; bottom: 0; 962 | z-index: 0; 963 | } 964 | 965 | .CodeMirror-linewidget { 966 | overflow: auto; 967 | position: relative; 968 | z-index: 2; 969 | } 970 | 971 | .CodeMirror-widget {} 972 | 973 | .CodeMirror-code { 974 | outline: none; 975 | } 976 | 977 | /* Force content-box sizing for the elements where we expect it */ 978 | .CodeMirror-scroll, 979 | .CodeMirror-sizer, 980 | .CodeMirror-gutter, 981 | .CodeMirror-gutters, 982 | .CodeMirror-linenumber { 983 | -webkit-box-sizing: content-box; 984 | box-sizing: content-box; 985 | } 986 | 987 | .CodeMirror-measure { 988 | height: 0; 989 | overflow: hidden; 990 | position: absolute; 991 | visibility: hidden; 992 | width: 100%; 993 | } 994 | 995 | .CodeMirror-cursor { position: absolute; } 996 | .CodeMirror-measure pre { position: static; } 997 | 998 | div.CodeMirror-cursors { 999 | position: relative; 1000 | visibility: hidden; 1001 | z-index: 3; 1002 | } 1003 | div.CodeMirror-dragcursors { 1004 | visibility: visible; 1005 | } 1006 | 1007 | .CodeMirror-focused div.CodeMirror-cursors { 1008 | visibility: visible; 1009 | } 1010 | 1011 | .CodeMirror-selected { background: #d9d9d9; } 1012 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 1013 | .CodeMirror-crosshair { cursor: crosshair; } 1014 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 1015 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 1016 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 1017 | 1018 | .cm-searching { 1019 | background: #ffa; 1020 | background: rgba(255, 255, 0, .4); 1021 | } 1022 | 1023 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 1024 | .CodeMirror span { *vertical-align: text-bottom; } 1025 | 1026 | /* Used to force a border model for a node */ 1027 | .cm-force-border { padding-right: .1px; } 1028 | 1029 | @media print { 1030 | /* Hide the cursor when printing */ 1031 | .CodeMirror div.CodeMirror-cursors { 1032 | visibility: hidden; 1033 | } 1034 | } 1035 | 1036 | /* See issue #2901 */ 1037 | .cm-tab-wrap-hack:after { content: ''; } 1038 | 1039 | /* Help users use markselection to safely style text background */ 1040 | span.CodeMirror-selectedtext { background: none; } 1041 | 1042 | .CodeMirror-dialog { 1043 | background: inherit; 1044 | color: inherit; 1045 | left: 0; right: 0; 1046 | overflow: hidden; 1047 | padding: .1em .8em; 1048 | position: absolute; 1049 | z-index: 15; 1050 | } 1051 | 1052 | .CodeMirror-dialog-top { 1053 | border-bottom: 1px solid #eee; 1054 | top: 0; 1055 | } 1056 | 1057 | .CodeMirror-dialog-bottom { 1058 | border-top: 1px solid #eee; 1059 | bottom: 0; 1060 | } 1061 | 1062 | .CodeMirror-dialog input { 1063 | background: transparent; 1064 | border: 1px solid #d3d6db; 1065 | color: inherit; 1066 | font-family: monospace; 1067 | outline: none; 1068 | width: 20em; 1069 | } 1070 | 1071 | .CodeMirror-dialog button { 1072 | font-size: 70%; 1073 | } 1074 | .graphiql-container .doc-explorer { 1075 | background: white; 1076 | } 1077 | 1078 | .graphiql-container .doc-explorer-title-bar, 1079 | .graphiql-container .history-title-bar { 1080 | cursor: default; 1081 | display: -webkit-box; 1082 | display: -ms-flexbox; 1083 | display: flex; 1084 | height: 34px; 1085 | line-height: 14px; 1086 | padding: 8px 8px 5px; 1087 | position: relative; 1088 | -webkit-user-select: none; 1089 | -moz-user-select: none; 1090 | -ms-user-select: none; 1091 | user-select: none; 1092 | } 1093 | 1094 | .graphiql-container .doc-explorer-title, 1095 | .graphiql-container .history-title { 1096 | -webkit-box-flex: 1; 1097 | -ms-flex: 1; 1098 | flex: 1; 1099 | font-weight: bold; 1100 | overflow-x: hidden; 1101 | padding: 10px 0 10px 10px; 1102 | text-align: center; 1103 | text-overflow: ellipsis; 1104 | -webkit-user-select: text; 1105 | -moz-user-select: text; 1106 | -ms-user-select: text; 1107 | user-select: text; 1108 | white-space: nowrap; 1109 | } 1110 | 1111 | .graphiql-container .doc-explorer-back { 1112 | color: #3B5998; 1113 | cursor: pointer; 1114 | margin: -7px 0 -6px -8px; 1115 | overflow-x: hidden; 1116 | padding: 17px 12px 16px 16px; 1117 | text-overflow: ellipsis; 1118 | white-space: nowrap; 1119 | } 1120 | 1121 | .doc-explorer-narrow .doc-explorer-back { 1122 | width: 0; 1123 | } 1124 | 1125 | .graphiql-container .doc-explorer-back:before { 1126 | border-left: 2px solid #3B5998; 1127 | border-top: 2px solid #3B5998; 1128 | content: ''; 1129 | display: inline-block; 1130 | height: 9px; 1131 | margin: 0 3px -1px 0; 1132 | position: relative; 1133 | -webkit-transform: rotate(-45deg); 1134 | transform: rotate(-45deg); 1135 | width: 9px; 1136 | } 1137 | 1138 | .graphiql-container .doc-explorer-rhs { 1139 | position: relative; 1140 | } 1141 | 1142 | .graphiql-container .doc-explorer-contents, 1143 | .graphiql-container .history-contents { 1144 | background-color: #ffffff; 1145 | border-top: 1px solid #d6d6d6; 1146 | bottom: 0; 1147 | left: 0; 1148 | overflow-y: auto; 1149 | padding: 20px 15px; 1150 | position: absolute; 1151 | right: 0; 1152 | top: 47px; 1153 | } 1154 | 1155 | .graphiql-container .doc-explorer-contents { 1156 | min-width: 300px; 1157 | } 1158 | 1159 | .graphiql-container .doc-type-kind { 1160 | border-bottom: 1px solid #e0e0e0; 1161 | color: #777; 1162 | cursor: default; 1163 | font-size: 20px; 1164 | font-variant: small-caps; 1165 | font-weight: bold; 1166 | letter-spacing: 1px; 1167 | margin: 0 -15px 10px 0; 1168 | padding: 10px 0; 1169 | -webkit-user-select: none; 1170 | -moz-user-select: none; 1171 | -ms-user-select: none; 1172 | user-select: none; 1173 | } 1174 | 1175 | .graphiql-container .doc-type-description p:first-child , 1176 | .graphiql-container .doc-type-description blockquote:first-child { 1177 | margin-top: 0; 1178 | } 1179 | 1180 | .graphiql-container .doc-explorer-contents a { 1181 | cursor: pointer; 1182 | text-decoration: none; 1183 | } 1184 | 1185 | .graphiql-container .doc-explorer-contents a:hover { 1186 | text-decoration: underline; 1187 | } 1188 | 1189 | .graphiql-container .doc-value-description > :first-child { 1190 | margin-top: 4px; 1191 | } 1192 | 1193 | .graphiql-container .doc-value-description > :last-child { 1194 | margin-bottom: 4px; 1195 | } 1196 | 1197 | .graphiql-container .doc-category { 1198 | margin: 20px 0; 1199 | } 1200 | 1201 | .graphiql-container .doc-category-title { 1202 | border-bottom: 1px solid #e0e0e0; 1203 | color: #777; 1204 | cursor: default; 1205 | font-size: 14px; 1206 | font-variant: small-caps; 1207 | font-weight: bold; 1208 | letter-spacing: 1px; 1209 | margin: 0 -15px 10px 0; 1210 | padding: 10px 0; 1211 | -webkit-user-select: none; 1212 | -moz-user-select: none; 1213 | -ms-user-select: none; 1214 | user-select: none; 1215 | } 1216 | 1217 | .graphiql-container .doc-category-item { 1218 | margin: 12px 0; 1219 | color: #555; 1220 | } 1221 | 1222 | .graphiql-container .keyword { 1223 | color: #B11A04; 1224 | } 1225 | 1226 | .graphiql-container .type-name { 1227 | color: #CA9800; 1228 | } 1229 | 1230 | .graphiql-container .field-name { 1231 | color: #1F61A0; 1232 | } 1233 | 1234 | .graphiql-container .field-short-description { 1235 | color: #999; 1236 | margin-left: 5px; 1237 | overflow: hidden; 1238 | text-overflow: ellipsis; 1239 | } 1240 | 1241 | .graphiql-container .enum-value { 1242 | color: #0B7FC7; 1243 | } 1244 | 1245 | .graphiql-container .arg-name { 1246 | color: #8B2BB9; 1247 | } 1248 | 1249 | .graphiql-container .arg { 1250 | display: block; 1251 | margin-left: 1em; 1252 | } 1253 | 1254 | .graphiql-container .arg:first-child:last-child, 1255 | .graphiql-container .arg:first-child:nth-last-child(2), 1256 | .graphiql-container .arg:first-child:nth-last-child(2) ~ .arg { 1257 | display: inherit; 1258 | margin: inherit; 1259 | } 1260 | 1261 | .graphiql-container .arg:first-child:nth-last-child(2):after { 1262 | content: ', '; 1263 | } 1264 | 1265 | .graphiql-container .arg-default-value { 1266 | color: #43A047; 1267 | } 1268 | 1269 | .graphiql-container .doc-deprecation { 1270 | background: #fffae8; 1271 | -webkit-box-shadow: inset 0 0 1px #bfb063; 1272 | box-shadow: inset 0 0 1px #bfb063; 1273 | color: #867F70; 1274 | line-height: 16px; 1275 | margin: 8px -8px; 1276 | max-height: 80px; 1277 | overflow: hidden; 1278 | padding: 8px; 1279 | border-radius: 3px; 1280 | } 1281 | 1282 | .graphiql-container .doc-deprecation:before { 1283 | content: 'Deprecated:'; 1284 | color: #c79b2e; 1285 | cursor: default; 1286 | display: block; 1287 | font-size: 9px; 1288 | font-weight: bold; 1289 | letter-spacing: 1px; 1290 | line-height: 1; 1291 | padding-bottom: 5px; 1292 | text-transform: uppercase; 1293 | -webkit-user-select: none; 1294 | -moz-user-select: none; 1295 | -ms-user-select: none; 1296 | user-select: none; 1297 | } 1298 | 1299 | .graphiql-container .doc-deprecation > :first-child { 1300 | margin-top: 0; 1301 | } 1302 | 1303 | .graphiql-container .doc-deprecation > :last-child { 1304 | margin-bottom: 0; 1305 | } 1306 | 1307 | .graphiql-container .show-btn { 1308 | -webkit-appearance: initial; 1309 | display: block; 1310 | border-radius: 3px; 1311 | border: solid 1px #ccc; 1312 | text-align: center; 1313 | padding: 8px 12px 10px; 1314 | width: 100%; 1315 | -webkit-box-sizing: border-box; 1316 | box-sizing: border-box; 1317 | background: #fbfcfc; 1318 | color: #555; 1319 | cursor: pointer; 1320 | } 1321 | 1322 | .graphiql-container .search-box { 1323 | border-bottom: 1px solid #d3d6db; 1324 | display: block; 1325 | font-size: 14px; 1326 | margin: -15px -15px 12px 0; 1327 | position: relative; 1328 | } 1329 | 1330 | .graphiql-container .search-box:before { 1331 | content: '\26b2'; 1332 | cursor: pointer; 1333 | display: block; 1334 | font-size: 24px; 1335 | position: absolute; 1336 | top: -2px; 1337 | -webkit-transform: rotate(-45deg); 1338 | transform: rotate(-45deg); 1339 | -webkit-user-select: none; 1340 | -moz-user-select: none; 1341 | -ms-user-select: none; 1342 | user-select: none; 1343 | } 1344 | 1345 | .graphiql-container .search-box .search-box-clear { 1346 | background-color: #d0d0d0; 1347 | border-radius: 12px; 1348 | color: #fff; 1349 | cursor: pointer; 1350 | font-size: 11px; 1351 | padding: 1px 5px 2px; 1352 | position: absolute; 1353 | right: 3px; 1354 | top: 8px; 1355 | -webkit-user-select: none; 1356 | -moz-user-select: none; 1357 | -ms-user-select: none; 1358 | user-select: none; 1359 | } 1360 | 1361 | .graphiql-container .search-box .search-box-clear:hover { 1362 | background-color: #b9b9b9; 1363 | } 1364 | 1365 | .graphiql-container .search-box > input { 1366 | border: none; 1367 | -webkit-box-sizing: border-box; 1368 | box-sizing: border-box; 1369 | font-size: 14px; 1370 | outline: none; 1371 | padding: 6px 24px 8px 20px; 1372 | width: 100%; 1373 | } 1374 | 1375 | .graphiql-container .error-container { 1376 | font-weight: bold; 1377 | left: 0; 1378 | letter-spacing: 1px; 1379 | opacity: 0.5; 1380 | position: absolute; 1381 | right: 0; 1382 | text-align: center; 1383 | text-transform: uppercase; 1384 | top: 50%; 1385 | -webkit-transform: translate(0, -50%); 1386 | transform: translate(0, -50%); 1387 | } 1388 | .CodeMirror-foldmarker { 1389 | color: blue; 1390 | cursor: pointer; 1391 | font-family: arial; 1392 | line-height: .3; 1393 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; 1394 | } 1395 | .CodeMirror-foldgutter { 1396 | width: .7em; 1397 | } 1398 | .CodeMirror-foldgutter-open, 1399 | .CodeMirror-foldgutter-folded { 1400 | cursor: pointer; 1401 | } 1402 | .CodeMirror-foldgutter-open:after { 1403 | content: "\25BE"; 1404 | } 1405 | .CodeMirror-foldgutter-folded:after { 1406 | content: "\25B8"; 1407 | } 1408 | .graphiql-container .history-contents, 1409 | .graphiql-container .history-contents input { 1410 | font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; 1411 | padding: 0; 1412 | } 1413 | 1414 | .graphiql-container .history-contents p { 1415 | -webkit-box-align: center; 1416 | -ms-flex-align: center; 1417 | align-items: center; 1418 | display: -webkit-box; 1419 | display: -ms-flexbox; 1420 | display: flex; 1421 | font-size: 12px; 1422 | overflow: hidden; 1423 | text-overflow: ellipsis; 1424 | white-space: nowrap; 1425 | margin: 0; 1426 | padding: 8px; 1427 | border-bottom: 1px solid #e0e0e0; 1428 | } 1429 | 1430 | .graphiql-container .history-contents p.editable { 1431 | padding-bottom: 6px; 1432 | padding-top: 7px; 1433 | } 1434 | 1435 | .graphiql-container .history-contents input { 1436 | -webkit-box-flex: 1; 1437 | -ms-flex-positive: 1; 1438 | flex-grow: 1; 1439 | font-size: 12px; 1440 | } 1441 | 1442 | .graphiql-container .history-contents p:hover { 1443 | cursor: pointer; 1444 | } 1445 | 1446 | .graphiql-container .history-contents p span.history-label { 1447 | -webkit-box-flex: 1; 1448 | -ms-flex-positive: 1; 1449 | flex-grow: 1; 1450 | overflow: hidden; 1451 | text-overflow: ellipsis; 1452 | }.CodeMirror-info { 1453 | background: white; 1454 | border-radius: 2px; 1455 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); 1456 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); 1457 | -webkit-box-sizing: border-box; 1458 | box-sizing: border-box; 1459 | color: #555; 1460 | font-family: 1461 | system, 1462 | -apple-system, 1463 | 'San Francisco', 1464 | '.SFNSDisplay-Regular', 1465 | 'Segoe UI', 1466 | Segoe, 1467 | 'Segoe WP', 1468 | 'Helvetica Neue', 1469 | helvetica, 1470 | 'Lucida Grande', 1471 | arial, 1472 | sans-serif; 1473 | font-size: 13px; 1474 | line-height: 16px; 1475 | margin: 8px -8px; 1476 | max-width: 400px; 1477 | opacity: 0; 1478 | overflow: hidden; 1479 | padding: 8px 8px; 1480 | position: fixed; 1481 | -webkit-transition: opacity 0.15s; 1482 | transition: opacity 0.15s; 1483 | z-index: 50; 1484 | } 1485 | 1486 | .CodeMirror-info :first-child { 1487 | margin-top: 0; 1488 | } 1489 | 1490 | .CodeMirror-info :last-child { 1491 | margin-bottom: 0; 1492 | } 1493 | 1494 | .CodeMirror-info p { 1495 | margin: 1em 0; 1496 | } 1497 | 1498 | .CodeMirror-info .info-description { 1499 | color: #777; 1500 | line-height: 16px; 1501 | margin-top: 1em; 1502 | max-height: 80px; 1503 | overflow: hidden; 1504 | } 1505 | 1506 | .CodeMirror-info .info-deprecation { 1507 | background: #fffae8; 1508 | -webkit-box-shadow: inset 0 1px 1px -1px #bfb063; 1509 | box-shadow: inset 0 1px 1px -1px #bfb063; 1510 | color: #867F70; 1511 | line-height: 16px; 1512 | margin: -8px; 1513 | margin-top: 8px; 1514 | max-height: 80px; 1515 | overflow: hidden; 1516 | padding: 8px; 1517 | } 1518 | 1519 | .CodeMirror-info .info-deprecation-label { 1520 | color: #c79b2e; 1521 | cursor: default; 1522 | display: block; 1523 | font-size: 9px; 1524 | font-weight: bold; 1525 | letter-spacing: 1px; 1526 | line-height: 1; 1527 | padding-bottom: 5px; 1528 | text-transform: uppercase; 1529 | -webkit-user-select: none; 1530 | -moz-user-select: none; 1531 | -ms-user-select: none; 1532 | user-select: none; 1533 | } 1534 | 1535 | .CodeMirror-info .info-deprecation-label + * { 1536 | margin-top: 0; 1537 | } 1538 | 1539 | .CodeMirror-info a { 1540 | text-decoration: none; 1541 | } 1542 | 1543 | .CodeMirror-info a:hover { 1544 | text-decoration: underline; 1545 | } 1546 | 1547 | .CodeMirror-info .type-name { 1548 | color: #CA9800; 1549 | } 1550 | 1551 | .CodeMirror-info .field-name { 1552 | color: #1F61A0; 1553 | } 1554 | 1555 | .CodeMirror-info .enum-value { 1556 | color: #0B7FC7; 1557 | } 1558 | 1559 | .CodeMirror-info .arg-name { 1560 | color: #8B2BB9; 1561 | } 1562 | 1563 | .CodeMirror-info .directive-name { 1564 | color: #B33086; 1565 | } 1566 | .CodeMirror-jump-token { 1567 | text-decoration: underline; 1568 | cursor: pointer; 1569 | } 1570 | /* The lint marker gutter */ 1571 | .CodeMirror-lint-markers { 1572 | width: 16px; 1573 | } 1574 | 1575 | .CodeMirror-lint-tooltip { 1576 | background-color: infobackground; 1577 | border-radius: 4px 4px 4px 4px; 1578 | border: 1px solid black; 1579 | color: infotext; 1580 | font-family: monospace; 1581 | font-size: 10pt; 1582 | max-width: 600px; 1583 | opacity: 0; 1584 | overflow: hidden; 1585 | padding: 2px 5px; 1586 | position: fixed; 1587 | -webkit-transition: opacity .4s; 1588 | transition: opacity .4s; 1589 | white-space: pre-wrap; 1590 | z-index: 100; 1591 | } 1592 | 1593 | .CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { 1594 | background-position: left bottom; 1595 | background-repeat: repeat-x; 1596 | } 1597 | 1598 | .CodeMirror-lint-mark-error { 1599 | background-image: 1600 | url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") 1601 | ; 1602 | } 1603 | 1604 | .CodeMirror-lint-mark-warning { 1605 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); 1606 | } 1607 | 1608 | .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { 1609 | background-position: center center; 1610 | background-repeat: no-repeat; 1611 | cursor: pointer; 1612 | display: inline-block; 1613 | height: 16px; 1614 | position: relative; 1615 | vertical-align: middle; 1616 | width: 16px; 1617 | } 1618 | 1619 | .CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { 1620 | background-position: top left; 1621 | background-repeat: no-repeat; 1622 | padding-left: 18px; 1623 | } 1624 | 1625 | .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { 1626 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); 1627 | } 1628 | 1629 | .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { 1630 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); 1631 | } 1632 | 1633 | .CodeMirror-lint-marker-multiple { 1634 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); 1635 | background-position: right bottom; 1636 | background-repeat: no-repeat; 1637 | width: 100%; height: 100%; 1638 | } 1639 | .graphiql-container .spinner-container { 1640 | height: 36px; 1641 | left: 50%; 1642 | position: absolute; 1643 | top: 50%; 1644 | -webkit-transform: translate(-50%, -50%); 1645 | transform: translate(-50%, -50%); 1646 | width: 36px; 1647 | z-index: 10; 1648 | } 1649 | 1650 | .graphiql-container .spinner { 1651 | -webkit-animation: rotation .6s infinite linear; 1652 | animation: rotation .6s infinite linear; 1653 | border-bottom: 6px solid rgba(150, 150, 150, .15); 1654 | border-left: 6px solid rgba(150, 150, 150, .15); 1655 | border-radius: 100%; 1656 | border-right: 6px solid rgba(150, 150, 150, .15); 1657 | border-top: 6px solid rgba(150, 150, 150, .8); 1658 | display: inline-block; 1659 | height: 24px; 1660 | position: absolute; 1661 | vertical-align: middle; 1662 | width: 24px; 1663 | } 1664 | 1665 | @-webkit-keyframes rotation { 1666 | from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 1667 | to { -webkit-transform: rotate(359deg); transform: rotate(359deg); } 1668 | } 1669 | 1670 | @keyframes rotation { 1671 | from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 1672 | to { -webkit-transform: rotate(359deg); transform: rotate(359deg); } 1673 | } 1674 | .CodeMirror-hints { 1675 | background: white; 1676 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); 1677 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); 1678 | font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; 1679 | font-size: 13px; 1680 | list-style: none; 1681 | margin-left: -6px; 1682 | margin: 0; 1683 | max-height: 14.5em; 1684 | overflow-y: auto; 1685 | overflow: hidden; 1686 | padding: 0; 1687 | position: absolute; 1688 | z-index: 10; 1689 | } 1690 | 1691 | .CodeMirror-hint { 1692 | border-top: solid 1px #f7f7f7; 1693 | color: #141823; 1694 | cursor: pointer; 1695 | margin: 0; 1696 | max-width: 300px; 1697 | overflow: hidden; 1698 | padding: 2px 6px; 1699 | white-space: pre; 1700 | } 1701 | 1702 | li.CodeMirror-hint-active { 1703 | background-color: #08f; 1704 | border-top-color: white; 1705 | color: white; 1706 | } 1707 | 1708 | .CodeMirror-hint-information { 1709 | border-top: solid 1px #c0c0c0; 1710 | max-width: 300px; 1711 | padding: 4px 6px; 1712 | position: relative; 1713 | z-index: 1; 1714 | } 1715 | 1716 | .CodeMirror-hint-information:first-child { 1717 | border-bottom: solid 1px #c0c0c0; 1718 | border-top: none; 1719 | margin-bottom: -1px; 1720 | } 1721 | 1722 | .CodeMirror-hint-deprecation { 1723 | background: #fffae8; 1724 | -webkit-box-shadow: inset 0 1px 1px -1px #bfb063; 1725 | box-shadow: inset 0 1px 1px -1px #bfb063; 1726 | color: #867F70; 1727 | font-family: 1728 | system, 1729 | -apple-system, 1730 | 'San Francisco', 1731 | '.SFNSDisplay-Regular', 1732 | 'Segoe UI', 1733 | Segoe, 1734 | 'Segoe WP', 1735 | 'Helvetica Neue', 1736 | helvetica, 1737 | 'Lucida Grande', 1738 | arial, 1739 | sans-serif; 1740 | font-size: 13px; 1741 | line-height: 16px; 1742 | margin-top: 4px; 1743 | max-height: 80px; 1744 | overflow: hidden; 1745 | padding: 6px; 1746 | } 1747 | 1748 | .CodeMirror-hint-deprecation .deprecation-label { 1749 | color: #c79b2e; 1750 | cursor: default; 1751 | display: block; 1752 | font-size: 9px; 1753 | font-weight: bold; 1754 | letter-spacing: 1px; 1755 | line-height: 1; 1756 | padding-bottom: 5px; 1757 | text-transform: uppercase; 1758 | -webkit-user-select: none; 1759 | -moz-user-select: none; 1760 | -ms-user-select: none; 1761 | user-select: none; 1762 | } 1763 | 1764 | .CodeMirror-hint-deprecation .deprecation-label + * { 1765 | margin-top: 0; 1766 | } 1767 | 1768 | .CodeMirror-hint-deprecation :last-child { 1769 | margin-bottom: 0; 1770 | } 1771 | -------------------------------------------------------------------------------- /local/graphiql.diff: -------------------------------------------------------------------------------- 1 | diff --git a/css/doc-explorer.css b/css/doc-explorer.css 2 | index 11acf19..ef802e3 100644 3 | --- a/css/doc-explorer.css 4 | +++ b/css/doc-explorer.css 5 | @@ -72,6 +72,20 @@ 6 | min-width: 300px; 7 | } 8 | 9 | +.graphiql-container .doc-type-kind { 10 | + border-bottom: 1px solid #e0e0e0; 11 | + color: #777; 12 | + cursor: default; 13 | + font-size: 20px; 14 | + font-variant: small-caps; 15 | + font-weight: bold; 16 | + letter-spacing: 1px; 17 | + margin: 0 -15px 10px 0; 18 | + padding: 10px 0; 19 | + -webkit-user-select: none; 20 | + user-select: none; 21 | +} 22 | + 23 | .graphiql-container .doc-type-description p:first-child , 24 | .graphiql-container .doc-type-description blockquote:first-child { 25 | margin-top: 0; 26 | diff --git a/package.json b/package.json 27 | index 49b420d..724efd4 100644 28 | --- a/package.json 29 | +++ b/package.json 30 | @@ -65,6 +65,7 @@ 31 | "codemirror": "^5.47.0", 32 | "codemirror-graphql": "^0.8.3", 33 | "copy-to-clipboard": "^3.2.0", 34 | + "html-to-react": "1.3.4", 35 | "markdown-it": "^8.4.2" 36 | }, 37 | "peerDependencies": { 38 | diff --git a/src/components/DocExplorer.js b/src/components/DocExplorer.js 39 | index b6ff977..9cf92fc 100644 40 | --- a/src/components/DocExplorer.js 41 | +++ b/src/components/DocExplorer.js 42 | @@ -97,6 +97,7 @@ export class DocExplorer extends React.Component { 43 | } else { 44 | content = ( 45 | 50 | diff --git a/src/components/DocExplorer/FieldDoc.js b/src/components/DocExplorer/FieldDoc.js 51 | index f0ca1f5..3011369 100644 52 | --- a/src/components/DocExplorer/FieldDoc.js 53 | +++ b/src/components/DocExplorer/FieldDoc.js 54 | @@ -11,9 +11,11 @@ import PropTypes from 'prop-types'; 55 | import Argument from './Argument'; 56 | import MarkdownContent from './MarkdownContent'; 57 | import TypeLink from './TypeLink'; 58 | +import { GraphQLSchema } from 'graphql'; 59 | 60 | export default class FieldDoc extends React.Component { 61 | static propTypes = { 62 | + schema: PropTypes.instanceOf(GraphQLSchema), 63 | field: PropTypes.object, 64 | onClickType: PropTypes.func, 65 | }; 66 | @@ -38,6 +40,8 @@ export default class FieldDoc extends React.Component { 67 | 68 | 69 | 75 | @@ -49,12 +53,19 @@ export default class FieldDoc extends React.Component { 76 | 77 | return ( 78 |
79 | +
80 | + {'field'} 81 | +
82 | 88 | {field.deprecationReason && 89 | } 95 | diff --git a/src/components/DocExplorer/MarkdownContent.js b/src/components/DocExplorer/MarkdownContent.js 96 | index 8a5fc03..878da75 100644 97 | --- a/src/components/DocExplorer/MarkdownContent.js 98 | +++ b/src/components/DocExplorer/MarkdownContent.js 99 | @@ -8,6 +8,9 @@ 100 | import React from 'react'; 101 | import PropTypes from 'prop-types'; 102 | import MD from 'markdown-it'; 103 | +import { GraphQLSchema } from 'graphql'; 104 | +import TypeLink from './TypeLink'; 105 | +import htmlToReact from 'html-to-react'; 106 | 107 | const md = new MD(); 108 | 109 | @@ -15,6 +18,8 @@ export default class MarkdownContent extends React.Component { 110 | static propTypes = { 111 | markdown: PropTypes.string, 112 | className: PropTypes.string, 113 | + schema: PropTypes.instanceOf(GraphQLSchema), 114 | + onClickType: PropTypes.func 115 | }; 116 | 117 | shouldComponentUpdate(nextProps) { 118 | @@ -27,11 +32,33 @@ export default class MarkdownContent extends React.Component { 119 | return
; 120 | } 121 | 122 | - return ( 123 | -
127 | - ); 128 | + let html = md.render(markdown, { sanitize: true }); 129 | + 130 | + html = `
${html}
` 131 | + 132 | + let typeMap = this.props.schema.getTypeMap(); 133 | + let processNodeDefinitions = new htmlToReact.ProcessNodeDefinitions(React); 134 | + let instructions = [ 135 | + { 136 | + shouldProcessNode: (node) => { 137 | + return node && node.name && node.name === "a"; 138 | + }, 139 | + processNode: (node, children, index) => { 140 | + let name = node.children.map((child) => child.data).join(""); 141 | + let type = typeMap[name] 142 | + if (type === undefined) 143 | + return processNodeDefinitions.processDefaultNode(node, children, index) 144 | + else 145 | + return ; 146 | + } 147 | + }, { 148 | + shouldProcessNode: (node) => true, 149 | + processNode: processNodeDefinitions.processDefaultNode 150 | + } 151 | + ]; 152 | + var htmlToReactParser = new htmlToReact.Parser(); 153 | + var markup = htmlToReactParser.parseWithInstructions(html, () => true, instructions); 154 | + 155 | + return markup; 156 | } 157 | } 158 | diff --git a/src/components/DocExplorer/SchemaDoc.js b/src/components/DocExplorer/SchemaDoc.js 159 | index 93f7d91..6fbf1a0 100644 160 | --- a/src/components/DocExplorer/SchemaDoc.js 161 | +++ b/src/components/DocExplorer/SchemaDoc.js 162 | @@ -32,6 +32,8 @@ export default class SchemaDoc extends React.Component { 163 | return ( 164 |
165 | !field.isDeprecated) 177 | .map(field => ( 178 | ( 186 | 195 | +
196 | + {'type'} 197 | +
198 | 204 | @@ -173,7 +180,7 @@ export default class TypeDoc extends React.Component { 205 | handleShowDeprecated = () => this.setState({ showDeprecated: true }); 206 | } 207 | 208 | -function Field({ type, field, onClickType, onClickField }) { 209 | +function Field({ type, field, onClickType, onClickField, schema }) { 210 | return ( 211 |
212 | 215 | {field.description && ( 216 | 222 | )} 223 | {field.deprecationReason && ( 224 | 230 | @@ -222,11 +233,15 @@ function EnumValue({ value }) { 231 |
232 |
{value.name}
233 | 239 | {value.deprecationReason && ( 240 | 246 | diff --git a/src/components/GraphiQL.js b/src/components/GraphiQL.js 247 | index 38f9ca7..eaa87b8 100644 248 | --- a/src/components/GraphiQL.js 249 | +++ b/src/components/GraphiQL.js 250 | @@ -728,7 +728,10 @@ export class GraphiQL extends React.Component { 251 | 252 | handlePrettifyQuery = () => { 253 | const editor = this.getQueryEditor(); 254 | - editor.setValue(print(parse(editor.getValue()))); 255 | + editor.setValue(print(parse(editor.getValue()).replace(/^( +)/mg, "$1$1"))); 256 | + const variables = JSON.stringify(JSON.parse(this.state.variables), null, 4); 257 | + const varEditor = this.variableEditorComponent.getCodeMirror(); 258 | + varEditor.setValue(variables); 259 | }; 260 | 261 | handleMergeQuery = () => { 262 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-plugin-graphiql", 3 | "version": "2.3.0", 4 | "description": "HAPI plugin for GraphiQL integration", 5 | "keywords": [ "hapi", "plugin", "graphiql", "graphql" ], 6 | "main": "./hapi-plugin-graphiql.js", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/rse/hapi-plugin-graphiql.git" 11 | }, 12 | "author": { 13 | "name": "Dr. Ralf S. Engelschall", 14 | "email": "rse@engelschall.com", 15 | "url": "http://engelschall.com" 16 | }, 17 | "homepage": "https://github.com/rse/hapi-plugin-graphiql", 18 | "bugs": "https://github.com/rse/hapi-plugin-graphiql/issues", 19 | "peerDependencies": { 20 | "@hapi/hapi": ">=18.0.0" 21 | }, 22 | "dependencies": { 23 | "@hapi/boom": "7.4.2", 24 | "mz": "2.7.0", 25 | "bluebird": "3.5.5", 26 | "jquery": "3.4.1", 27 | "whatwg-fetch": "3.0.0", 28 | "react": "16.8.6", 29 | "react-dom": "16.8.6", 30 | "react-dom-factories": "1.0.2", 31 | "graphql": "14.3.1", 32 | "graphiql": "0.13.0", 33 | "nunjucks": "3.2.0" 34 | }, 35 | "devDependencies": { 36 | "@hapi/hapi": "18.3.1", 37 | "babel-eslint": "10.0.1", 38 | "eslint": "5.16.0", 39 | "eslint-config-standard": "12.0.0", 40 | "eslint-plugin-standard": "4.0.0", 41 | "eslint-plugin-promise": "4.1.1", 42 | "eslint-plugin-import": "2.17.3", 43 | "eslint-plugin-node": "9.1.0" 44 | }, 45 | "engines": { 46 | "node": ">=8.0.0" 47 | }, 48 | "scripts": { 49 | "prepublishOnly": "eslint --config eslint.yaml hapi-plugin-graphiql.js" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rse/hapi-plugin-graphiql/1348120bc90961c62b228bf11f643e6f2d4a9eb5/screenshot.png --------------------------------------------------------------------------------