Skip to content

Commit

Permalink
feat(graphiql): add support for plugins (backstage#1532)
Browse files Browse the repository at this point in the history
Signed-off-by: Mikko Korhonen <mikko.korhonen@op.fi>
  • Loading branch information
kmikko authored Nov 7, 2024
1 parent 134a482 commit d6ab6de
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 49 deletions.
5 changes: 5 additions & 0 deletions workspaces/graphiql/.changeset/fresh-pets-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage-community/plugin-graphiql': minor
---

Add support for GraphiQL plugins.
4 changes: 3 additions & 1 deletion workspaces/graphiql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
},
"resolutions": {
"@types/react": "^18",
"@types/react-dom": "^18"
"@types/react-dom": "^18",
"graphql-language-service": "5.1.7",
"@graphiql/react": "0.22.1"
},
"prettier": "@spotify/prettier-config",
"lint-staged": {
Expand Down
2 changes: 2 additions & 0 deletions workspaces/graphiql/plugins/graphiql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ If all you need is a static list of endpoints, the plugin exports a `GraphQLEndp
+ body: JSON.stringify(params),
+ }).then(res => res.json());
+ },
+ // Optional list of plugins
+ plugins: []
+ }
+ ]),
+ }),
Expand Down
4 changes: 4 additions & 0 deletions workspaces/graphiql/plugins/graphiql/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ApiRef } from '@backstage/core-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { ErrorApi } from '@backstage/core-plugin-api';
import { FetchApi } from '@backstage/core-plugin-api';
import type { GraphiQLPlugin } from '@graphiql/react';
import { JSX as JSX_2 } from 'react';
import { OAuthApi } from '@backstage/core-plugin-api';
import { default as React_2 } from 'react';
Expand All @@ -25,6 +26,7 @@ export type EndpointConfig = {
[name in string]: string;
};
fetchApi?: FetchApi;
plugins?: GraphiQLPlugin[];
};

// @public (undocumented)
Expand All @@ -35,6 +37,7 @@ export type GithubEndpointConfig = {
errorApi?: ErrorApi;
fetchApi?: FetchApi;
githubAuthApi: OAuthApi;
plugins?: GraphiQLPlugin[];
};

// @public (undocumented)
Expand Down Expand Up @@ -64,6 +67,7 @@ export type GraphQLEndpoint = {
id: string;
title: string;
fetcher: (body: any) => Promise<any>;
plugins?: GraphiQLPlugin[];
};

// @public (undocumented)
Expand Down
7 changes: 7 additions & 0 deletions workspaces/graphiql/plugins/graphiql/dev/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ createDevApp()
id: 'countries',
title: 'Countries',
url: 'https://countries.trevorblades.com/',
plugins: [
{
content: () => <div>Hello from My plugin</div>,
icon: GraphiQLIcon,
title: 'My plugin',
},
],
}),
]);
},
Expand Down
1 change: 1 addition & 0 deletions workspaces/graphiql/plugins/graphiql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"@testing-library/dom": "^10.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^15.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/codemirror": "^5.0.0",
"@types/react-dom": "^18.2.19",
"canvas": "^2.11.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import React from 'react';
import { renderInTestApp } from '@backstage/test-utils';
import { GraphiQLBrowser } from './GraphiQLBrowser';

jest.mock('graphiql', () => ({ GraphiQL: () => '<GraphiQL />' }));
import { GraphiQLIcon } from '../../GraphiQLIcon';
import userEvent from '@testing-library/user-event';

describe('GraphiQLBrowser', () => {
it('should render error text if there are no endpoints', async () => {
Expand All @@ -45,5 +45,33 @@ describe('GraphiQLBrowser', () => {
);
rendered.getByText('Endpoint A');
rendered.getByText('Endpoint B');
expect(rendered.getByTestId('graphiql-container')).not.toBeNull();
});

it('should render plugins', async () => {
const rendered = await renderInTestApp(
<GraphiQLBrowser
endpoints={[
{
id: 'a',
title: 'Endpoint A',
async fetcher() {},
plugins: [
{
content: () => <div>Hello from My plugin</div>,
icon: GraphiQLIcon,
title: 'My plugin',
},
],
},
]}
/>,
);

await userEvent.click(
rendered.getByRole('button', { name: 'Show My plugin' }),
);

expect(rendered.getByText('Hello from My plugin')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const GraphiQLBrowser = (props: GraphiQLBrowserProps) => {
return <Typography variant="h4">No endpoints available</Typography>;
}

const { id, fetcher } = endpoints[tabIndex];
const { id, fetcher, plugins } = endpoints[tabIndex];
const storage = StorageBucket.forLocalStorage(`plugin/graphiql/data/${id}`);

return (
Expand All @@ -80,7 +80,12 @@ export const GraphiQLBrowser = (props: GraphiQLBrowserProps) => {
</Tabs>
<Divider />
<div className={classes.graphiQlWrapper}>
<GraphiQL key={tabIndex} fetcher={fetcher} storage={storage} />
<GraphiQL
key={tabIndex}
fetcher={fetcher}
storage={storage}
plugins={plugins}
/>
</div>
</Suspense>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { GraphQLBrowseApi, GraphQLEndpoint } from './types';
import { ErrorApi, FetchApi, OAuthApi } from '@backstage/core-plugin-api';
import type { GraphiQLPlugin } from '@graphiql/react';

/**
* Helper for generic http endpoints
Expand All @@ -35,6 +36,10 @@ export type EndpointConfig = {
* Fetch API to use instead of browser fetch()
*/
fetchApi?: FetchApi;
/**
* A list of plugins to be used with GraphiQL Explorer
*/
plugins?: GraphiQLPlugin[];
};

/** @public */
Expand All @@ -57,13 +62,17 @@ export type GithubEndpointConfig = {
* GitHub Auth API used to authenticate requests.
*/
githubAuthApi: OAuthApi;
/**
* A list of plugins to be used with GraphiQL Explorer
*/
plugins?: GraphiQLPlugin[];
};

/** @public */
export class GraphQLEndpoints implements GraphQLBrowseApi {
// Create a support
static create(config: EndpointConfig): GraphQLEndpoint {
const { id, title, url, method = 'POST', fetchApi } = config;
const { id, title, url, method = 'POST', fetchApi, plugins } = config;
return {
id,
title,
Expand All @@ -81,6 +90,7 @@ export class GraphQLEndpoints implements GraphQLBrowseApi {
});
return res.json();
},
plugins,
};
}

Expand All @@ -98,6 +108,7 @@ export class GraphQLEndpoints implements GraphQLBrowseApi {
errorApi,
fetchApi,
githubAuthApi,
plugins,
} = config;
type ResponseBody = {
errors?: Array<{ type: string; message: string }>;
Expand Down Expand Up @@ -161,6 +172,7 @@ export class GraphQLEndpoints implements GraphQLBrowseApi {

return await doRequest();
},
plugins,
};
}

Expand Down
3 changes: 3 additions & 0 deletions workspaces/graphiql/plugins/graphiql/src/lib/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { createApiRef } from '@backstage/core-plugin-api';
import type { GraphiQLPlugin } from '@graphiql/react';

/** @public */
export type GraphQLEndpoint = {
Expand All @@ -28,6 +29,8 @@ export type GraphQLEndpoint = {
// The body parameter is equivalent to the POST body to be JSON-serialized, and the
// return value should be the equivalent of a parsed JSON response from that POST.
fetcher: (body: any) => Promise<any>;

plugins?: GraphiQLPlugin[];
};

/** @public */
Expand Down
22 changes: 22 additions & 0 deletions workspaces/graphiql/plugins/graphiql/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,25 @@
*/

import '@testing-library/jest-dom';
// eslint-disable-next-line no-restricted-imports
import { TextDecoder, TextEncoder } from 'util';

Object.assign(global, { TextDecoder, TextEncoder });

// https://github.com/jsdom/jsdom/issues/3002#issuecomment-1118039915
Range.prototype.getBoundingClientRect = () => ({
x: 0,
y: 0,
bottom: 0,
height: 0,
left: 0,
right: 0,
top: 0,
width: 0,
toJSON: () => '',
});
Range.prototype.getClientRects = () => ({
item: () => null,
length: 0,
[Symbol.iterator]: jest.fn(),
});
Loading

0 comments on commit d6ab6de

Please sign in to comment.