Skip to content

Commit

Permalink
always use the latest header values for introspection
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasheyenbrock committed May 31, 2022
1 parent 1e6fc68 commit b4aa4d8
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 122 deletions.
6 changes: 6 additions & 0 deletions .changeset/friendly-cougars-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'graphiql': patch
'@graphiql/react': patch
---

Always use the current value of the headers for the introspection request
72 changes: 70 additions & 2 deletions packages/graphiql-react/src/editor/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { DocumentNode, OperationDefinitionNode } from 'graphql';
import { VariableToType } from 'graphql-language-service';
import { ReactNode, useMemo, useState } from 'react';

import { useStorageContext } from '../storage';
import { createContextHook, createNullableContext } from '../utility/context';
import { STORAGE_KEY as STORAGE_KEY_HEADERS } from './header-editor';
import { useSynchronizeValue } from './hooks';
import { STORAGE_KEY_QUERY } from './query-editor';
import { CodeMirrorEditor } from './types';
import { STORAGE_KEY as STORAGE_KEY_VARIABLES } from './variable-editor';

export type CodeMirrorEditorWithOperationFacts = CodeMirrorEditor & {
documentAST: DocumentNode | null;
Expand All @@ -21,13 +26,25 @@ export type EditorContextType = {
setQueryEditor(newEditor: CodeMirrorEditorWithOperationFacts): void;
setResponseEditor(newEditor: CodeMirrorEditor): void;
setVariableEditor(newEditor: CodeMirrorEditor): void;
initialHeaders: string;
initialQuery: string;
initialVariables: string;
};

export const EditorContext = createNullableContext<EditorContextType>(
'EditorContext',
);

export function EditorContextProvider(props: { children: ReactNode }) {
type EditorContextProviderProps = {
children: ReactNode;
defaultQuery?: string;
headers?: string;
query?: string;
variables?: string;
};

export function EditorContextProvider(props: EditorContextProviderProps) {
const storage = useStorageContext();
const [headerEditor, setHeaderEditor] = useState<CodeMirrorEditor | null>(
null,
);
Expand All @@ -42,6 +59,23 @@ export function EditorContextProvider(props: { children: ReactNode }) {
null,
);

useSynchronizeValue(headerEditor, props.headers);
useSynchronizeValue(queryEditor, props.query);
useSynchronizeValue(variableEditor, props.variables);

// We store this in state but never update it. By passing a function we only
// need to compute it lazily during the initial render.
const [initialValues] = useState(() => ({
initialHeaders: props.headers ?? storage?.get(STORAGE_KEY_HEADERS) ?? '',
initialQuery:
props.query ??
storage?.get(STORAGE_KEY_QUERY) ??
props.defaultQuery ??
DEFAULT_QUERY,
initialVariables:
props.variables ?? storage?.get(STORAGE_KEY_VARIABLES) ?? '',
}));

const value = useMemo<EditorContextType>(
() => ({
headerEditor,
Expand All @@ -52,8 +86,9 @@ export function EditorContextProvider(props: { children: ReactNode }) {
setQueryEditor,
setResponseEditor,
setVariableEditor,
...initialValues,
}),
[headerEditor, queryEditor, responseEditor, variableEditor],
[headerEditor, initialValues, queryEditor, responseEditor, variableEditor],
);

return (
Expand All @@ -64,3 +99,36 @@ export function EditorContextProvider(props: { children: ReactNode }) {
}

export const useEditorContext = createContextHook(EditorContext);

const DEFAULT_QUERY = `# Welcome to GraphiQL
#
# GraphiQL is an in-browser tool for writing, validating, and
# testing GraphQL queries.
#
# Type queries into this side of the screen, and you will see intelligent
# typeaheads aware of the current GraphQL type schema and live syntax and
# validation errors highlighted within the text.
#
# GraphQL queries typically start with a "{" character. Lines that start
# with a # are ignored.
#
# An example GraphQL query might look like:
#
# {
# field(arg: "value") {
# subField
# }
# }
#
# Keyboard shortcuts:
#
# Prettify Query: Shift-Ctrl-P (or press the prettify button above)
#
# Merge Query: Shift-Ctrl-M (or press the merge button above)
#
# Run Query: Ctrl-Enter (or press the play button above)
#
# Auto Complete: Ctrl-Space (or just start typing)
#
`;
16 changes: 4 additions & 12 deletions packages/graphiql-react/src/editor/header-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useRef } from 'react';

import { useStorageContext } from '../storage';
import { commonKeys, importCodeMirror } from './common';
import { useEditorContext } from './context';
import {
Expand All @@ -12,7 +11,6 @@ import {
useMergeQuery,
usePrettifyEditors,
useResizeEditor,
useSynchronizeValue,
} from './hooks';

export type UseHeaderEditorArgs = {
Expand All @@ -21,7 +19,6 @@ export type UseHeaderEditorArgs = {
onRunQuery?: EmptyCallback;
readOnly?: boolean;
shouldPersistHeaders?: boolean;
value?: string;
};

export function useHeaderEditor({
Expand All @@ -30,17 +27,14 @@ export function useHeaderEditor({
onRunQuery,
readOnly = false,
shouldPersistHeaders = false,
value,
}: UseHeaderEditorArgs = {}) {
const { headerEditor, setHeaderEditor } = useEditorContext({
const { initialHeaders, headerEditor, setHeaderEditor } = useEditorContext({
nonNull: true,
caller: useHeaderEditor,
});
const storage = useStorageContext();
const merge = useMergeQuery({ caller: useHeaderEditor });
const prettify = usePrettifyEditors({ caller: useHeaderEditor });
const ref = useRef<HTMLDivElement>(null);
const initialValue = useRef(value ?? storage?.get(STORAGE_KEY) ?? '');

useEffect(() => {
let isActive = true;
Expand All @@ -60,7 +54,7 @@ export function useHeaderEditor({
}

const newEditor = CodeMirror(container, {
value: initialValue.current || '',
value: initialHeaders,
lineNumbers: true,
tabSize: 2,
mode: { name: 'javascript', json: true },
Expand Down Expand Up @@ -108,9 +102,7 @@ export function useHeaderEditor({
return () => {
isActive = false;
};
}, [editorTheme, readOnly, setHeaderEditor]);

useSynchronizeValue(headerEditor, value);
}, [editorTheme, initialHeaders, readOnly, setHeaderEditor]);

useChangeHandler(
headerEditor,
Expand All @@ -129,4 +121,4 @@ export function useHeaderEditor({
return ref;
}

const STORAGE_KEY = 'headers';
export const STORAGE_KEY = 'headers';
57 changes: 9 additions & 48 deletions packages/graphiql-react/src/editor/query-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,13 @@ import {
useMergeQuery,
usePrettifyEditors,
useResizeEditor,
useSynchronizeValue,
} from './hooks';
import { CodeMirrorEditor, CodeMirrorType } from './types';
import { normalizeWhitespace } from './whitespace';

type OnClickReference = (reference: SchemaReference) => void;

export type UseQueryEditorArgs = {
defaultValue?: string;
editorTheme?: string;
externalFragments?: string | FragmentDefinitionNode[];
onEdit?: EditCallback;
Expand All @@ -45,11 +43,9 @@ export type UseQueryEditorArgs = {
onRunQuery?: EmptyCallback;
readOnly?: boolean;
validationRules?: ValidationRule[];
value?: string;
};

export function useQueryEditor({
defaultValue = DEFAULT_VALUE,
editorTheme = 'graphiql',
externalFragments,
onEdit,
Expand All @@ -58,13 +54,17 @@ export function useQueryEditor({
onRunQuery,
readOnly = false,
validationRules,
value,
}: UseQueryEditorArgs = {}) {
const { schema } = useSchemaContext({
nonNull: true,
caller: useQueryEditor,
});
const { queryEditor, setQueryEditor, variableEditor } = useEditorContext({
const {
initialQuery,
queryEditor,
setQueryEditor,
variableEditor,
} = useEditorContext({
nonNull: true,
caller: useQueryEditor,
});
Expand Down Expand Up @@ -95,10 +95,6 @@ export function useQueryEditor({
};
}, [explorer]);

const initialValue = useRef(
value ?? storage?.get(STORAGE_KEY_QUERY) ?? defaultValue,
);

useEffect(() => {
let isActive = true;

Expand All @@ -124,7 +120,7 @@ export function useQueryEditor({
}

const newEditor = CodeMirror(container, {
value: initialValue.current || '',
value: initialQuery,
lineNumbers: true,
tabSize: 2,
foldGutter: true,
Expand Down Expand Up @@ -218,7 +214,7 @@ export function useQueryEditor({
return () => {
isActive = false;
};
}, [editorTheme, readOnly, setQueryEditor]);
}, [editorTheme, initialQuery, readOnly, setQueryEditor]);

/**
* We don't use the generic `useChangeHandler` hook here because we want to
Expand Down Expand Up @@ -320,8 +316,6 @@ export function useQueryEditor({
codeMirrorRef,
);

useSynchronizeValue(queryEditor, value);

useCompletion(queryEditor);

useKeyMap(queryEditor, ['Cmd-Enter', 'Ctrl-Enter'], onRunQuery);
Expand Down Expand Up @@ -412,39 +406,6 @@ function useSynchronizeExternalFragments(

const AUTO_COMPLETE_AFTER_KEY = /^[a-zA-Z0-9_@(]$/;

const DEFAULT_VALUE = `# Welcome to GraphiQL
#
# GraphiQL is an in-browser tool for writing, validating, and
# testing GraphQL queries.
#
# Type queries into this side of the screen, and you will see intelligent
# typeaheads aware of the current GraphQL type schema and live syntax and
# validation errors highlighted within the text.
#
# GraphQL queries typically start with a "{" character. Lines that start
# with a # are ignored.
#
# An example GraphQL query might look like:
#
# {
# field(arg: "value") {
# subField
# }
# }
#
# Keyboard shortcuts:
#
# Prettify Query: Shift-Ctrl-P (or press the prettify button above)
#
# Merge Query: Shift-Ctrl-M (or press the merge button above)
#
# Run Query: Ctrl-Enter (or press the play button above)
#
# Auto Complete: Ctrl-Space (or just start typing)
#
`;

const STORAGE_KEY_QUERY = 'query';
export const STORAGE_KEY_QUERY = 'query';

const STORAGE_KEY_OPERATION_NAME = 'operationName';
20 changes: 8 additions & 12 deletions packages/graphiql-react/src/editor/variable-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect, useRef } from 'react';

import { useStorageContext } from '../storage';
import { commonKeys, importCodeMirror } from './common';
import { useEditorContext } from './context';
import {
Expand All @@ -12,7 +11,6 @@ import {
useMergeQuery,
usePrettifyEditors,
useResizeEditor,
useSynchronizeValue,
} from './hooks';
import { CodeMirrorType } from './types';

Expand All @@ -21,26 +19,26 @@ export type UseVariableEditorArgs = {
onEdit?: EditCallback;
onRunQuery?: EmptyCallback;
readOnly?: boolean;
value?: string;
};

export function useVariableEditor({
editorTheme = 'graphiql',
onEdit,
onRunQuery,
readOnly = false,
value,
}: UseVariableEditorArgs = {}) {
const { variableEditor, setVariableEditor } = useEditorContext({
const {
initialVariables,
variableEditor,
setVariableEditor,
} = useEditorContext({
nonNull: true,
caller: useVariableEditor,
});
const storage = useStorageContext();
const merge = useMergeQuery({ caller: useVariableEditor });
const prettify = usePrettifyEditors({ caller: useVariableEditor });
const ref = useRef<HTMLDivElement>(null);
const codeMirrorRef = useRef<CodeMirrorType>();
const initialValue = useRef(value ?? storage?.get(STORAGE_KEY) ?? '');

useEffect(() => {
let isActive = true;
Expand All @@ -63,7 +61,7 @@ export function useVariableEditor({
}

const newEditor = CodeMirror(container, {
value: initialValue.current || '',
value: initialVariables,
lineNumbers: true,
tabSize: 2,
mode: 'graphql-variables',
Expand Down Expand Up @@ -122,9 +120,7 @@ export function useVariableEditor({
return () => {
isActive = false;
};
}, [editorTheme, readOnly, setVariableEditor]);

useSynchronizeValue(variableEditor, value);
}, [editorTheme, initialVariables, readOnly, setVariableEditor]);

useChangeHandler(variableEditor, onEdit, STORAGE_KEY);

Expand All @@ -139,4 +135,4 @@ export function useVariableEditor({
return ref;
}

const STORAGE_KEY = 'variables';
export const STORAGE_KEY = 'variables';
Loading

0 comments on commit b4aa4d8

Please sign in to comment.