console: improve flow between rest and graphiql page

This PR adds a button on the REST page `Create REST` which will open the rest form.
Also, to select the query we have a link which will route the user to `GraphiQL` page with the `REST` button on the Nav bar highlighted.

Demo -

https://github.com/hasura/graphql-engine-mono/assets/68095256/3fab4ef2-fe21-4be6-b52d-bf8f937888aa

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9797
GitOrigin-RevId: 848ccff74ec1489df945821f583270714ba4e770
This commit is contained in:
Varun Choudhary 2023-07-07 14:22:10 +05:30 committed by hasura-bot
parent c68f6c7ba1
commit 7d35304816
6 changed files with 99 additions and 27 deletions

View File

@ -285,6 +285,14 @@
color: #b00;
}
.graphiql-container .toolbar-button.primary {
background: #f9c548;
border: 1px solid #d5ae52;
box-shadow: none;
color: #475569;
font-weight: 500;
}
.graphiql-container .toolbar-button-group {
margin: 0 5px;
white-space: nowrap;

View File

@ -0,0 +1,38 @@
import React from 'react';
export type GraphiQLToolbarButtonProps = {
label: string;
primary: boolean;
} & React.ComponentProps<'button'>;
export const GraphiQLToolbarButton = React.forwardRef<
HTMLButtonElement,
GraphiQLToolbarButtonProps
>((props, forwardedRef) => {
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
const handleClick = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => {
try {
if (props.onClick) props.onClick(event);
setErrorMessage(null);
} catch (error: any) {
setErrorMessage(error.message);
}
};
return (
<button
className={
'toolbar-button' +
(errorMessage ? ' error' : '') +
(props.primary ? ' primary' : '')
}
title={errorMessage ? errorMessage : props.title}
onClick={handleClick}
aria-invalid={errorMessage ? 'true' : 'false'}
>
{props.label}
</button>
);
});

View File

@ -1,6 +1,7 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import React, { Component } from 'react';
import GraphiQL from 'graphiql';
import queryString from 'query-string';
import { connect } from 'react-redux';
import { FaCheckCircle, FaExclamationTriangle } from 'react-icons/fa';
import PropTypes from 'prop-types';
@ -49,6 +50,7 @@ import {
ResponseTimeWarning,
RESPONSE_TIME_CACHE_WARNING,
} from './ResponseTimeWarning';
import { GraphiQLToolbarButton } from './GraphiQLToolbarButton';
class GraphiQLWrapper extends Component {
constructor(props) {
@ -293,6 +295,9 @@ class GraphiQLWrapper extends Component {
const renderGraphiql = graphiqlProps => {
const voyagerUrl = graphqlNetworkData.consoleUrl + '/voyager-view';
const queryParams = queryString.parseUrl(window.location.href);
const highlightRestButton = queryParams.query.mode === 'rest';
let analyzerProps = {};
if (graphiqlContext) {
analyzerProps = graphiqlContext.state;
@ -345,6 +350,7 @@ class GraphiQLWrapper extends Component {
{
label: 'REST',
title: 'REST Endpoints',
primary: highlightRestButton,
onClick: () => {
trackGraphiQlToolbarButtonClick('REST');
routeToREST();
@ -361,7 +367,7 @@ class GraphiQLWrapper extends Component {
return buttons
.filter(b => !b.hide)
.map(b => {
return <GraphiQL.Button key={b.label} {...b} />;
return <GraphiQLToolbarButton key={b.label} {...b} />;
});
};

View File

@ -15,6 +15,7 @@ import globals from '../../../../../Globals';
import { FaArrowRight, FaMagic } from 'react-icons/fa';
import { IndicatorCard } from '../../../../../new-components/IndicatorCard';
import clsx from 'clsx';
import { Link } from 'react-router';
const editorOptions = {
minLines: 10,
@ -131,10 +132,10 @@ export const RestEndpointForm: React.FC<RestEndpointFormProps> = ({
editorOptions={editorOptions}
/>
<div className="text-sm absolute top-6 right-0 mt-2 mr-2">
<a href="/api/api-explorer?mode=rest">
<Link to="/api/api-explorer?mode=rest">
{request ? 'Test it in ' : 'Import from '} GraphiQL{' '}
<FaArrowRight />
</a>
</Link>
</div>
</div>
<InputField name="name" label="Name" placeholder="Name" />

View File

@ -1,21 +1,32 @@
import React from 'react';
import { Link } from 'react-router';
import TopicDescription from '../../Common/Landing/TopicDescription';
import LandingImage from './LandingImage';
import { Button } from '../../../../new-components/Button';
import _push from '../../Data/push';
import { useAppDispatch } from '../../../../storeHooks';
const landingDescription = `REST endpoints allow for the creation of a REST interface to your saved GraphQL queries and mutations.
Endpoints are accessible from /api/rest/* and inherit the authorization and permission structure from your associated GraphQL nodes.
To create a new endpoint simply test your query in GraphiQL then click the REST button on GraphiQL to configure a URL.`;
const Landing = () => (
const Landing = () => {
const dispatch = useAppDispatch();
return (
<div className="pl-md pt-md">
<div className="flex">
<h2 className="text-xl font-bold">REST Endpoints</h2>
<h2 className="text-xl font-bold pr-2">REST Endpoints</h2>
<Button
mode="primary"
size="sm"
onClick={() => dispatch(_push('/api/rest/create'))}
>
Create REST
</Button>
</div>
<div className="pt-md">
Create endpoints from GraphQL queries using{' '}
<Link to="/api/api-explorer">GraphiQL</Link>.
<div className="pb-md pr-md">
Create Rest endpoints on the top of existing GraphQL queries and
mutations{' '}
</div>
<hr className="mb-md" />
<TopicDescription
@ -27,6 +38,7 @@ const Landing = () => (
/>
<hr className="clear-both my-lg" />
</div>
);
);
};
export default Landing;

View File

@ -61,11 +61,18 @@ const ListComponent: React.FC<ListComponentProps> = ({
<Analytics name="RestList" {...REDACT_EVERYTHING}>
<div className="pl-md pt-md pr-md">
<div className="flex">
<h2 className="text-xl font-bold">REST Endpoints</h2>
<h2 className="text-xl font-bold pr-2">REST Endpoints</h2>
<Button
mode="primary"
size="sm"
onClick={() => dispatch(_push('/api/rest/create'))}
>
Create REST
</Button>
</div>
<div className="pt-md">
Create endpoints from GraphQL queries using{' '}
<Link to="/api/api-explorer">GraphiQL</Link>.
<div className="">
Create Rest endpoints on the top of existing GraphQL queries
andmutations{' '}
<div className="w-8/12 mt-sm">
REST endpoints allow for the creation of a REST interface to your
saved GraphQL queries and mutations. Endpoints are generated from