GraphQL query editor transformer works!

This commit is contained in:
Gregory Schier
2023-03-14 19:08:18 -07:00
parent b9294ff994
commit 3247190a46
9 changed files with 51 additions and 21 deletions

Binary file not shown.

View File

@@ -81,13 +81,11 @@ function FormRow({
pair, pair,
onChange, onChange,
onDelete, onDelete,
onFocus,
isLast, isLast,
}: { }: {
pair: PairWithId; pair: PairWithId;
onChange: (pair: PairWithId) => void; onChange: (pair: PairWithId) => void;
onDelete?: (pair: PairWithId) => void; onDelete?: (pair: PairWithId) => void;
onFocus?: (pair: PairWithId) => void;
isLast?: boolean; isLast?: boolean;
}) { }) {
return ( return (
@@ -99,7 +97,6 @@ function FormRow({
label="Name" label="Name"
name="name" name="name"
onChange={(name) => onChange({ id: pair.id, header: { name } })} onChange={(name) => onChange({ id: pair.id, header: { name } })}
onFocus={() => onFocus?.(pair)}
placeholder="name" placeholder="name"
useEditor={{ useTemplating: true }} useEditor={{ useTemplating: true }}
/> />
@@ -110,7 +107,6 @@ function FormRow({
label="Value" label="Value"
name="value" name="value"
onChange={(value) => onChange({ id: pair.id, header: { value } })} onChange={(value) => onChange({ id: pair.id, header: { value } })}
onFocus={() => onFocus?.(pair)}
placeholder="value" placeholder="value"
useEditor={{ useTemplating: true }} useEditor={{ useTemplating: true }}
/> />
@@ -118,7 +114,8 @@ function FormRow({
<IconButton <IconButton
icon="trash" icon="trash"
onClick={() => onDelete(pair)} onClick={() => onDelete(pair)}
className="invisible group-hover:visible" tabIndex={-1}
className={classnames('opacity-0 group-hover:opacity-100')}
/> />
)} )}
</div> </div>

View File

@@ -1,10 +1,12 @@
import classnames from 'classnames'; import classnames from 'classnames';
import { act } from 'react-dom/test-utils';
import { useActiveRequest } from '../hooks/useActiveRequest'; import { useActiveRequest } from '../hooks/useActiveRequest';
import { useSendRequest } from '../hooks/useSendRequest'; import { useSendRequest } from '../hooks/useSendRequest';
import { useUpdateRequest } from '../hooks/useUpdateRequest'; import { useUpdateRequest } from '../hooks/useUpdateRequest';
import { Editor } from './core/Editor'; import { Editor } from './core/Editor';
import { HeaderEditor } from './HeaderEditor';
import { TabContent, Tabs } from './core/Tabs/Tabs'; import { TabContent, Tabs } from './core/Tabs/Tabs';
import { GraphQLEditor } from './editors/GraphQLEditor';
import { HeaderEditor } from './HeaderEditor';
import { UrlBar } from './UrlBar'; import { UrlBar } from './UrlBar';
interface Props { interface Props {
@@ -63,21 +65,20 @@ export function RequestPane({ fullHeight, className }: Props) {
{activeRequest.bodyType === 'json' ? ( {activeRequest.bodyType === 'json' ? (
<Editor <Editor
key={activeRequest.id} key={activeRequest.id}
useTemplating
className="!bg-gray-50" className="!bg-gray-50"
heightMode={fullHeight ? 'full' : 'auto'} heightMode={fullHeight ? 'full' : 'auto'}
useTemplating
defaultValue={activeRequest.body ?? ''} defaultValue={activeRequest.body ?? ''}
contentType="application/json" contentType="application/json"
onChange={(body) => updateRequest.mutate({ body })} onChange={(body) => updateRequest.mutate({ body })}
/> />
) : activeRequest.bodyType === 'graphql' ? ( ) : activeRequest.bodyType === 'graphql' ? (
<Editor <GraphQLEditor
key={activeRequest.id} key={activeRequest.id}
className="!bg-gray-50"
heightMode={fullHeight ? 'full' : 'auto'}
useTemplating useTemplating
defaultValue={activeRequest.body ?? ''} className="!bg-gray-50"
contentType="application/graphql+json" defaultValue={activeRequest?.body ?? ''}
heightMode={fullHeight ? 'full' : 'auto'}
onChange={(body) => updateRequest.mutate({ body })} onChange={(body) => updateRequest.mutate({ body })}
/> />
) : ( ) : (

View File

@@ -6,6 +6,7 @@ import { VStack } from './core/Stacks';
export default function RouteError() { export default function RouteError() {
const error = useRouteError(); const error = useRouteError();
const stringified = JSON.stringify(error); const stringified = JSON.stringify(error);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const message = (error as any).message ?? stringified; const message = (error as any).message ?? stringified;
return ( return (
<div className="flex items-center justify-center h-full"> <div className="flex items-center justify-center h-full">

View File

@@ -31,7 +31,7 @@ export default function Workspace() {
: 'grid-cols-1 grid-rows-[minmax(0,auto)_minmax(0,100%)]', : 'grid-cols-1 grid-rows-[minmax(0,auto)_minmax(0,100%)]',
)} )}
> >
<RequestPane fullHeight={isH} className={classnames(!isH && 'pr-2 pb-0')} /> <RequestPane fullHeight={isH} className={classnames(!isH && 'pr-2')} />
<ResponsePane /> <ResponsePane />
</div> </div>
</div> </div>

View File

@@ -33,7 +33,6 @@ import {
} from '@codemirror/view'; } from '@codemirror/view';
import { tags as t } from '@lezer/highlight'; import { tags as t } from '@lezer/highlight';
import { graphqlLanguageSupport } from 'cm6-graphql'; import { graphqlLanguageSupport } from 'cm6-graphql';
import { debouncedAutocompletionDisplay } from './autocomplete';
import { twig } from './twig/extension'; import { twig } from './twig/extension';
import { url } from './url/extension'; import { url } from './url/extension';
@@ -78,7 +77,7 @@ export const myHighlightStyle = HighlightStyle.define([
// ]); // ]);
const syntaxExtensions: Record<string, LanguageSupport> = { const syntaxExtensions: Record<string, LanguageSupport> = {
'application/graphql+json': graphqlLanguageSupport(), 'application/graphql': graphqlLanguageSupport(),
'application/json': json(), 'application/json': json(),
'application/javascript': javascript(), 'application/javascript': javascript(),
'text/html': html(), 'text/html': html(),

View File

@@ -1,6 +1,6 @@
import * as T from '@radix-ui/react-tabs'; import * as T from '@radix-ui/react-tabs';
import classnames from 'classnames'; import classnames from 'classnames';
import type { ReactElement, ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { Button } from '../Button'; import { Button } from '../Button';
import type { DropdownMenuRadioItem, DropdownMenuRadioProps } from '../Dropdown'; import type { DropdownMenuRadioItem, DropdownMenuRadioProps } from '../Dropdown';
@@ -81,7 +81,7 @@ export function Tabs({ defaultValue, label, children, tabs, className, tabListCl
)} )}
> >
{t.options.items.find((i) => i.value === t.options?.value)?.label ?? ''} {t.options.items.find((i) => i.value === t.options?.value)?.label ?? ''}
<Icon icon="triangleDown" className="-mr-1.5" /> <Icon icon="triangleDown" className="-mr-1.5 opacity-40" />
</Button> </Button>
</T.Trigger> </T.Trigger>
); );

View File

@@ -0,0 +1,32 @@
import { useMemo } from 'react';
import type { EditorProps } from '../core/Editor';
import { Editor } from '../core/Editor';
type Props = Pick<
EditorProps,
'heightMode' | 'onChange' | 'defaultValue' | 'className' | 'useTemplating'
>;
export function GraphQLEditor({ defaultValue, onChange, ...props }: Props) {
const { query } = useMemo(() => {
try {
const { query } = JSON.parse(defaultValue ?? '{}');
return { query };
} catch (err) {
return { query: 'failed to parse' };
}
}, [defaultValue]);
const handleChange = (query: string) => {
onChange?.(JSON.stringify({ query }, null, 2));
};
return (
<Editor
defaultValue={query ?? ''}
onChange={handleChange}
contentType="application/graphql"
{...props}
/>
);
}

View File

@@ -11,11 +11,11 @@ const darkTheme: AppTheme = {
blackPoint: 0.2, blackPoint: 0.2,
colors: { colors: {
gray: '#6b5b98', gray: '#6b5b98',
red: '#ee3b3b', red: '#ff417b',
orange: '#ff9411', orange: '#ff9411',
yellow: '#dcc73b', yellow: '#e8d13f',
green: '#44cb44', green: '#43e76f',
blue: '#2e91ff', blue: '#219dff',
pink: '#f670f6', pink: '#f670f6',
violet: '#b176ff', violet: '#b176ff',
}, },