Skip to content

Commit

Permalink
assume all context values are nullable and create hooks to consume in…
Browse files Browse the repository at this point in the history
…dividual contexts (#2449)
  • Loading branch information
thomasheyenbrock authored May 31, 2022
1 parent 4e7b907 commit a0b02ed
Show file tree
Hide file tree
Showing 19 changed files with 258 additions and 236 deletions.
6 changes: 6 additions & 0 deletions .changeset/ninety-suns-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphiql/react': minor
'graphiql': patch
---

Assume all context values are nullable and create hooks to consume individual contexts
37 changes: 12 additions & 25 deletions packages/graphiql-react/src/editor/context.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { fillLeafs, GetDefaultFieldNamesFn } from '@graphiql/toolkit';
import { DocumentNode, OperationDefinitionNode } from 'graphql';
import { VariableToType } from 'graphql-language-service';
import {
createContext,
ReactNode,
useCallback,
useMemo,
useState,
} from 'react';
import { ReactNode, useCallback, useMemo, useState } from 'react';
import { useSchemaContext } from '../schema';

import { useSchemaWithError } from '../schema';
import { createContextHook, createNullableContext } from '../utility/context';
import { useCopyQuery, useMergeQuery, usePrettifyEditors } from './hooks';
import { CodeMirrorEditor } from './types';

Expand All @@ -35,22 +30,9 @@ export type EditorContextType = {
setVariableEditor(newEditor: CodeMirrorEditor): void;
};

export const EditorContext = createContext<EditorContextType>({
autoCompleteLeafs() {
return undefined;
},
copy() {},
merge() {},
prettify() {},
headerEditor: null,
queryEditor: null,
responseEditor: null,
variableEditor: null,
setHeaderEditor() {},
setQueryEditor() {},
setResponseEditor() {},
setVariableEditor() {},
});
export const EditorContext = createNullableContext<EditorContextType>(
'EditorContext',
);

type EditorContextProviderProps = {
children: ReactNode;
Expand All @@ -59,7 +41,10 @@ type EditorContextProviderProps = {
};

export function EditorContextProvider(props: EditorContextProviderProps) {
const { schema } = useSchemaWithError('component', 'EditorContextProvider');
const { schema } = useSchemaContext({
nonNull: true,
caller: EditorContextProvider,
});
const [headerEditor, setHeaderEditor] = useState<CodeMirrorEditor | null>(
null,
);
Expand Down Expand Up @@ -161,3 +146,5 @@ export function EditorContextProvider(props: EditorContextProviderProps) {
</EditorContext.Provider>
);
}

export const useEditorContext = createContextHook(EditorContext);
25 changes: 10 additions & 15 deletions packages/graphiql-react/src/editor/header-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useContext, useEffect, useRef } from 'react';
import { useEffect, useRef } from 'react';

import { StorageContext } from '../storage';
import { useStorageContext } from '../storage';
import { commonKeys, importCodeMirror } from './common';
import { EditorContext } from './context';
import { useEditorContext } from './context';
import {
EditCallback,
EmptyCallback,
Expand Down Expand Up @@ -30,18 +30,13 @@ export function useHeaderEditor({
shouldPersistHeaders = false,
value,
}: UseHeaderEditorArgs = {}) {
const context = useContext(EditorContext);
const storage = useContext(StorageContext);
const { headerEditor, merge, prettify, setHeaderEditor } = useEditorContext({
nonNull: true,
caller: useHeaderEditor,
});
const storage = useStorageContext();
const ref = useRef<HTMLDivElement>(null);

if (!context) {
throw new Error(
'Tried to call the `useHeaderEditor` hook without the necessary context. Make sure that the `EditorContextProvider` from `@graphiql/react` is rendered higher in the tree.',
);
}

const { headerEditor, setHeaderEditor } = context;

const initialValue = useRef(value ?? storage?.get(STORAGE_KEY) ?? '');

useEffect(() => {
Expand Down Expand Up @@ -123,8 +118,8 @@ export function useHeaderEditor({
useCompletion(headerEditor);

useKeyMap(headerEditor, ['Cmd-Enter', 'Ctrl-Enter'], onRunQuery);
useKeyMap(headerEditor, ['Shift-Ctrl-P'], context.prettify);
useKeyMap(headerEditor, ['Shift-Ctrl-M'], context.merge);
useKeyMap(headerEditor, ['Shift-Ctrl-P'], prettify);
useKeyMap(headerEditor, ['Shift-Ctrl-M'], merge);

useResizeEditor(headerEditor, ref);

Expand Down
14 changes: 7 additions & 7 deletions packages/graphiql-react/src/editor/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { mergeAst } from '@graphiql/toolkit';
import { EditorChange } from 'codemirror';
import copyToClipboard from 'copy-to-clipboard';
import { GraphQLSchema, parse, print } from 'graphql';
import { RefObject, useCallback, useContext, useEffect, useRef } from 'react';
import { RefObject, useCallback, useEffect, useRef } from 'react';

import { ExplorerContext } from '../explorer';
import { useSchemaWithError } from '../schema';
import { StorageContext } from '../storage';
import { useExplorerContext } from '../explorer';
import { useSchemaContext } from '../schema';
import { useStorageContext } from '../storage';
import debounce from '../utility/debounce';
import { onHasCompletion } from './completion';
import { CodeMirrorEditorWithOperationFacts } from './context';
Expand All @@ -30,7 +30,7 @@ export function useChangeHandler(
callback: EditCallback | undefined,
storageKey: string | null,
) {
const storage = useContext(StorageContext);
const storage = useStorageContext();
useEffect(() => {
if (!editor) {
return;
Expand All @@ -54,8 +54,8 @@ export function useChangeHandler(
}

export function useCompletion(editor: CodeMirrorEditor | null) {
const { schema } = useSchemaWithError('hook', 'useCompletion');
const explorer = useContext(ExplorerContext);
const { schema } = useSchemaContext({ nonNull: true, caller: useCompletion });
const explorer = useExplorerContext();
useEffect(() => {
if (!editor) {
return;
Expand Down
7 changes: 6 additions & 1 deletion packages/graphiql-react/src/editor/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { onHasCompletion } from './completion';
import { ImagePreview } from './components';
import { EditorContext, EditorContextProvider } from './context';
import {
EditorContext,
EditorContextProvider,
useEditorContext,
} from './context';
import { useHeaderEditor } from './header-editor';
import { useQueryEditor } from './query-editor';
import { useResponseEditor } from './response-editor';
Expand All @@ -20,6 +24,7 @@ export {
ImagePreview,
EditorContext,
EditorContextProvider,
useEditorContext,
useHeaderEditor,
useQueryEditor,
useResponseEditor,
Expand Down
48 changes: 28 additions & 20 deletions packages/graphiql-react/src/editor/query-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import type {
ValidationRule,
} from 'graphql';
import { getOperationFacts } from 'graphql-language-service';
import { MutableRefObject, useContext, useEffect, useRef } from 'react';
import { MutableRefObject, useEffect, useRef } from 'react';

import { ExplorerContext } from '../explorer';
import { useExplorerContext } from '../explorer';
import { markdown } from '../markdown';
import { useSchemaWithError } from '../schema';
import { StorageContext } from '../storage';
import { useSchemaContext } from '../schema';
import { useStorageContext } from '../storage';
import debounce from '../utility/debounce';
import { commonKeys, importCodeMirror } from './common';
import { CodeMirrorEditorWithOperationFacts, EditorContext } from './context';
import {
CodeMirrorEditorWithOperationFacts,
useEditorContext,
} from './context';
import {
EditCallback,
EmptyCallback,
Expand Down Expand Up @@ -51,21 +54,26 @@ export function useQueryEditor({
validationRules,
value,
}: UseQueryEditorArgs = {}) {
const { schema } = useSchemaWithError('hook', 'useQueryEditor');
const editorContext = useContext(EditorContext);
const storage = useContext(StorageContext);
const explorer = useContext(ExplorerContext);
const { schema } = useSchemaContext({
nonNull: true,
caller: useQueryEditor,
});
const {
copy,
merge,
prettify,
queryEditor,
setQueryEditor,
variableEditor,
} = useEditorContext({
nonNull: true,
caller: useQueryEditor,
});
const storage = useStorageContext();
const explorer = useExplorerContext();
const ref = useRef<HTMLDivElement>(null);
const codeMirrorRef = useRef<CodeMirrorType>();

if (!editorContext) {
throw new Error(
'Tried to call the `useQueryEditor` hook without the necessary context. Make sure that the `EditorContextProvider` from `@graphiql/react` is rendered higher in the tree.',
);
}

const { queryEditor, setQueryEditor, variableEditor } = editorContext;

const onClickReferenceRef = useRef<OnClickReference>(() => {});
useEffect(() => {
onClickReferenceRef.current = reference => {
Expand Down Expand Up @@ -315,17 +323,17 @@ export function useQueryEditor({
useCompletion(queryEditor);

useKeyMap(queryEditor, ['Cmd-Enter', 'Ctrl-Enter'], onRunQuery);
useKeyMap(queryEditor, ['Shift-Ctrl-C'], editorContext.copy);
useKeyMap(queryEditor, ['Shift-Ctrl-C'], copy);
useKeyMap(
queryEditor,
[
'Shift-Ctrl-P',
// Shift-Ctrl-P is hard coded in Firefox for private browsing so adding an alternative to Pretiffy
'Shift-Ctrl-F',
],
editorContext.prettify,
prettify,
);
useKeyMap(queryEditor, ['Shift-Ctrl-M'], editorContext.merge);
useKeyMap(queryEditor, ['Shift-Ctrl-M'], merge);

useResizeEditor(queryEditor, ref);

Expand Down
27 changes: 11 additions & 16 deletions packages/graphiql-react/src/editor/response-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { formatError } from '@graphiql/toolkit';
import type { Position, Token } from 'codemirror';
import { ComponentType, useContext, useEffect, useRef } from 'react';
import { ComponentType, useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import { useSchemaContext } from '../schema';

import { commonKeys, importCodeMirror } from './common';
import { ImagePreview } from './components';
import { EditorContext } from './context';
import { useEditorContext } from './context';
import { useResizeEditor, useSynchronizeValue } from './hooks';
import { CodeMirrorEditor } from './types';
import { useSchemaWithError } from '../schema';

export type ResponseTooltipType = ComponentType<{ pos: Position }>;

Expand All @@ -23,11 +23,14 @@ export function useResponseEditor({
editorTheme = 'graphiql',
value,
}: UseResponseEditorArgs = {}) {
const { fetchError, validationErrors } = useSchemaWithError(
'hook',
'useResponseEditor',
);
const editorContext = useContext(EditorContext);
const { fetchError, validationErrors } = useSchemaContext({
nonNull: true,
caller: useResponseEditor,
});
const { responseEditor, setResponseEditor } = useEditorContext({
nonNull: true,
caller: useResponseEditor,
});
const ref = useRef<HTMLDivElement>(null);

const responseTooltipRef = useRef<ResponseTooltipType | undefined>(
Expand All @@ -37,14 +40,6 @@ export function useResponseEditor({
responseTooltipRef.current = ResponseTooltip;
}, [ResponseTooltip]);

if (!editorContext) {
throw new Error(
'Tried to call the `useResponseEditor` hook without the necessary context. Make sure that the `EditorContextProvider` from `@graphiql/react` is rendered higher in the tree.',
);
}

const { responseEditor, setResponseEditor } = editorContext;

const initialValue = useRef(value);

useEffect(() => {
Expand Down
30 changes: 15 additions & 15 deletions packages/graphiql-react/src/editor/variable-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useContext, useEffect, useRef } from 'react';
import { useEffect, useRef } from 'react';

import { StorageContext } from '../storage';
import { useStorageContext } from '../storage';
import { commonKeys, importCodeMirror } from './common';
import { EditorContext } from './context';
import { useEditorContext } from './context';
import {
EditCallback,
EmptyCallback,
Expand All @@ -29,19 +29,19 @@ export function useVariableEditor({
readOnly = false,
value,
}: UseVariableEditorArgs = {}) {
const context = useContext(EditorContext);
const storage = useContext(StorageContext);
const {
merge,
prettify,
variableEditor,
setVariableEditor,
} = useEditorContext({
nonNull: true,
caller: useVariableEditor,
});
const storage = useStorageContext();
const ref = useRef<HTMLDivElement>(null);
const codeMirrorRef = useRef<CodeMirrorType>();

if (!context) {
throw new Error(
'Tried to call the `useVariableEditor` hook without the necessary context. Make sure that the `EditorContextProvider` from `@graphiql/react` is rendered higher in the tree.',
);
}

const { variableEditor, setVariableEditor } = context;

const initialValue = useRef(value ?? storage?.get(STORAGE_KEY) ?? '');

useEffect(() => {
Expand Down Expand Up @@ -133,8 +133,8 @@ export function useVariableEditor({
useCompletion(variableEditor);

useKeyMap(variableEditor, ['Cmd-Enter', 'Ctrl-Enter'], onRunQuery);
useKeyMap(variableEditor, ['Shift-Ctrl-P'], context.prettify);
useKeyMap(variableEditor, ['Shift-Ctrl-M'], context.merge);
useKeyMap(variableEditor, ['Shift-Ctrl-P'], prettify);
useKeyMap(variableEditor, ['Shift-Ctrl-M'], merge);

useResizeEditor(variableEditor, ref);

Expand Down
Loading

0 comments on commit a0b02ed

Please sign in to comment.