Skip to content

Commit d5dfe99

Browse files
authoredJun 4, 2024··
[graphiql] Allow for full customization of GraphiQL (#3314)
1 parent e7dcce3 commit d5dfe99

File tree

3 files changed

+93
-18
lines changed

3 files changed

+93
-18
lines changed
 

‎.changeset/kind-fireants-rush.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'graphql-yoga': minor
3+
'@graphql-yoga/graphiql': minor
4+
---
5+
6+
Allow for full customization of the GraphiQL page.
7+
8+
Props from the `YogaGraphiQL` are now forwarded to the underlying GraphiQL components.
9+
10+
The `graphiql` option field type of the Yoga server as also been updated to document which options
11+
are configurable from the server side. Only serializable options are available.

‎packages/graphiql/src/YogaGraphiQL.tsx

+26-18
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,7 @@ const getOperationWithFragments = (
3232
};
3333
};
3434

35-
export type YogaGraphiQLProps = Omit<
36-
GraphiQLProps,
37-
| 'fetcher'
38-
| 'isHeadersEditorEnabled'
39-
| 'defaultEditorToolsVisibility'
40-
| 'onToggleDocs'
41-
| 'toolbar'
42-
| 'onSchemaChange'
43-
| 'query'
44-
| 'onEditQuery'
45-
> &
35+
export type YogaGraphiQLProps = Partial<GraphiQLProps> &
4636
Partial<Omit<LoadFromUrlOptions, 'headers'>> & {
4737
title?: string;
4838
/**
@@ -98,6 +88,15 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement {
9888
const urlLoader = useMemo(() => new UrlLoader(), []);
9989

10090
const fetcher = useMemo(() => {
91+
if (props.fetcher) {
92+
if (props.endpoint) {
93+
// eslint-disable-next-line no-console
94+
console.warn(
95+
'You are using a custom fetcher and an endpoint. The endpoint will be ignored.',
96+
);
97+
}
98+
return props.fetcher;
99+
}
101100
const executor = urlLoader.getExecutorAsync(endpoint, {
102101
subscriptionsProtocol: SubscriptionProtocol.GRAPHQL_SSE,
103102
subscriptionsEndpoint: endpoint, // necessary because graphql-sse in graphql-tools url-loader defaults to endpoint+'/stream'
@@ -123,7 +122,7 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement {
123122
},
124123
});
125124
};
126-
}, [urlLoader, endpoint]) as Fetcher;
125+
}, [urlLoader, endpoint, props.fetcher]) as Fetcher;
127126

128127
const [params, setParams] = useUrlSearchParams(
129128
{
@@ -138,25 +137,34 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement {
138137
showAttribution: true,
139138
});
140139

140+
if (props.query && !props.onEditQuery) {
141+
// eslint-disable-next-line no-console
142+
console.warn(
143+
'If you provide `query` prop, you should also provide `onEditQuery` prop to handle query changes.',
144+
);
145+
}
146+
141147
return (
142148
<div className="graphiql-container">
143149
<GraphiQLProvider
144-
defaultHeaders={props.defaultHeaders}
145-
fetcher={fetcher}
146-
headers={props.headers}
150+
// default values that can be override by props
151+
shouldPersistHeaders
147152
plugins={[explorer]}
148-
query={query}
149153
schemaDescription={true}
150-
shouldPersistHeaders={props.shouldPersistHeaders ?? true}
154+
query={query}
155+
{...props}
156+
fetcher={fetcher}
151157
>
152158
<GraphiQLInterface
153159
isHeadersEditorEnabled
154160
defaultEditorToolsVisibility
155-
onEditQuery={query => {
161+
{...props}
162+
onEditQuery={(query, ast) => {
156163
setParams({
157164
query,
158165
});
159166
setQuery(query);
167+
props.onEditQuery?.(query, ast);
160168
}}
161169
>
162170
<GraphiQL.Logo>

‎packages/graphql-yoga/src/plugins/use-graphiql.ts

+56
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,62 @@ export type GraphiQLOptions = {
6262
* Whether to use the GET HTTP method for queries when querying the original schema
6363
*/
6464
useGETForQueries?: boolean;
65+
/**
66+
* "external" fragments that will be included in the query document (depending on usage)
67+
*/
68+
externalFragments?: string;
69+
/**
70+
* The maximum number of executed operations to store.
71+
* @default 20
72+
*/
73+
maxHistoryLength?: number;
74+
/**
75+
* Whether target GraphQL server support deprecation of input values.
76+
* @default false
77+
*/
78+
inputValueDeprecation?: boolean;
79+
/**
80+
* Custom operation name for the introspection query.
81+
*/
82+
introspectionQueryName?: string;
83+
/**
84+
* Whether to include schema description in introspection query.
85+
* @default false
86+
*/
87+
schemaDescription?: boolean;
88+
/**
89+
* Editor theme
90+
* @default "graphiql"
91+
*/
92+
editorTheme?: string;
93+
/**
94+
* Sets the key map to use when using the editor.
95+
* @default 'sublime'
96+
*/
97+
keyMap?: 'sublime' | 'emacs' | 'vim';
98+
defaultEditorToolsVisibility?: boolean | 'variables' | 'headers';
99+
isHeadersEditorEnabled?: boolean;
100+
disableTabs?: boolean;
101+
/**
102+
* Whether to include `isRepeatable` flag on directives.
103+
* @default false
104+
*/
105+
directiveIsRepeatable?: boolean;
106+
experimentalFragmentVariables?: boolean;
107+
/**
108+
* Set to `true` in order to convert all GraphQL comments (marked with # sign) to descriptions (""")
109+
* GraphQL has built-in support for transforming descriptions to comments (with `print`), but not while
110+
* parsing. Turning the flag on will support the other way as well (`parse`)
111+
*/
112+
commentDescriptions?: boolean;
113+
/**
114+
* Timeout in milliseconds
115+
*/
116+
timeout?: number;
117+
/**
118+
* Retry attempts
119+
*/
120+
retry?: number;
65121
};
66122

67123
export type GraphiQLRendererOptions = {

0 commit comments

Comments
 (0)
Please sign in to comment.