Skip to content

Commit

Permalink
feat: [RFC] Introduce Query Params
Browse files Browse the repository at this point in the history
  • Loading branch information
harshithpabbati committed Feb 5, 2021
1 parent 59296d5 commit 3662456
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 14 deletions.
18 changes: 18 additions & 0 deletions packages/graphiql-2-rfc-context/src/api/actions/browserActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export enum BrowserActionTypes {
QueryStringParamsChanged = 'QueryStringParamsChanged',
}

export type BrowserAction = QueryStringParamsChangedAction;

export const queryStringParamsChangedAction = (
parameter: string,
value: string,
) =>
({
type: BrowserActionTypes.QueryStringParamsChanged,
payload: { parameter, value },
} as const);

export type QueryStringParamsChangedAction = ReturnType<
typeof queryStringParamsChangedAction
>;
1 change: 1 addition & 0 deletions packages/graphiql-2-rfc-context/src/api/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './useOperation';
export * from './useQueryFacts';
export * from './useSchema';
export * from './useValueRef';
export * from './useQueryParams';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useBrowserContext } from '../providers/GraphiQLBrowserProvider';

export default function useQueryParams(name: string) {
const { queryStringParams } = useBrowserContext();
return queryStringParams[name];
}
1 change: 1 addition & 0 deletions packages/graphiql-2-rfc-context/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
export * from './providers/GraphiQLEditorsProvider';
export * from './providers/GraphiQLSessionProvider';
export * from './providers/GraphiQLSchemaProvider';
export * from './providers/GraphiQLBrowserProvider';
export * from './hooks';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as React from 'react';
import { parseQueryStringURL, updateQueryStringURL } from 'graphiql-toolkit';
import { BrowserState } from '../types';

import {
BrowserAction,
BrowserActionTypes,
queryStringParamsChangedAction,
} from '../actions/browserActions';

export type BrowserReducer = React.Reducer<BrowserState, BrowserAction>;
export interface BrowserHandlers {
changeQueryStringParams: (parameter: string, value: string) => void;
dispatch: React.Dispatch<BrowserAction>;
}

export const initialBrowserState: BrowserState = {
queryStringParams: {
operation:
parseQueryStringURL(window.location.search).operation ||
parseQueryStringURL(window.location.search).query,
variables: parseQueryStringURL(window.location.search).variables,
operationName: parseQueryStringURL(window.location.search).operationName,
},
queryStringParamsURL: '',
};

export const initialBrowserContextState: BrowserState & BrowserHandlers = {
changeQueryStringParams: () => null,
dispatch: () => null,
...initialBrowserState,
};

export const BrowserContext = React.createContext<
BrowserState & BrowserHandlers
>(initialBrowserContextState);

export const useBrowserContext = () => React.useContext(BrowserContext);

const browserReducer: BrowserReducer = (state, action) => {
switch (action.type) {
case BrowserActionTypes.QueryStringParamsChanged: {
const { parameter, value } = action.payload;
return {
...state,
queryStringParams: {
...state.queryStringParams,
[parameter]: value,
},
queryStringParamsURL: updateQueryStringURL(
state.queryStringParams,
parameter,
value,
),
};
}
default: {
return state;
}
}
};

export type BrowserProviderProps = {
children: React.ReactNode;
};

