Skip to content

Commit e4a3620

Browse files
authored
fix code exporter on early init, new hooks: useOperationsEditorState() (#3341)
this abstracts away some editor specific details until we can leverage `<Suspense />` - add useOperationsEditorState() - add useVariablesEditorState() - match up @graphiql/react globals - simplify plugin implementations
1 parent 17069e7 commit e4a3620

File tree

7 files changed

+75
-18
lines changed

7 files changed

+75
-18
lines changed

.changeset/six-bears-smile.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@graphiql/plugin-code-exporter': patch
3+
'@graphiql/plugin-explorer': patch
4+
'@graphiql/react': patch
5+
---
6+
7+
Fix code exporter plugin on early init, add hooks

packages/graphiql-plugin-code-exporter/src/index.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEditorContext, type GraphiQLPlugin } from '@graphiql/react';
1+
import { useOperationsEditorState, type GraphiQLPlugin } from '@graphiql/react';
22
import React from 'react';
33
import GraphiQLCodeExporter, {
44
GraphiQLCodeExporterProps,
@@ -10,13 +10,12 @@ import './index.css';
1010
type GraphiQLCodeExporterPluginProps = Omit<GraphiQLCodeExporterProps, 'query'>;
1111

1212
function GraphiQLCodeExporterPlugin(props: GraphiQLCodeExporterPluginProps) {
13-
const { queryEditor } = useEditorContext({ nonNull: true });
14-
13+
const [operationsString] = useOperationsEditorState();
1514
return (
1615
<GraphiQLCodeExporter
1716
codeMirrorTheme="graphiql"
1817
{...props}
19-
query={queryEditor!.getValue()}
18+
query={operationsString}
2019
/>
2120
);
2221
}

packages/graphiql-plugin-code-exporter/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default defineConfig({
2424
output: {
2525
chunkFileNames: '[name].[format].js',
2626
globals: {
27+
'@graphiql/react': 'GraphiQL.React',
2728
graphql: 'GraphiQL.GraphQL',
2829
react: 'React',
2930
'react-dom': 'ReactDOM',

packages/graphiql-plugin-explorer/src/index.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
useEditorContext,
44
useExecutionContext,
55
useSchemaContext,
6+
useOperationsEditorState,
67
} from '@graphiql/react';
78
import {
89
Explorer as GraphiQLExplorer,
@@ -116,30 +117,29 @@ const styles = {
116117
};
117118

118119
export type GraphiQLExplorerPluginProps = Omit<
119-
Omit<GraphiQLExplorerProps, 'query'>,
120-
'onEdit'
120+
GraphiQLExplorerProps,
121+
'onEdit' | 'query'
121122
>;
122123

123124
function ExplorerPlugin(props: GraphiQLExplorerPluginProps) {
124-
const { setOperationName, queryEditor } = useEditorContext({ nonNull: true });
125+
const { setOperationName } = useEditorContext({ nonNull: true });
125126
const { schema } = useSchemaContext({ nonNull: true });
126127
const { run } = useExecutionContext({ nonNull: true });
128+
129+
// handle running the current operation from the plugin
127130
const handleRunOperation = useCallback(
128131
(operationName: string | null) => {
129132
if (operationName) {
133+
// set the plugin-defined operation name before executing
130134
setOperationName(operationName);
131135
}
132136
run();
133137
},
134138
[run, setOperationName],
135139
);
136-
// todo: document how to do this!
137-
const handleEditOperation = useCallback(
138-
(value: string) => queryEditor?.setValue(value),
139-
[queryEditor],
140-
);
141140

142-
const operationDocument = queryEditor?.getValue() ?? '';
141+
// load the current editor tab state into the explorer
142+
const [operationsString, handleEditOperations] = useOperationsEditorState();
143143

144144
return (
145145
<GraphiQLExplorer
@@ -152,14 +152,16 @@ function ExplorerPlugin(props: GraphiQLExplorerPluginProps) {
152152
checkboxUnchecked={checkboxUnchecked}
153153
checkboxChecked={checkboxChecked}
154154
styles={styles}
155-
query={operationDocument}
156-
onEdit={handleEditOperation}
155+
query={operationsString}
156+
onEdit={handleEditOperations}
157157
{...props}
158158
/>
159159
);
160160
}
161161

162-
export function explorerPlugin(props: GraphiQLExplorerPluginProps) {
162+
export function explorerPlugin(
163+
props: GraphiQLExplorerPluginProps,
164+
): GraphiQLPlugin {
163165
return {
164166
title: 'GraphiQL Explorer',
165167
icon: () => (
@@ -185,5 +187,5 @@ export function explorerPlugin(props: GraphiQLExplorerPluginProps) {
185187
</svg>
186188
),
187189
content: () => <ExplorerPlugin {...props} />,
188-
} as GraphiQLPlugin;
190+
};
189191
}

packages/graphiql-react/src/editor/hooks.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { EditorChange, EditorConfiguration } from 'codemirror';
33
import type { SchemaReference } from 'codemirror-graphql/utils/SchemaReference';
44
import copyToClipboard from 'copy-to-clipboard';
55
import { parse, print } from 'graphql';
6-
import { useCallback, useEffect } from 'react';
6+
import { useCallback, useEffect, useMemo } from 'react';
77

88
import { useExplorerContext } from '../explorer';
99
import { usePluginContext } from '../plugin';
@@ -332,3 +332,47 @@ export function useAutoCompleteLeafs({
332332
return result;
333333
}, [getDefaultFieldNames, queryEditor, schema]);
334334
}
335+
336+
// https://react.dev/learn/you-might-not-need-an-effect
337+
338+
/**
339+
* useState-like hook for current tab operations editor state
340+
*/
341+
export function useOperationsEditorState(): [
342+
opString: string,
343+
handleEditOperations: (content: string) => void,
344+
] {
345+
const { queryEditor } = useEditorContext({
346+
nonNull: true,
347+
});
348+
const opString = queryEditor?.getValue() ?? '';
349+
const handleEditOperations = useCallback(
350+
(value: string) => queryEditor?.setValue(value),
351+
[queryEditor],
352+
);
353+
return useMemo(
354+
() => [opString, handleEditOperations],
355+
[opString, handleEditOperations],
356+
);
357+
}
358+
359+
/**
360+
* useState-like hook for variables tab operations editor state
361+
*/
362+
export function useVariablesEditorState(): [
363+
varsString: string,
364+
handleEditVariables: (content: string) => void,
365+
] {
366+
const { variableEditor } = useEditorContext({
367+
nonNull: true,
368+
});
369+
const varsString = variableEditor?.getValue() ?? '';
370+
const handleEditVariables = useCallback(
371+
(value: string) => variableEditor?.setValue(value),
372+
[variableEditor],
373+
);
374+
return useMemo(
375+
() => [varsString, handleEditVariables],
376+
[varsString, handleEditVariables],
377+
);
378+
}

packages/graphiql-react/src/editor/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export {
1616
useCopyQuery,
1717
useMergeQuery,
1818
usePrettifyEditors,
19+
useOperationsEditorState,
20+
useVariablesEditorState,
1921
} from './hooks';
2022
export { useQueryEditor } from './query-editor';
2123
export { useResponseEditor } from './response-editor';

packages/graphiql-react/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export {
1616
useQueryEditor,
1717
useResponseEditor,
1818
useVariableEditor,
19+
useOperationsEditorState,
20+
useVariablesEditorState,
1921
VariableEditor,
2022
} from './editor';
2123
export {

0 commit comments

Comments
 (0)