Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

always use the latest header values for introspection #2451

Merged
merged 1 commit into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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