export function BrowserProvider({ children }: BrowserProviderProps) {
const [state, dispatch] = React.useReducer<BrowserReducer>(
browserReducer,
initialBrowserState,
);

const changeQueryStringParams = React.useCallback(
(parameter: string, value: string) =>
dispatch(queryStringParamsChangedAction(parameter, value)),
[dispatch],
);

React.useEffect(() => {
history.replaceState(null, document.title, state.queryStringParamsURL);
}, [state.queryStringParamsURL]);

return (
<BrowserContext.Provider
value={{
...state,
changeQueryStringParams,
dispatch,
}}>
{children}
</BrowserContext.Provider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
*/

/* global monaco */
import React, { useCallback } from 'react';
import { GraphQLSchema } from 'graphql';
import { SchemaConfig, Fetcher } from '../../types';
Expand Down
5 changes: 5 additions & 0 deletions packages/graphiql-2-rfc-context/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ export type SessionState = {
subscription?: Unsubscribable | null;
operationName?: string; // current operation name
};

export type BrowserState = {
queryStringParams: any;
queryStringParamsURL: string;
};
24 changes: 14 additions & 10 deletions packages/graphiql-2-rfc-context/src/components/GraphiQL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
SessionProvider,
SessionContext,
} from '../api/providers/GraphiQLSessionProvider';
import { BrowserProvider } from '../api/providers/GraphiQLBrowserProvider';

import { getFetcher } from '../api/common';

import { Unsubscribable, Fetcher, ReactNodeLike } from '../types';
Expand Down Expand Up @@ -126,16 +128,18 @@ export const GraphiQL: React.FC<GraphiQLProps> = props => {
<SchemaProvider
fetcher={fetcher}
config={{ uri: props.uri, ...props.schemaConfig }}>
<SessionProvider fetcher={fetcher} sessionId={0}>
<GraphiQLInternals
{...{
formatResult,
formatError,
...props,
}}>
{props.children}
</GraphiQLInternals>
</SessionProvider>
<BrowserProvider>
<SessionProvider fetcher={fetcher} sessionId={0}>
<GraphiQLInternals
{...{
formatResult,
formatError,
...props,
}}>
{props.children}
</GraphiQLInternals>
</SessionProvider>
</BrowserProvider>
</SchemaProvider>
</EditorsProvider>
</I18nextProvider>
Expand Down
11 changes: 10 additions & 1 deletion packages/graphiql-2-rfc-context/src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* global monaco */
/** @jsx jsx */
/**
* Copyright (c) 2021 GraphQL Contributors.
Expand All @@ -15,6 +16,7 @@ import EditorWrapper from '../components/common/EditorWrapper';

import { useSessionContext } from '../api/providers/GraphiQLSessionProvider';
import { useEditorsContext } from '../api/providers/GraphiQLEditorsProvider';
import { useBrowserContext } from '../api/providers/GraphiQLBrowserProvider';

export type QueryEditorProps = {
onEdit?: (value: string) => void;
Expand All @@ -37,6 +39,7 @@ export function QueryEditor(props: QueryEditorProps) {
const [ignoreChangeEvent, setIgnoreChangeEvent] = React.useState(false);
const cachedValueRef = React.useRef<string>(props.operation ?? '');
const session = useSessionContext();
const browser = useBrowserContext();

const { loadEditor } = useEditorsContext();

Expand All @@ -49,6 +52,7 @@ export function QueryEditor(props: QueryEditorProps) {

React.useEffect(() => {
require('monaco-graphql/esm/monaco.contribution');
session.changeOperation(browser.queryStringParams.operation);

// Lazily require to ensure requiring GraphiQL outside of a Browser context
// does not produce an error.
Expand Down Expand Up @@ -88,7 +92,12 @@ export function QueryEditor(props: QueryEditorProps) {
editor.setValue(thisValue);
}
setIgnoreChangeEvent(false);
}, [session, session.operation, session.operation.text]);
}, [
session,
session.operation,
session.operation.text,
browser.queryStringParams.operation,
]);

React.useEffect(() => {
const editor = editorRef.current;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

/* global monaco */
/** @jsx jsx */
import { jsx } from 'theme-ui';
import { GraphQLType } from 'graphql';
Expand All @@ -15,6 +15,7 @@ import EditorWrapper from '../components/common/EditorWrapper';

import { useEditorsContext } from '../api/providers/GraphiQLEditorsProvider';
import { useSessionContext } from '../api/providers/GraphiQLSessionProvider';
import { useBrowserContext } from '../api/providers/GraphiQLBrowserProvider';

import type { EditorOptions } from '../types';
// import useQueryFacts from '../api/hooks/useQueryFacts';
Expand Down Expand Up @@ -45,6 +46,7 @@ export type VariableEditorProps = {
*/
export function VariableEditor(props: VariableEditorProps) {
const session = useSessionContext();
const browser = useBrowserContext();
// const queryFacts = useQueryFacts();
const [ignoreChangeEvent, setIgnoreChangeEvent] = React.useState(false);
const editorRef = React.useRef<monaco.editor.IStandaloneCodeEditor>();
Expand All @@ -54,6 +56,7 @@ export function VariableEditor(props: VariableEditorProps) {
// const variableToType = queryFacts?.variableToType

React.useEffect(() => {
session.changeVariables(browser.queryStringParams.variables);
// Lazily require to ensure requiring GraphiQL outside of a Browser context
// does not produce an error.

Expand Down Expand Up @@ -89,6 +92,7 @@ export function VariableEditor(props: VariableEditorProps) {
if (!ignoreChangeEvent) {
cachedValueRef.current = editor.getValue();
session.changeVariables(cachedValueRef.current);
browser.changeQueryStringParams('variables', cachedValueRef.current);
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -104,7 +108,8 @@ export function VariableEditor(props: VariableEditorProps) {
// user-input changes which could otherwise result in an infinite
// event loop.
setIgnoreChangeEvent(true);
if (session.variables.text !== cachedValueRef.current) {
const vars = session.variables.text;
if (vars && vars !== cachedValueRef.current) {
const thisValue = session.variables.text || '';
cachedValueRef.current = thisValue;
editor.setValue(thisValue);
Expand Down
53 changes: 53 additions & 0 deletions packages/graphiql-toolkit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
/**
* Copyright (c) 2020 GraphQL Contributors.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

export * from './types';

// TODO: move the most useful utilities from graphiql to here

export function parseQueryStringURL(searchURL: string) {
const parameters: any = {};
searchURL
.substr(1)
.split('&')
.forEach(function (entry) {
const eq = entry.indexOf('=');
if (eq >= 0) {
parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(
entry.slice(eq + 1),
);
}
});
return parameters;
}

export function updateQueryStringURL(
parameters: any,
parameter: string,
value: string,
) {
parameters[parameter] = value;
let newSearch: string = '';
if (
parameters.operationName !== undefined ||
parameters.operation !== undefined ||
parameters.variables !== undefined
) {
newSearch =
'?' +
Object.keys(parameters)
.filter(function (key) {
return Boolean(parameters[key]);
})
.map(function (key) {
return (
encodeURIComponent(key) +
'=' +
encodeURIComponent(parameters?.[key])
);
})
.join('&');
}
return newSearch;
}

0 comments on commit 3662456

Please sign in to comment.