From 9b0d1f5e7b297df1cb93ce2a2a3377b51fad1731 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 11:38:24 +0200 Subject: [PATCH 01/21] plugins: create new graphiql plugin --- packages/app/package.json | 1 + packages/app/src/plugins.ts | 1 + plugins/graphiql/.eslintrc.js | 3 + plugins/graphiql/README.md | 6 + plugins/graphiql/package.json | 34 +++++ .../ExampleComponent.test.tsx | 34 +++++ .../ExampleComponent/ExampleComponent.tsx | 59 +++++++++ .../src/components/ExampleComponent/index.ts | 17 +++ .../ExampleFetchComponent.test.tsx | 28 +++++ .../ExampleFetchComponent.tsx | 119 ++++++++++++++++++ .../components/ExampleFetchComponent/index.ts | 17 +++ plugins/graphiql/src/index.ts | 17 +++ plugins/graphiql/src/plugin.test.ts | 23 ++++ plugins/graphiql/src/plugin.ts | 25 ++++ plugins/graphiql/src/setupTests.ts | 18 +++ plugins/graphiql/tsconfig.json | 7 ++ 16 files changed, 409 insertions(+) create mode 100644 plugins/graphiql/.eslintrc.js create mode 100644 plugins/graphiql/README.md create mode 100644 plugins/graphiql/package.json create mode 100644 plugins/graphiql/src/components/ExampleComponent/ExampleComponent.test.tsx create mode 100644 plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx create mode 100644 plugins/graphiql/src/components/ExampleComponent/index.ts create mode 100644 plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx create mode 100644 plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx create mode 100644 plugins/graphiql/src/components/ExampleFetchComponent/index.ts create mode 100644 plugins/graphiql/src/index.ts create mode 100644 plugins/graphiql/src/plugin.test.ts create mode 100644 plugins/graphiql/src/plugin.ts create mode 100644 plugins/graphiql/src/setupTests.ts create mode 100644 plugins/graphiql/tsconfig.json diff --git a/packages/app/package.json b/packages/app/package.json index 2c8b10ffb3..b596b541bd 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -20,6 +20,7 @@ "dependencies": { "@backstage/cli": "^0.1.1-alpha.4", "@backstage/core": "^0.1.1-alpha.4", + "@backstage/plugin-graphiql": "^0.1.1-alpha.4", "@backstage/plugin-home-page": "^0.1.1-alpha.4", "@backstage/plugin-lighthouse": "^0.1.1-alpha.4", "@backstage/plugin-welcome": "^0.1.1-alpha.4", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index 919ea0666b..b39ff948a9 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -16,3 +16,4 @@ export { plugin as HomePagePlugin } from '@backstage/plugin-home-page'; export { plugin as WelcomePlugin } from '@backstage/plugin-welcome'; export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse'; +export { plugin as Graphiql } from '@backstage/plugin-graphiql'; diff --git a/plugins/graphiql/.eslintrc.js b/plugins/graphiql/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/graphiql/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/graphiql/README.md b/plugins/graphiql/README.md new file mode 100644 index 0000000000..da0ca235fd --- /dev/null +++ b/plugins/graphiql/README.md @@ -0,0 +1,6 @@ +# Title +Welcome to the graphiql plugin! + +## Sub-section 1 + +## Sub-section 2 diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json new file mode 100644 index 0000000000..550b245e38 --- /dev/null +++ b/plugins/graphiql/package.json @@ -0,0 +1,34 @@ +{ + "name": "@backstage/plugin-graphiql", + "version": "0.1.1-alpha.4", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "backstage-cli plugin:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "clean": "backstage-cli clean" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.4", + "@testing-library/jest-dom": "^4.2.4", + "@testing-library/react": "^9.3.2", + "@testing-library/user-event": "^7.1.2", + "@types/jest": "^24.0.0", + "@types/node": "^12.0.0", + "@types/testing-library__jest-dom": "5.0.2", + "jest-fetch-mock": "^3.0.3" + }, + "dependencies": { + "@backstage/core": "^0.1.1-alpha.4", + "@backstage/theme": "^0.1.1-alpha.4", + "@material-ui/core": "^4.9.1", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.45", + "react": "16.13.1", + "react-dom": "16.13.1", + "react-use": "^13.0.0" + } +} diff --git a/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.test.tsx new file mode 100644 index 0000000000..4a61c9bf09 --- /dev/null +++ b/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; +import ExampleComponent from './ExampleComponent'; +import { ThemeProvider } from '@material-ui/core'; +import { lightTheme } from '@backstage/theme'; + +describe('ExampleComponent', () => { + it('should render', () => { + mockFetch.mockResponse(() => new Promise(() => {})); + const rendered = render( + + + , + ); + expect(rendered.getByText('Welcome to graphiql!')).toBeInTheDocument(); + }); +}); diff --git a/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx new file mode 100644 index 0000000000..c3118c5e3c --- /dev/null +++ b/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC } from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import { + InfoCard, + Header, + Page, + pageTheme, + Content, + ContentHeader, + HeaderLabel, + SupportButton, +} from '@backstage/core'; +import ExampleFetchComponent from '../ExampleFetchComponent'; + +const ExampleComponent: FC<{}> = () => ( + +
+ + +
+ + + A description of your plugin goes here. + + + + + + All content should be wrapped in a card like this. + + + + + + + + + + +
+); + +export default ExampleComponent; diff --git a/plugins/graphiql/src/components/ExampleComponent/index.ts b/plugins/graphiql/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..e785d45082 --- /dev/null +++ b/plugins/graphiql/src/components/ExampleComponent/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './ExampleComponent'; diff --git a/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx new file mode 100644 index 0000000000..7fecdc6f11 --- /dev/null +++ b/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx @@ -0,0 +1,28 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; +import ExampleFetchComponent from './ExampleFetchComponent'; + +describe('ExampleFetchComponent', () => { + it('should render', async () => { + mockFetch.mockResponse(() => new Promise(() => {})); + const rendered = render(); + expect(await rendered.findByTestId('progress')).toBeInTheDocument(); + }); +}); diff --git a/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx new file mode 100644 index 0000000000..a198bfcc3f --- /dev/null +++ b/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx @@ -0,0 +1,119 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableContainer from '@material-ui/core/TableContainer'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Alert from '@material-ui/lab/Alert'; +import { useAsync } from 'react-use'; +import { Progress } from '@backstage/core'; + +const useStyles = makeStyles({ + table: { + minWidth: 650, + }, + avatar: { + height: 32, + width: 32, + borderRadius: '50%', + }, +}); + +type User = { + gender: string; // "male" + name: { + title: string; // "Mr", + first: string; // "Duane", + last: string; // "Reed" + }; + location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} + email: string; // "duane.reed@example.com" + login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} + dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} + registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} + phone: string; // "07-2154-5651" + cell: string; // "0405-592-879" + id: { + name: string; // "TFN", + value: string; // "796260432" + }; + picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} + nat: string; // "AU" +}; + +type DenseTableProps = { + users: User[]; +}; + +export const DenseTable: FC = ({ users }) => { + const classes = useStyles(); + + return ( + + + + + Avatar + Name + Email + Nationality + + + + {users.map(user => ( + + + {user.name.first} + + + {user.name.first} {user.name.last} + + {user.email} + {user.nat} + + ))} + +
+
+ ); +}; + +const ExampleFetchComponent: FC<{}> = () => { + const { value, loading, error } = useAsync(async (): Promise => { + const response = await fetch('https://randomuser.me/api/?results=20'); + const data = await response.json(); + return data.results; + }, []); + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + + return ; +}; + +export default ExampleFetchComponent; diff --git a/plugins/graphiql/src/components/ExampleFetchComponent/index.ts b/plugins/graphiql/src/components/ExampleFetchComponent/index.ts new file mode 100644 index 0000000000..28482f9fe1 --- /dev/null +++ b/plugins/graphiql/src/components/ExampleFetchComponent/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './ExampleFetchComponent'; diff --git a/plugins/graphiql/src/index.ts b/plugins/graphiql/src/index.ts new file mode 100644 index 0000000000..3a0a0fe2d3 --- /dev/null +++ b/plugins/graphiql/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { plugin } from './plugin'; diff --git a/plugins/graphiql/src/plugin.test.ts b/plugins/graphiql/src/plugin.test.ts new file mode 100644 index 0000000000..0841d3e071 --- /dev/null +++ b/plugins/graphiql/src/plugin.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { plugin } from './plugin'; + +describe('graphiql', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/plugins/graphiql/src/plugin.ts b/plugins/graphiql/src/plugin.ts new file mode 100644 index 0000000000..f5b4496b16 --- /dev/null +++ b/plugins/graphiql/src/plugin.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createPlugin } from '@backstage/core'; +import ExampleComponent from './components/ExampleComponent'; + +export const plugin = createPlugin({ + id: 'graphiql', + register({ router }) { + router.registerRoute('/graphiql', ExampleComponent); + }, +}); diff --git a/plugins/graphiql/src/setupTests.ts b/plugins/graphiql/src/setupTests.ts new file mode 100644 index 0000000000..1a907ab8e6 --- /dev/null +++ b/plugins/graphiql/src/setupTests.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '@testing-library/jest-dom/extend-expect'; +require('jest-fetch-mock').enableMocks(); diff --git a/plugins/graphiql/tsconfig.json b/plugins/graphiql/tsconfig.json new file mode 100644 index 0000000000..7b73db2f0f --- /dev/null +++ b/plugins/graphiql/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], + "compilerOptions": { + "baseUrl": "src" + } +} From a2491cb378ddaeb795fef7239d90fdcb0e357083 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 11:48:25 +0200 Subject: [PATCH 02/21] plugins/graphiql: added plugin serve --- plugins/graphiql/dev/index.tsx | 54 ++++++++++++++++++++++++++++++++++ plugins/graphiql/package.json | 2 ++ 2 files changed, 56 insertions(+) create mode 100644 plugins/graphiql/dev/index.tsx diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx new file mode 100644 index 0000000000..2125baa6eb --- /dev/null +++ b/plugins/graphiql/dev/index.tsx @@ -0,0 +1,54 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC } from 'react'; +import ReactDOM from 'react-dom'; +import { BrowserRouter } from 'react-router-dom'; +import HomeIcon from '@material-ui/icons/Home'; +import { ThemeProvider, CssBaseline } from '@material-ui/core'; +import { + createApp, + SidebarPage, + Sidebar, + SidebarItem, + SidebarSpacer, +} from '@backstage/core'; +import { lightTheme } from '@backstage/theme'; +import { plugin } from '../src/plugin'; + +const app = createApp(); +app.registerPlugin(plugin); +const AppComponent = app.build(); + +const App: FC<{}> = () => { + return ( + + + + + + + + + + + + + + ); +}; + +ReactDOM.render(, document.getElementById('root')); diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 550b245e38..2c4f3e267e 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -7,6 +7,7 @@ "private": true, "scripts": { "build": "backstage-cli plugin:build", + "start": "backstage-cli plugin:serve", "lint": "backstage-cli lint", "test": "backstage-cli test", "clean": "backstage-cli clean" @@ -19,6 +20,7 @@ "@types/jest": "^24.0.0", "@types/node": "^12.0.0", "@types/testing-library__jest-dom": "5.0.2", + "react-router-dom": "^5.1.2", "jest-fetch-mock": "^3.0.3" }, "dependencies": { From 10b6fd5bcbe0c339690933513bb0085ea175fa4f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 14:31:57 +0200 Subject: [PATCH 03/21] plugins/graphiql: replace example page with minimal graphiql page --- plugins/graphiql/package.json | 6 +- .../ExampleComponent/ExampleComponent.tsx | 59 ------ .../ExampleFetchComponent.tsx | 119 ------------ .../GraphiQLPage.test.tsx} | 0 .../components/GraphiQLPage/GraphiQLPage.tsx | 67 +++++++ .../graphiql.d.ts} | 24 +-- .../index.ts | 2 +- .../{ExampleFetchComponent => }/index.ts | 2 +- plugins/graphiql/src/plugin.ts | 4 +- yarn.lock | 170 +++++++++++++++++- 10 files changed, 256 insertions(+), 197 deletions(-) delete mode 100644 plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx delete mode 100644 plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx rename plugins/graphiql/src/components/{ExampleComponent/ExampleComponent.test.tsx => GraphiQLPage/GraphiQLPage.test.tsx} (100%) create mode 100644 plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx rename plugins/graphiql/src/components/{ExampleFetchComponent/ExampleFetchComponent.test.tsx => GraphiQLPage/graphiql.d.ts} (56%) rename plugins/graphiql/src/components/{ExampleComponent => GraphiQLPage}/index.ts (92%) rename plugins/graphiql/src/components/{ExampleFetchComponent => }/index.ts (92%) diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 2c4f3e267e..df761be356 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -20,8 +20,8 @@ "@types/jest": "^24.0.0", "@types/node": "^12.0.0", "@types/testing-library__jest-dom": "5.0.2", - "react-router-dom": "^5.1.2", - "jest-fetch-mock": "^3.0.3" + "jest-fetch-mock": "^3.0.3", + "react-router-dom": "^5.1.2" }, "dependencies": { "@backstage/core": "^0.1.1-alpha.4", @@ -29,6 +29,8 @@ "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", + "graphiql": "^0.17.5", + "graphql": "^14.0.0", "react": "16.13.1", "react-dom": "16.13.1", "react-use": "^13.0.0" diff --git a/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx deleted file mode 100644 index c3118c5e3c..0000000000 --- a/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { FC } from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - pageTheme, - Content, - ContentHeader, - HeaderLabel, - SupportButton, -} from '@backstage/core'; -import ExampleFetchComponent from '../ExampleFetchComponent'; - -const ExampleComponent: FC<{}> = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - - - -
-); - -export default ExampleComponent; diff --git a/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index a198bfcc3f..0000000000 --- a/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { FC } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Table from '@material-ui/core/Table'; -import TableBody from '@material-ui/core/TableBody'; -import TableCell from '@material-ui/core/TableCell'; -import TableContainer from '@material-ui/core/TableContainer'; -import TableHead from '@material-ui/core/TableHead'; -import TableRow from '@material-ui/core/TableRow'; -import Alert from '@material-ui/lab/Alert'; -import { useAsync } from 'react-use'; -import { Progress } from '@backstage/core'; - -const useStyles = makeStyles({ - table: { - minWidth: 650, - }, - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable: FC = ({ users }) => { - const classes = useStyles(); - - return ( - - - - - Avatar - Name - Email - Nationality - - - - {users.map(user => ( - - - {user.name.first} - - - {user.name.first} {user.name.last} - - {user.email} - {user.nat} - - ))} - -
-
- ); -}; - -const ExampleFetchComponent: FC<{}> = () => { - const { value, loading, error } = useAsync(async (): Promise => { - const response = await fetch('https://randomuser.me/api/?results=20'); - const data = await response.json(); - return data.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return {error.message}; - } - - return ; -}; - -export default ExampleFetchComponent; diff --git a/plugins/graphiql/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx similarity index 100% rename from plugins/graphiql/src/components/ExampleComponent/ExampleComponent.test.tsx rename to plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx new file mode 100644 index 0000000000..37fae51290 --- /dev/null +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC, useState } from 'react'; +import { Tabs, Tab } from '@material-ui/core'; +import { Page, pageTheme, Content, Header, HeaderLabel } from '@backstage/core'; +import 'graphiql/graphiql.css'; +import GraphiQL from 'graphiql'; + +const tabs = [ + { + title: 'GitLab', + fetcher: async (params: any) => { + const res = await fetch('https://gitlab.com/api/graphql', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params), + }); + return res.json(); + }, + }, + { + title: 'Countries', + fetcher: async (params: any) => { + const res = await fetch('https://countries.trevorblades.com/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params), + }); + return res.json(); + }, + }, +]; + +export const GraphiQLPage: FC<{}> = () => { + const [tabIndex, setTabIndex] = useState(0); + + return ( + +
+ + +
+ + setTabIndex(value)}> + {tabs.map(({ title }, index) => ( + + ))} + + + +
+ ); +}; diff --git a/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts similarity index 56% rename from plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx rename to plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts index 7fecdc6f11..b48b764415 100644 --- a/plugins/graphiql/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts @@ -14,15 +14,17 @@ * limitations under the License. */ -import React from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleFetchComponent from './ExampleFetchComponent'; +declare module 'graphiql' { + import { FC } from 'react'; -describe('ExampleFetchComponent', () => { - it('should render', async () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render(); - expect(await rendered.findByTestId('progress')).toBeInTheDocument(); - }); -}); + type Props = { + fetcher: (params: any) => Promise; + schema?: any; + storage?: Storage; + editorTheme?: string; + }; + + const GraphiQL: FC; + + export default GraphiQL; +} diff --git a/plugins/graphiql/src/components/ExampleComponent/index.ts b/plugins/graphiql/src/components/GraphiQLPage/index.ts similarity index 92% rename from plugins/graphiql/src/components/ExampleComponent/index.ts rename to plugins/graphiql/src/components/GraphiQLPage/index.ts index e785d45082..ed11f03c40 100644 --- a/plugins/graphiql/src/components/ExampleComponent/index.ts +++ b/plugins/graphiql/src/components/GraphiQLPage/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { default } from './ExampleComponent'; +export * from './GraphiQLPage'; diff --git a/plugins/graphiql/src/components/ExampleFetchComponent/index.ts b/plugins/graphiql/src/components/index.ts similarity index 92% rename from plugins/graphiql/src/components/ExampleFetchComponent/index.ts rename to plugins/graphiql/src/components/index.ts index 28482f9fe1..ed11f03c40 100644 --- a/plugins/graphiql/src/components/ExampleFetchComponent/index.ts +++ b/plugins/graphiql/src/components/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { default } from './ExampleFetchComponent'; +export * from './GraphiQLPage'; diff --git a/plugins/graphiql/src/plugin.ts b/plugins/graphiql/src/plugin.ts index f5b4496b16..6436380c27 100644 --- a/plugins/graphiql/src/plugin.ts +++ b/plugins/graphiql/src/plugin.ts @@ -15,11 +15,11 @@ */ import { createPlugin } from '@backstage/core'; -import ExampleComponent from './components/ExampleComponent'; +import { GraphiQLPage } from './components'; export const plugin = createPlugin({ id: 'graphiql', register({ router }) { - router.registerRoute('/graphiql', ExampleComponent); + router.registerRoute('/graphiql', GraphiQLPage); }, }); diff --git a/yarn.lock b/yarn.lock index 641cefab27..bff17a5656 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4476,6 +4476,11 @@ resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb" integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA== +"@xobotyi/scrollbar-width@1.9.5": + version "1.9.5" + resolved "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -6466,6 +6471,19 @@ code-point-at@^1.0.0: resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= +codemirror-graphql@^0.11.6: + version "0.11.6" + resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-0.11.6.tgz#885e34afb5b7aacf0e328d4d5949e73ad21d5a4e" + integrity sha512-/zVKgOVS2/hfjAY0yoBkLz9ESHnWKBWpBNXQSoFF4Hl5q5AS2DmM22coonWKJcCvNry6TLak2F+QkzPeKVv3Eg== + dependencies: + graphql-language-service-interface "^2.3.3" + graphql-language-service-parser "^1.5.2" + +codemirror@^5.47.0: + version "5.53.2" + resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.53.2.tgz#9799121cf8c50809cca487304e9de3a74d33f428" + integrity sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA== + collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" @@ -7033,6 +7051,14 @@ cross-env@^7.0.0: dependencies: cross-spawn "^7.0.1" +cross-fetch@2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.2.tgz#a47ff4f7fc712daba8f6a695a11c948440d45723" + integrity sha1-pH/09/xxLauo9qaVoRyUhEDUVyM= + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + cross-fetch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c" @@ -8181,7 +8207,7 @@ entities@^1.1.1, entities@^1.1.2: resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== -entities@^2.0.0: +entities@^2.0.0, entities@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== @@ -9961,6 +9987,84 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graphiql@^0.17.5: + version "0.17.5" + resolved "https://registry.npmjs.org/graphiql/-/graphiql-0.17.5.tgz#76c553fc0d8936f77e33114ac3374f1807a718ff" + integrity sha512-ogNsrg9qM1py9PzcIUn+C29JukOADbjIfB6zwtfui4BrpOEpDb5UZ6TjAmSL/F/8tCt4TbgwKtkSrBeLNNUrqA== + dependencies: + codemirror "^5.47.0" + codemirror-graphql "^0.11.6" + copy-to-clipboard "^3.2.0" + entities "^2.0.0" + markdown-it "^10.0.0" + regenerator-runtime "^0.13.3" + +graphql-config@2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz#5fd0ec77ac7428ca5fb2026cf131be10151a0cb2" + integrity sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ== + dependencies: + graphql-import "^0.7.1" + graphql-request "^1.5.0" + js-yaml "^3.10.0" + lodash "^4.17.4" + minimatch "^3.0.4" + +graphql-import@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/graphql-import/-/graphql-import-0.7.1.tgz#4add8d91a5f752d764b0a4a7a461fcd93136f223" + integrity sha512-YpwpaPjRUVlw2SN3OPljpWbVRWAhMAyfSba5U47qGMOSsPLi2gYeJtngGpymjm9nk57RFWEpjqwh4+dpYuFAPw== + dependencies: + lodash "^4.17.4" + resolve-from "^4.0.0" + +graphql-language-service-interface@^2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.3.3.tgz#33d2263e797dcfcac2426e00a33349d2a489edfa" + integrity sha512-SMUbbiHbD19ffyDrucR+vwyaKYhDcTgbBFDJu9Z4TBa5XaksmyiurB3f+pWlIkuFvogBvW3JDiiJJlUW7awivg== + dependencies: + graphql-config "2.2.1" + graphql-language-service-parser "^1.5.2" + graphql-language-service-types "^1.5.2" + graphql-language-service-utils "^2.3.3" + +graphql-language-service-parser@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.5.2.tgz#37deb56c16155cbd324fedef42ef9a3f0b38d723" + integrity sha512-kModfvwX5XiT+tYRhh8d6X+rb5Zq9zFQVdcoVlQJvoIW7U6SkxUAeO5Ei9OI3KOMH5r8wyfmXflBZ+xUbJySJw== + dependencies: + graphql-config "2.2.1" + graphql-language-service-types "^1.5.2" + +graphql-language-service-types@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.5.2.tgz#bfd3b27a45dbc2457233c73cc1f8ff5da26795f8" + integrity sha512-WOFHBZX1K41svohPTmhOcKg+zz27d6ULFuZ8mzkiJ9nIpGKueAPyh7/xR0VZNBUAfDzTCbE6wQZxsPl5Kvd7IA== + dependencies: + graphql-config "2.2.1" + +graphql-language-service-utils@^2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.3.3.tgz#babfffecb754920f028525c4c094bb68638370a3" + integrity sha512-uHLdIbQpKkE1V2WA12DRMXrUZpPD3ZKPOuH3MHlNg+j9AEe1y83chA4yP5DQqR+ARdMpefz4FJHvEjQr9alXYw== + dependencies: + graphql-config "2.2.1" + graphql-language-service-types "^1.5.2" + +graphql-request@^1.5.0: + version "1.8.2" + resolved "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz#398d10ae15c585676741bde3fc01d5ca948f8fbe" + integrity sha512-dDX2M+VMsxXFCmUX0Vo0TopIZIX4ggzOtiCsThgtrKR4niiaagsGTDIHj3fsOMFETpa064vzovI+4YV4QnMbcg== + dependencies: + cross-fetch "2.2.2" + +graphql@^14.0.0: + version "14.6.0" + resolved "https://registry.npmjs.org/graphql/-/graphql-14.6.0.tgz#57822297111e874ea12f5cd4419616930cd83e49" + integrity sha512-VKzfvHEKybTKjQVpTFrA5yUq2S9ihcZvfJAtsDBBCuV6wauPu1xl/f9ehgVf0FcEJJs4vz6ysb/ZMkGigQZseg== + dependencies: + iterall "^1.2.2" + growly@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -11531,6 +11635,11 @@ istanbul-reports@^3.0.0: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterall@^1.2.2: + version "1.3.0" + resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== + iterate-iterator@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" @@ -12312,7 +12421,7 @@ js-tokens@^3.0.2: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.13.1: +js-yaml@^3.10.0, js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -12903,6 +13012,13 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +linkify-it@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" + integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== + dependencies: + uc.micro "^1.0.1" + lint-staged@^10.0.4: version "10.0.8" resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.8.tgz#0f7849cdc336061f25f5d4fcbcfa385701ff4739" @@ -13457,6 +13573,17 @@ markdown-escapes@^1.0.0: resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== +markdown-it@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" + integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg== + dependencies: + argparse "^1.0.7" + entities "~2.0.0" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3: version "6.11.0" resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0" @@ -13508,6 +13635,11 @@ mdn-data@2.0.6: resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + meant@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/meant/-/meant-1.0.1.tgz#66044fea2f23230ec806fb515efea29c44d2115d" @@ -14097,6 +14229,11 @@ node-fetch-npm@^2.0.2: json-parse-better-errors "^1.0.0" safe-buffer "^5.1.1" +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + node-fetch@2.6.0, node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" @@ -16960,6 +17097,25 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" +react-use@^13.0.0: + version "13.27.1" + resolved "https://registry.npmjs.org/react-use/-/react-use-13.27.1.tgz#e2ae2b708dafc7893c4772628801589aab9de370" + integrity sha512-bAwdqDMXs5lovEanXnL1izledfrPEUUv1afoTVB59eUiYcDyKul+M/dT/2WcgHjoY/R6QlrTcZoW4R7ifwvBfw== + dependencies: + "@types/js-cookie" "2.2.5" + "@xobotyi/scrollbar-width" "1.9.5" + copy-to-clipboard "^3.2.0" + fast-deep-equal "^3.1.1" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.2.1" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^2.1.0" + ts-easing "^0.2.0" + tslib "^1.10.0" + react-use@^13.24.0: version "13.27.0" resolved "https://registry.npmjs.org/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa" @@ -19715,6 +19871,11 @@ ua-parser-js@^0.7.18: resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + uglify-js@3.4.x: version "3.4.10" resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" @@ -20387,6 +20548,11 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" From c119eb9a9ac3cecbce6ef96c2f0084322f119afc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 15:45:23 +0200 Subject: [PATCH 04/21] plugins/graphiql: switch graphiql version to next, with TS support --- plugins/graphiql/package.json | 4 +- .../src/components/GraphiQLPage/graphiql.d.ts | 30 -- yarn.lock | 350 +++++++++++++----- 3 files changed, 251 insertions(+), 133 deletions(-) delete mode 100644 plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index df761be356..8f50c59e66 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -29,8 +29,8 @@ "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "graphiql": "^0.17.5", - "graphql": "^14.0.0", + "graphiql": "^1.0.0-alpha.8", + "graphql": "15.0.0", "react": "16.13.1", "react-dom": "16.13.1", "react-use": "^13.0.0" diff --git a/plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts b/plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts deleted file mode 100644 index b48b764415..0000000000 --- a/plugins/graphiql/src/components/GraphiQLPage/graphiql.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -declare module 'graphiql' { - import { FC } from 'react'; - - type Props = { - fetcher: (params: any) => Promise; - schema?: any; - storage?: Storage; - editorTheme?: string; - }; - - const GraphiQL: FC; - - export default GraphiQL; -} diff --git a/yarn.lock b/yarn.lock index bff17a5656..c43b294646 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1166,7 +1166,7 @@ "@emotion/utils" "0.11.3" "@emotion/weak-memoize" "0.2.5" -"@emotion/core@^10.0.20": +"@emotion/core@^10.0.0", "@emotion/core@^10.0.20", "@emotion/core@^10.0.28": version "10.0.28" resolved "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d" integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA== @@ -1192,14 +1192,14 @@ resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== -"@emotion/is-prop-valid@0.8.8": +"@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.1": version "0.8.8" resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== dependencies: "@emotion/memoize" "0.7.4" -"@emotion/memoize@0.7.4": +"@emotion/memoize@0.7.4", "@emotion/memoize@^0.7.1": version "0.7.4" resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== @@ -1230,7 +1230,7 @@ "@emotion/serialize" "^0.11.15" "@emotion/utils" "0.11.3" -"@emotion/styled@^10.0.17": +"@emotion/styled@^10.0.0", "@emotion/styled@^10.0.17": version "10.0.27" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf" integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q== @@ -2491,6 +2491,11 @@ prop-types "^15.7.2" react-is "^16.8.0" +"@mdx-js/react@^1.0.0", "@mdx-js/react@^1.5.2": + version "1.5.9" + resolved "https://registry.npmjs.org/@mdx-js/react/-/react-1.5.9.tgz#31873ab097fbe58c61c7585fc0be64e83182b6df" + integrity sha512-rengdUSedIdIQbXPSeafItCacTYocARAjUA51b6R1KNHmz+59efz7UmyTKr73viJQZ98ouu7iRGmOTtjRrbbWA== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -3401,6 +3406,105 @@ telejson "^3.2.0" util-deprecate "^1.0.2" +"@styled-system/background@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz#75c63d06b497ab372b70186c0bf608d62847a2ba" + integrity sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/border@^5.1.5": + version "5.1.5" + resolved "https://registry.npmjs.org/@styled-system/border/-/border-5.1.5.tgz#0493d4332d2b59b74bb0d57d08c73eb555761ba6" + integrity sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/color@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/color/-/color-5.1.2.tgz#b8d6b4af481faabe4abca1a60f8daa4ccc2d9f43" + integrity sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/core@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/core/-/core-5.1.2.tgz#b8b7b86455d5a0514f071c4fa8e434b987f6a772" + integrity sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw== + dependencies: + object-assign "^4.1.1" + +"@styled-system/css@^5.1.5": + version "5.1.5" + resolved "https://registry.npmjs.org/@styled-system/css/-/css-5.1.5.tgz#0460d5f3ff962fa649ea128ef58d9584f403bbbc" + integrity sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A== + +"@styled-system/flexbox@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/flexbox/-/flexbox-5.1.2.tgz#077090f43f61c3852df63da24e4108087a8beecf" + integrity sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/grid@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/grid/-/grid-5.1.2.tgz#7165049877732900b99cd00759679fbe45c6c573" + integrity sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/layout@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/layout/-/layout-5.1.2.tgz#12d73e79887e10062f4dbbbc2067462eace42339" + integrity sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/position@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/position/-/position-5.1.2.tgz#56961266566836f57a24d8e8e33ce0c1adb59dd3" + integrity sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/shadow@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/shadow/-/shadow-5.1.2.tgz#beddab28d7de03cd0177a87ac4ed3b3b6d9831fd" + integrity sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/should-forward-prop@^5.1.2": + version "5.1.5" + resolved "https://registry.npmjs.org/@styled-system/should-forward-prop/-/should-forward-prop-5.1.5.tgz#c392008c6ae14a6eb78bf1932733594f7f7e5c76" + integrity sha512-+rPRomgCGYnUIaFabDoOgpSDc4UUJ1KsmlnzcEp0tu5lFrBQKgZclSo18Z1URhaZm7a6agGtS5Xif7tuC2s52Q== + dependencies: + "@emotion/is-prop-valid" "^0.8.1" + "@emotion/memoize" "^0.7.1" + styled-system "^5.1.5" + +"@styled-system/space@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/space/-/space-5.1.2.tgz#38925d2fa29a41c0eb20e65b7c3efb6e8efce953" + integrity sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/typography@^5.1.2": + version "5.1.2" + resolved "https://registry.npmjs.org/@styled-system/typography/-/typography-5.1.2.tgz#65fb791c67d50cd2900d234583eaacdca8c134f7" + integrity sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg== + dependencies: + "@styled-system/core" "^5.1.2" + +"@styled-system/variant@^5.1.5": + version "5.1.5" + resolved "https://registry.npmjs.org/@styled-system/variant/-/variant-5.1.5.tgz#8446d8aad06af3a4c723d717841df2dbe4ddeafd" + integrity sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw== + dependencies: + "@styled-system/core" "^5.1.2" + "@styled-system/css" "^5.1.5" + "@svgr/babel-plugin-add-jsx-attribute@^4.2.0": version "4.2.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" @@ -3566,6 +3670,61 @@ resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz#2ad4e844175a3738cb9e7064be5ea070b8863a1c" integrity sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA== +"@theme-ui/color-modes@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@theme-ui/color-modes/-/color-modes-0.3.1.tgz#8c2b18fb170e6c998287b7381240a8b9cca8b0d1" + integrity sha512-WuZGgFW7M5wOWSse1PVZCEfM0OZip15/D6U3bB3B9KmWax7qiSnAm1yAMLRQKC+QYhndrjq3xU+WAQm11KnhIw== + dependencies: + "@emotion/core" "^10.0.0" + "@theme-ui/core" "^0.3.1" + "@theme-ui/css" "^0.3.1" + deepmerge "^4.2.2" + +"@theme-ui/components@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@theme-ui/components/-/components-0.3.1.tgz#fe023e156c1e1c076d5f2258466426e94adc2765" + integrity sha512-uG4dUM61s4tWv6N34uxs5VIh24bJyA/7TrYJ75WDiI+s72zbcNG7aGRpvX/hSZnAhxjdXpuskdEM3eEgOabdEg== + dependencies: + "@emotion/core" "^10.0.0" + "@emotion/styled" "^10.0.0" + "@styled-system/color" "^5.1.2" + "@styled-system/should-forward-prop" "^5.1.2" + "@styled-system/space" "^5.1.2" + "@theme-ui/css" "^0.3.1" + +"@theme-ui/core@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@theme-ui/core/-/core-0.3.1.tgz#dbe9800b9d6e923e1a7417e6adebce21524f8c02" + integrity sha512-cK6EVSOx0Kyx1Xpi4qb0JTLIxywx0DRh+53Ln1foXMplF2qKaDsFi3vD6duHIlT331E3CNOa9dftHHNM7y4rbA== + dependencies: + "@emotion/core" "^10.0.0" + "@theme-ui/css" "^0.3.1" + deepmerge "^4.2.2" + +"@theme-ui/css@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@theme-ui/css/-/css-0.3.1.tgz#b85c7e8fae948dc0de65aa30b853368993e25cb3" + integrity sha512-QB2/fZBpo4inaLHL3OrB8NOBgNfwnj8GtHzXWHb9iQSRjmtNX8zPXBe32jLT7qQP0+y8JxPT4YChZIkm5ZyIdg== + +"@theme-ui/mdx@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@theme-ui/mdx/-/mdx-0.3.0.tgz#8bb1342204acfaa69914d6b6567c5c49d9a8c1e6" + integrity sha512-/GHBNKqmUptWwkmF+zIASVQtjYs81XMEwtqPCHnHuaaCzhZxcXrtCwvcAgmCXF8hpRttCXVVxw1X3Gt0mhzaTQ== + dependencies: + "@emotion/core" "^10.0.0" + "@emotion/styled" "^10.0.0" + "@mdx-js/react" "^1.0.0" + +"@theme-ui/theme-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@theme-ui/theme-provider/-/theme-provider-0.3.1.tgz#910bc43454fd61b1047d7bb0dce05e36ffb6b44b" + integrity sha512-Sjj6lD0gPxBi+hcGCkawcGZECeESV/mW2YfmPqjNgmc296x5tulfNc+0/N5CJwLVOmnkn8zR5KNWZ8BjndfeTg== + dependencies: + "@emotion/core" "^10.0.0" + "@theme-ui/color-modes" "^0.3.1" + "@theme-ui/core" "^0.3.1" + "@theme-ui/mdx" "^0.3.0" + "@tootallnate/once@1": version "1.0.0" resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" @@ -6471,15 +6630,15 @@ code-point-at@^1.0.0: resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemirror-graphql@^0.11.6: - version "0.11.6" - resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-0.11.6.tgz#885e34afb5b7aacf0e328d4d5949e73ad21d5a4e" - integrity sha512-/zVKgOVS2/hfjAY0yoBkLz9ESHnWKBWpBNXQSoFF4Hl5q5AS2DmM22coonWKJcCvNry6TLak2F+QkzPeKVv3Eg== +codemirror-graphql@^0.12.0-alpha.7: + version "0.12.0-alpha.7" + resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-0.12.0-alpha.7.tgz#fb8a8e633f5e416a9736df2fa697312a0955db20" + integrity sha512-LOsHnAc/pWOqqUEfQX5tNn10jStqIe8il6/vHq7m81uyGcp7tx5Np3O7zwGKQ/HdewDUMjeqXnva5tFGYaKzVg== dependencies: - graphql-language-service-interface "^2.3.3" - graphql-language-service-parser "^1.5.2" + graphql-language-service-interface "^2.4.0-alpha.7" + graphql-language-service-parser "^1.6.0-alpha.3" -codemirror@^5.47.0: +codemirror@^5.52.2: version "5.53.2" resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.53.2.tgz#9799121cf8c50809cca487304e9de3a74d33f428" integrity sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA== @@ -7051,14 +7210,6 @@ cross-env@^7.0.0: dependencies: cross-spawn "^7.0.1" -cross-fetch@2.2.2: - version "2.2.2" - resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.2.tgz#a47ff4f7fc712daba8f6a695a11c948440d45723" - integrity sha1-pH/09/xxLauo9qaVoRyUhEDUVyM= - dependencies: - node-fetch "2.1.2" - whatwg-fetch "2.0.4" - cross-fetch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c" @@ -7637,6 +7788,11 @@ deep-object-diff@^1.1.0: resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a" integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw== +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -9987,83 +10143,54 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3 resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== -graphiql@^0.17.5: - version "0.17.5" - resolved "https://registry.npmjs.org/graphiql/-/graphiql-0.17.5.tgz#76c553fc0d8936f77e33114ac3374f1807a718ff" - integrity sha512-ogNsrg9qM1py9PzcIUn+C29JukOADbjIfB6zwtfui4BrpOEpDb5UZ6TjAmSL/F/8tCt4TbgwKtkSrBeLNNUrqA== +graphiql@^1.0.0-alpha.8: + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.0.0-alpha.8.tgz#a9cc594a8666e372c919a5c86a32835bc37499ff" + integrity sha512-6cR4NUtR79P2ShXt2P5d4OSIlsqiHzb7fK27LSm4JHIx0uBLYWrlJwHcjjF+Ucujr3fbVn4miyNF+ypvMJp53g== dependencies: - codemirror "^5.47.0" - codemirror-graphql "^0.11.6" + "@emotion/core" "^10.0.28" + "@mdx-js/react" "^1.5.2" + codemirror "^5.52.2" + codemirror-graphql "^0.12.0-alpha.7" copy-to-clipboard "^3.2.0" entities "^2.0.0" markdown-it "^10.0.0" - regenerator-runtime "^0.13.3" + regenerator-runtime "^0.13.5" + theme-ui "^0.3.1" -graphql-config@2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz#5fd0ec77ac7428ca5fb2026cf131be10151a0cb2" - integrity sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ== +graphql-language-service-interface@^2.4.0-alpha.7: + version "2.4.0-alpha.7" + resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.4.0-alpha.7.tgz#5458f1f17a79dde5b51dab0d21eda63dff48a918" + integrity sha512-X5jwTI4AlcKB/t9/hZBn9O69uG9WPbkFnZivhNfztzeudKV+cXHYR6behsR47bR2nzF3YEu7Nv+3HExXKQAlWg== dependencies: - graphql-import "^0.7.1" - graphql-request "^1.5.0" - js-yaml "^3.10.0" - lodash "^4.17.4" - minimatch "^3.0.4" + graphql-language-service-parser "^1.6.0-alpha.3" + graphql-language-service-types "^1.6.0-alpha.5" + graphql-language-service-utils "^2.4.0-alpha.6" + vscode-languageserver-types "^3.15.1" -graphql-import@^0.7.1: - version "0.7.1" - resolved "https://registry.npmjs.org/graphql-import/-/graphql-import-0.7.1.tgz#4add8d91a5f752d764b0a4a7a461fcd93136f223" - integrity sha512-YpwpaPjRUVlw2SN3OPljpWbVRWAhMAyfSba5U47qGMOSsPLi2gYeJtngGpymjm9nk57RFWEpjqwh4+dpYuFAPw== +graphql-language-service-parser@^1.6.0-alpha.3: + version "1.6.0-alpha.3" + resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.6.0-alpha.3.tgz#671643018fb4b24f409997a05b021030948562fb" + integrity sha512-gx2u6b2m9X9DjvlfEjmBQ67GtZxPPWAtVb63BQ21u2MPxqYjKsBJkKLq9PiC2u57bJhv+h4MoL4OLJ/vkmq6Ig== dependencies: - lodash "^4.17.4" - resolve-from "^4.0.0" + graphql-language-service-types "^1.6.0-alpha.5" -graphql-language-service-interface@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.3.3.tgz#33d2263e797dcfcac2426e00a33349d2a489edfa" - integrity sha512-SMUbbiHbD19ffyDrucR+vwyaKYhDcTgbBFDJu9Z4TBa5XaksmyiurB3f+pWlIkuFvogBvW3JDiiJJlUW7awivg== - dependencies: - graphql-config "2.2.1" - graphql-language-service-parser "^1.5.2" - graphql-language-service-types "^1.5.2" - graphql-language-service-utils "^2.3.3" +graphql-language-service-types@^1.6.0-alpha.5: + version "1.6.0-alpha.5" + resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.6.0-alpha.5.tgz#113a92c7544d98c8a8b8c3e04c9f982d3dbe1a00" + integrity sha512-Q/pTif29xM6OiFV9RsYLwX2uDeCMdSG36n4G0aDvfbWxbOBj48vSlLL5R8Gpt/DjocG9Kkv51CZay6pMPtmAfg== -graphql-language-service-parser@^1.5.2: - version "1.5.2" - resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.5.2.tgz#37deb56c16155cbd324fedef42ef9a3f0b38d723" - integrity sha512-kModfvwX5XiT+tYRhh8d6X+rb5Zq9zFQVdcoVlQJvoIW7U6SkxUAeO5Ei9OI3KOMH5r8wyfmXflBZ+xUbJySJw== +graphql-language-service-utils@^2.4.0-alpha.6: + version "2.4.0-alpha.6" + resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.4.0-alpha.6.tgz#5c3b30150e4905517b84b71f84956851c2c883e1" + integrity sha512-/+crYcPhOoeu1qpHhH2FZpI7w4i3q4Kq7jzbodXxOZBAqJFd3FSHTnVBYEAfgTgkTbIC7yIXVwnIwXtqDFTNDQ== dependencies: - graphql-config "2.2.1" - graphql-language-service-types "^1.5.2" + graphql-language-service-types "^1.6.0-alpha.5" -graphql-language-service-types@^1.5.2: - version "1.5.2" - resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.5.2.tgz#bfd3b27a45dbc2457233c73cc1f8ff5da26795f8" - integrity sha512-WOFHBZX1K41svohPTmhOcKg+zz27d6ULFuZ8mzkiJ9nIpGKueAPyh7/xR0VZNBUAfDzTCbE6wQZxsPl5Kvd7IA== - dependencies: - graphql-config "2.2.1" - -graphql-language-service-utils@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.3.3.tgz#babfffecb754920f028525c4c094bb68638370a3" - integrity sha512-uHLdIbQpKkE1V2WA12DRMXrUZpPD3ZKPOuH3MHlNg+j9AEe1y83chA4yP5DQqR+ARdMpefz4FJHvEjQr9alXYw== - dependencies: - graphql-config "2.2.1" - graphql-language-service-types "^1.5.2" - -graphql-request@^1.5.0: - version "1.8.2" - resolved "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz#398d10ae15c585676741bde3fc01d5ca948f8fbe" - integrity sha512-dDX2M+VMsxXFCmUX0Vo0TopIZIX4ggzOtiCsThgtrKR4niiaagsGTDIHj3fsOMFETpa064vzovI+4YV4QnMbcg== - dependencies: - cross-fetch "2.2.2" - -graphql@^14.0.0: - version "14.6.0" - resolved "https://registry.npmjs.org/graphql/-/graphql-14.6.0.tgz#57822297111e874ea12f5cd4419616930cd83e49" - integrity sha512-VKzfvHEKybTKjQVpTFrA5yUq2S9ihcZvfJAtsDBBCuV6wauPu1xl/f9ehgVf0FcEJJs4vz6ysb/ZMkGigQZseg== - dependencies: - iterall "^1.2.2" +graphql@15.0.0: + version "15.0.0" + resolved "https://registry.npmjs.org/graphql/-/graphql-15.0.0.tgz#042a5eb5e2506a2e2111ce41eb446a8e570b8be9" + integrity sha512-ZyVO1xIF9F+4cxfkdhOJINM+51B06Friuv4M66W7HzUOeFd+vNzUn4vtswYINPi6sysjf1M2Ri/rwZALqgwbaQ== growly@^1.3.0: version "1.3.0" @@ -11635,11 +11762,6 @@ istanbul-reports@^3.0.0: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterall@^1.2.2: - version "1.3.0" - resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" - integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== - iterate-iterator@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" @@ -12421,7 +12543,7 @@ js-tokens@^3.0.2: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.10.0, js-yaml@^3.13.1: +js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -14229,11 +14351,6 @@ node-fetch-npm@^2.0.2: json-parse-better-errors "^1.0.0" safe-buffer "^5.1.1" -node-fetch@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" - integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= - node-fetch@2.6.0, node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0: version "2.6.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" @@ -17439,7 +17556,7 @@ regenerator-runtime@^0.11.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.5: version "0.13.5" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== @@ -19172,6 +19289,25 @@ style-loader@^1.0.0: loader-utils "^1.2.3" schema-utils "^2.6.4" +styled-system@^5.1.5: + version "5.1.5" + resolved "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz#e362d73e1dbb5641a2fd749a6eba1263dc85075e" + integrity sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A== + dependencies: + "@styled-system/background" "^5.1.2" + "@styled-system/border" "^5.1.5" + "@styled-system/color" "^5.1.2" + "@styled-system/core" "^5.1.2" + "@styled-system/flexbox" "^5.1.2" + "@styled-system/grid" "^5.1.2" + "@styled-system/layout" "^5.1.2" + "@styled-system/position" "^5.1.2" + "@styled-system/shadow" "^5.1.2" + "@styled-system/space" "^5.1.2" + "@styled-system/typography" "^5.1.2" + "@styled-system/variant" "^5.1.5" + object-assign "^4.1.1" + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -19443,6 +19579,18 @@ text-table@0.2.0, text-table@^0.2.0, text-table@~0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +theme-ui@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/theme-ui/-/theme-ui-0.3.1.tgz#b00ee2c03eb3d820536af8b121c64d13b3777cf0" + integrity sha512-My/TSALqp7Dst5Ez7nJA+94Q8zJhc26Z0qGo8kEWyoqHHJ5TU8xdhjLPBltTdQck3T32cSq5USIeSKU3JtxYUQ== + dependencies: + "@theme-ui/color-modes" "^0.3.1" + "@theme-ui/components" "^0.3.1" + "@theme-ui/core" "^0.3.1" + "@theme-ui/css" "^0.3.1" + "@theme-ui/mdx" "^0.3.0" + "@theme-ui/theme-provider" "^0.3.1" + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -20330,6 +20478,11 @@ vm-browserify@^1.0.1: resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +vscode-languageserver-types@^3.15.1: + version "3.15.1" + resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" + integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== + w3c-hr-time@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -20548,11 +20701,6 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-fetch@2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" - integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== - whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" From dceff8dd2f575671c670a2ff949928c1b78e6f41 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 15:45:55 +0200 Subject: [PATCH 05/21] plugins/graphiql: add global style override to fix top bar layout issue --- .../src/components/GraphiQLPage/GraphiQLPage.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx index 37fae51290..9bda2db944 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx @@ -15,7 +15,7 @@ */ import React, { FC, useState } from 'react'; -import { Tabs, Tab } from '@material-ui/core'; +import { Tabs, Tab, makeStyles } from '@material-ui/core'; import { Page, pageTheme, Content, Header, HeaderLabel } from '@backstage/core'; import 'graphiql/graphiql.css'; import GraphiQL from 'graphiql'; @@ -45,7 +45,18 @@ const tabs = [ }, ]; +const useStyles = makeStyles({ + root: { + '@global': { + '.graphiql-container': { + boxSizing: 'initial', + }, + }, + }, +}); + export const GraphiQLPage: FC<{}> = () => { + const classes = useStyles(); const [tabIndex, setTabIndex] = useState(0); return ( @@ -54,7 +65,7 @@ export const GraphiQLPage: FC<{}> = () => { - + setTabIndex(value)}> {tabs.map(({ title }, index) => ( From 9d080a4dce9dae615b6d0172ce2eb51af7b83d7b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 17:47:35 +0200 Subject: [PATCH 06/21] plugins/graphiql: add storage buckets to separate storage for different endpoints --- .../components/GraphiQLPage/GraphiQLPage.tsx | 8 +- .../src/lib/storage/StorageBucket.test.ts | 30 ++++++ .../graphiql/src/lib/storage/StorageBucket.ts | 102 ++++++++++++++++++ plugins/graphiql/src/lib/storage/index.ts | 17 +++ 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 plugins/graphiql/src/lib/storage/StorageBucket.test.ts create mode 100644 plugins/graphiql/src/lib/storage/StorageBucket.ts create mode 100644 plugins/graphiql/src/lib/storage/index.ts diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx index 9bda2db944..76b17f545d 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx @@ -19,9 +19,11 @@ import { Tabs, Tab, makeStyles } from '@material-ui/core'; import { Page, pageTheme, Content, Header, HeaderLabel } from '@backstage/core'; import 'graphiql/graphiql.css'; import GraphiQL from 'graphiql'; +import { StorageBucket } from 'lib/storage'; const tabs = [ { + id: 'gitlab', title: 'GitLab', fetcher: async (params: any) => { const res = await fetch('https://gitlab.com/api/graphql', { @@ -33,6 +35,7 @@ const tabs = [ }, }, { + id: 'countries', title: 'Countries', fetcher: async (params: any) => { const res = await fetch('https://countries.trevorblades.com/', { @@ -59,6 +62,9 @@ export const GraphiQLPage: FC<{}> = () => { const classes = useStyles(); const [tabIndex, setTabIndex] = useState(0); + const { id, fetcher } = tabs[tabIndex]; + const storage = StorageBucket.forLocalStorage(`plugin/graphiql/data/${id}`); + return (
@@ -71,7 +77,7 @@ export const GraphiQLPage: FC<{}> = () => { ))} - + ); diff --git a/plugins/graphiql/src/lib/storage/StorageBucket.test.ts b/plugins/graphiql/src/lib/storage/StorageBucket.test.ts new file mode 100644 index 0000000000..2b6046aefe --- /dev/null +++ b/plugins/graphiql/src/lib/storage/StorageBucket.test.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { StorageBucket } from './StorageBucket'; + +describe('StorageBucket', () => { + it('should forbid access to unknown keys', () => { + const bucket = StorageBucket.forStorage(localStorage, 'hello'); + + expect(() => { + bucket['dunno-this-one'] = 'nope'; + }).toThrow('Direct property access is not allowed for StorageBuckets'); + expect(() => { + return bucket['dunno-this-one']; + }).toThrow('Direct property access is not allowed for StorageBuckets'); + }); +}); diff --git a/plugins/graphiql/src/lib/storage/StorageBucket.ts b/plugins/graphiql/src/lib/storage/StorageBucket.ts new file mode 100644 index 0000000000..eb8feb394d --- /dev/null +++ b/plugins/graphiql/src/lib/storage/StorageBucket.ts @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +type BucketData = { [key in string]: string }; + +export class StorageBucket implements Storage { + private static noPropAccessProxyHandler: ProxyHandler = { + get(target, prop) { + if (prop in target) { + return target[prop as any]; + } + throw new Error( + 'Direct property access is not allowed for StorageBuckets', + ); + }, + set() { + throw new Error( + 'Direct property access is not allowed for StorageBuckets', + ); + }, + }; + + static forStorage(storage: Storage, bucket: string) { + const storageBucket = new StorageBucket(storage, bucket); + return new Proxy(storageBucket, StorageBucket.noPropAccessProxyHandler); + } + + static forLocalStorage(bucket: string): StorageBucket { + return StorageBucket.forStorage(localStorage, bucket); + } + + private constructor( + private readonly storage: Storage, + private readonly bucket: string, + ) {} + + [name: string]: any; + + get length(): number { + throw new Error('Method not implemented.'); + } + + clear(): void { + this.write({}); + } + + getItem(key: string): string | null { + return this.read()?.[key] ?? null; + } + + key(): never { + throw new Error('Method not implemented.'); + } + + removeItem(key: string): void { + const data = this.read(); + if (!data) { + return; + } + + const deleted = delete data[key]; + if (deleted) { + this.write(data); + } + } + + setItem(key: string, value: string): void { + const data = this.read() ?? {}; + data[key] = value; + this.write(data); + } + + private read(): BucketData | undefined { + const bucketValue = this.storage.getItem(this.bucket); + if (!bucketValue) { + return undefined; + } + + try { + return JSON.parse(bucketValue); + } catch { + return undefined; + } + } + + private write(data: BucketData) { + this.storage.setItem(this.bucket, JSON.stringify(data)); + } +} diff --git a/plugins/graphiql/src/lib/storage/index.ts b/plugins/graphiql/src/lib/storage/index.ts new file mode 100644 index 0000000000..a760761e44 --- /dev/null +++ b/plugins/graphiql/src/lib/storage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './StorageBucket'; From 7ec8e0d0c2c1cbc3a7713a8a1badfc507bcfa167 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 17:48:34 +0200 Subject: [PATCH 07/21] plugins/graphiql: add tests for StorageBucket + minor fix --- .../src/lib/storage/StorageBucket.test.ts | 62 +++++++++++++++++++ .../graphiql/src/lib/storage/StorageBucket.ts | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/plugins/graphiql/src/lib/storage/StorageBucket.test.ts b/plugins/graphiql/src/lib/storage/StorageBucket.test.ts index 2b6046aefe..21fe10165c 100644 --- a/plugins/graphiql/src/lib/storage/StorageBucket.test.ts +++ b/plugins/graphiql/src/lib/storage/StorageBucket.test.ts @@ -27,4 +27,66 @@ describe('StorageBucket', () => { return bucket['dunno-this-one']; }).toThrow('Direct property access is not allowed for StorageBuckets'); }); + + describe('with mocked underlying storage', () => { + const mockStorage = { + getItem: jest.fn(), + setItem: jest.fn(), + removeItem: jest.fn(), + }; + const bucket = StorageBucket.forStorage( + (mockStorage as unknown) as Storage, + 'my-bucket', + ); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should set a first item', () => { + bucket.setItem('x', 'a'); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(1); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(1); + expect(mockStorage.setItem).toHaveBeenLastCalledWith( + 'my-bucket', + JSON.stringify({ x: 'a' }), + ); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + }); + + it('should set a second item', () => { + mockStorage.getItem.mockReturnValueOnce(JSON.stringify({ y: 'b' })); + bucket.setItem('x', 'a'); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(1); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(1); + expect(mockStorage.setItem).toHaveBeenLastCalledWith( + 'my-bucket', + JSON.stringify({ y: 'b', x: 'a' }), + ); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + }); + + it('should clear the bucket', () => { + bucket.clear(); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(0); + expect(mockStorage.setItem).toHaveBeenCalledTimes(0); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(1); + expect(mockStorage.removeItem).toHaveBeenLastCalledWith('my-bucket'); + }); + + it('should get an item', () => { + mockStorage.getItem.mockReturnValueOnce(JSON.stringify({ x: 'X' })); + expect(bucket.getItem('x')).toBe('X'); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(1); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(0); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + }); + }); }); diff --git a/plugins/graphiql/src/lib/storage/StorageBucket.ts b/plugins/graphiql/src/lib/storage/StorageBucket.ts index eb8feb394d..06263583c3 100644 --- a/plugins/graphiql/src/lib/storage/StorageBucket.ts +++ b/plugins/graphiql/src/lib/storage/StorageBucket.ts @@ -54,7 +54,7 @@ export class StorageBucket implements Storage { } clear(): void { - this.write({}); + this.storage.removeItem(this.bucket); } getItem(key: string): string | null { From c8ef790e1bb7a8ea5517f61cf1c3deef40b3a668 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 23 Apr 2020 17:54:58 +0200 Subject: [PATCH 08/21] plugins/graphiql: fix page test --- .../src/components/GraphiQLPage/GraphiQLPage.test.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx index 4a61c9bf09..4dd18c326f 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx @@ -17,18 +17,21 @@ import React from 'react'; import { render } from '@testing-library/react'; import mockFetch from 'jest-fetch-mock'; -import ExampleComponent from './ExampleComponent'; +import { GraphiQLPage } from './GraphiQLPage'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; -describe('ExampleComponent', () => { +jest.mock('graphiql', () => () => ''); + +describe('GraphiQLPage', () => { it('should render', () => { mockFetch.mockResponse(() => new Promise(() => {})); const rendered = render( - + , ); - expect(rendered.getByText('Welcome to graphiql!')).toBeInTheDocument(); + expect(rendered.getByText('GraphiQL')).toBeInTheDocument(); + expect(rendered.getByText('')).toBeInTheDocument(); }); }); From 3734aeab628fd187f12d0d397d8e3e9fca24c0bd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 24 Apr 2020 16:28:48 +0200 Subject: [PATCH 09/21] plugins/graphiql: more tests for StorageBucket and fix removeItem --- .../src/lib/storage/StorageBucket.test.ts | 51 +++++++++++++++++++ .../graphiql/src/lib/storage/StorageBucket.ts | 4 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/plugins/graphiql/src/lib/storage/StorageBucket.test.ts b/plugins/graphiql/src/lib/storage/StorageBucket.test.ts index 21fe10165c..d18d038cb5 100644 --- a/plugins/graphiql/src/lib/storage/StorageBucket.test.ts +++ b/plugins/graphiql/src/lib/storage/StorageBucket.test.ts @@ -28,6 +28,13 @@ describe('StorageBucket', () => { }).toThrow('Direct property access is not allowed for StorageBuckets'); }); + it('should not implement all methods', () => { + const bucket = StorageBucket.forLocalStorage('hello'); + + expect(() => bucket.length).toThrow('Method not implemented.'); + expect(() => bucket.key()).toThrow('Method not implemented.'); + }); + describe('with mocked underlying storage', () => { const mockStorage = { getItem: jest.fn(), @@ -88,5 +95,49 @@ describe('StorageBucket', () => { expect(mockStorage.setItem).toHaveBeenCalledTimes(0); expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); }); + + it('should remove an item', () => { + mockStorage.getItem.mockReturnValueOnce( + JSON.stringify({ x: 'X', y: 'Y' }), + ); + bucket.removeItem('x'); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(1); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(1); + expect(mockStorage.setItem).toHaveBeenLastCalledWith( + 'my-bucket', + JSON.stringify({ y: 'Y' }), + ); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + }); + + it('should not bother to write when deleting a missing key', () => { + mockStorage.getItem.mockReturnValueOnce(JSON.stringify({ y: 'Y' })); + bucket.removeItem('x'); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(1); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(0); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + }); + + it('should ignore bad data', () => { + mockStorage.getItem.mockReturnValue('derp'); + + expect(bucket.getItem('x')).toBe(null); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(1); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(0); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + + bucket.removeItem('x'); + + expect(mockStorage.getItem).toHaveBeenCalledTimes(2); + expect(mockStorage.getItem).toHaveBeenLastCalledWith('my-bucket'); + expect(mockStorage.setItem).toHaveBeenCalledTimes(0); + expect(mockStorage.removeItem).toHaveBeenCalledTimes(0); + }); }); }); diff --git a/plugins/graphiql/src/lib/storage/StorageBucket.ts b/plugins/graphiql/src/lib/storage/StorageBucket.ts index 06263583c3..9d275e5f0d 100644 --- a/plugins/graphiql/src/lib/storage/StorageBucket.ts +++ b/plugins/graphiql/src/lib/storage/StorageBucket.ts @@ -71,8 +71,8 @@ export class StorageBucket implements Storage { return; } - const deleted = delete data[key]; - if (deleted) { + if (key in data) { + delete data[key]; this.write(data); } } From 95341965acc56ce29b745c7dd964acd5754fba48 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 12:53:50 +0200 Subject: [PATCH 10/21] plugins/graphiql: added graphql browsing api --- .../graphiql/src/lib/api/GraphQLBrowser.ts | 62 +++++++++++++++++++ plugins/graphiql/src/lib/api/index.ts | 18 ++++++ plugins/graphiql/src/lib/api/types.ts | 39 ++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 plugins/graphiql/src/lib/api/GraphQLBrowser.ts create mode 100644 plugins/graphiql/src/lib/api/index.ts create mode 100644 plugins/graphiql/src/lib/api/types.ts diff --git a/plugins/graphiql/src/lib/api/GraphQLBrowser.ts b/plugins/graphiql/src/lib/api/GraphQLBrowser.ts new file mode 100644 index 0000000000..645658237d --- /dev/null +++ b/plugins/graphiql/src/lib/api/GraphQLBrowser.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLBrowseApi, GraphQLEndpoint } from './types'; + +// Helper for generic http endpoints +export type EndpointConfig = { + id: string; + title: string; + // Endpoint URL + url: string; + // only supports POST right now + method?: 'POST'; + // Defaults to setting Content-Type to application/json + headers?: { [name in string]: string }; +}; + +export class GraphQLBrowser implements GraphQLBrowseApi { + static createEndpoint(config: EndpointConfig): GraphQLEndpoint { + const { id, title, url, method = 'POST' } = config; + return { + id, + title, + fetcher: async (params: any) => { + const body = JSON.stringify(params); + const headers = { + 'Content-Type': 'application/json', + ...config.headers, + }; + const res = await fetch(url, { + method, + headers, + body, + }); + return res.json(); + }, + }; + } + + static fromEndpoints(endpoints: GraphQLEndpoint[]) { + return new GraphQLBrowser(endpoints); + } + + private constructor(private readonly endpoints: GraphQLEndpoint[]) {} + + async getEndpoints() { + return this.endpoints; + } +} diff --git a/plugins/graphiql/src/lib/api/index.ts b/plugins/graphiql/src/lib/api/index.ts new file mode 100644 index 0000000000..e5ae2131ea --- /dev/null +++ b/plugins/graphiql/src/lib/api/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './GraphQLBrowser'; +export * from './types'; diff --git a/plugins/graphiql/src/lib/api/types.ts b/plugins/graphiql/src/lib/api/types.ts new file mode 100644 index 0000000000..6c41e2aaa0 --- /dev/null +++ b/plugins/graphiql/src/lib/api/types.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ApiRef } from '@backstage/core'; + +export type GraphQLEndpoint = { + // Will be used as unique key for storing history and query data + id: string; + + // Displayed to the user to identify the source. + title: string; + + // Method used send a GraphQL query. + // 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; +}; + +export type GraphQLBrowseApi = { + getEndpoints(): Promise; +}; + +export const graphQlBrowseApiRef = new ApiRef({ + id: 'plugin.graphiql.browse', + description: 'Used to supply GraphQL endpoints for browsing', +}); From 2a253d2556afccc8b4ba1167faf1f971ee4490f5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 12:55:05 +0200 Subject: [PATCH 11/21] plugins/graphiql: update tsconfig for plugin serve --- plugins/graphiql/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/graphiql/tsconfig.json b/plugins/graphiql/tsconfig.json index 7b73db2f0f..55fa1e2384 100644 --- a/plugins/graphiql/tsconfig.json +++ b/plugins/graphiql/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../tsconfig.json", - "include": ["src"], + "include": ["src", "dev"], "compilerOptions": { "baseUrl": "src" } From 0f63f63fcc7e5642d8177fd064f4b758849b503b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 12:55:39 +0200 Subject: [PATCH 12/21] plugins/graphiql: added supply graphql browsing api in dev wrapper --- plugins/graphiql/dev/index.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx index 2125baa6eb..bf0304df23 100644 --- a/plugins/graphiql/dev/index.tsx +++ b/plugins/graphiql/dev/index.tsx @@ -25,11 +25,26 @@ import { Sidebar, SidebarItem, SidebarSpacer, + ApiRegistry, } from '@backstage/core'; import { lightTheme } from '@backstage/theme'; -import { plugin } from '../src/plugin'; +import { plugin, GraphQLBrowser, graphQlBrowseApiRef } from '../src'; + +const graphQlBrowseApi = GraphQLBrowser.fromEndpoints([ + GraphQLBrowser.createEndpoint({ + id: 'gitlab', + title: 'GitLab', + url: 'https://gitlab.com/api/graphql', + }), + GraphQLBrowser.createEndpoint({ + id: 'countries', + title: 'Countries', + url: 'https://countries.trevorblades.com/', + }), +]); const app = createApp(); +app.registerApis(ApiRegistry.from([[graphQlBrowseApiRef, graphQlBrowseApi]])); app.registerPlugin(plugin); const AppComponent = app.build(); From c73872d01203fb62bb9de60ab4297a2bb026c76d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 12:56:06 +0200 Subject: [PATCH 13/21] plugins/graphiql: separate GraphiQLBrowser component and load endpoints from api --- .../GraphiQLBrowser/GraphiQLBrowser.test.tsx | 37 +++++++ .../GraphiQLBrowser/GraphiQLBrowser.tsx | 71 ++++++++++++++ .../src/components/GraphiQLBrowser/index.ts | 17 ++++ .../components/GraphiQLPage/GraphiQLPage.tsx | 96 ++++++++----------- plugins/graphiql/src/components/index.ts | 1 + plugins/graphiql/src/index.ts | 1 + 6 files changed, 169 insertions(+), 54 deletions(-) create mode 100644 plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx create mode 100644 plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx create mode 100644 plugins/graphiql/src/components/GraphiQLBrowser/index.ts diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx new file mode 100644 index 0000000000..4dd18c326f --- /dev/null +++ b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import mockFetch from 'jest-fetch-mock'; +import { GraphiQLPage } from './GraphiQLPage'; +import { ThemeProvider } from '@material-ui/core'; +import { lightTheme } from '@backstage/theme'; + +jest.mock('graphiql', () => () => ''); + +describe('GraphiQLPage', () => { + it('should render', () => { + mockFetch.mockResponse(() => new Promise(() => {})); + const rendered = render( + + + , + ); + expect(rendered.getByText('GraphiQL')).toBeInTheDocument(); + expect(rendered.getByText('')).toBeInTheDocument(); + }); +}); diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx new file mode 100644 index 0000000000..69ceec3136 --- /dev/null +++ b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx @@ -0,0 +1,71 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC, useState } from 'react'; +import { Tabs, Tab, makeStyles, Typography } from '@material-ui/core'; +import 'graphiql/graphiql.css'; +import GraphiQL from 'graphiql'; +import { StorageBucket } from 'lib/storage'; +import { GraphQLEndpoint } from 'lib/api'; + +const useStyles = makeStyles({ + root: { + height: '100%', + display: 'flex', + flexFlow: 'column nowrap', + }, + graphiQlWrapper: { + flex: 1, + '@global': { + '.graphiql-container': { + boxSizing: 'initial', + }, + }, + }, +}); + +type GraphiQLBrowserProps = { + endpoints: GraphQLEndpoint[]; +}; + +export const GraphiQLBrowser: FC = ({ endpoints }) => { + const classes = useStyles(); + const [tabIndex, setTabIndex] = useState(0); + + if (!endpoints.length) { + return No endpoints available; + } + + const { id, fetcher } = endpoints[tabIndex]; + const storage = StorageBucket.forLocalStorage(`plugin/graphiql/data/${id}`); + + return ( +
+ setTabIndex(value)} + indicatorColor="primary" + > + {endpoints.map(({ title }, index) => ( + + ))} + +
+ +
+
+ ); +}; diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/index.ts b/plugins/graphiql/src/components/GraphiQLBrowser/index.ts new file mode 100644 index 0000000000..cb9a4a932f --- /dev/null +++ b/plugins/graphiql/src/components/GraphiQLBrowser/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './GraphiQLBrowser'; diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx index 76b17f545d..90a0bb92c0 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx @@ -14,56 +14,51 @@ * limitations under the License. */ -import React, { FC, useState } from 'react'; -import { Tabs, Tab, makeStyles } from '@material-ui/core'; -import { Page, pageTheme, Content, Header, HeaderLabel } from '@backstage/core'; +import React, { FC } from 'react'; +import { + Content, + Header, + HeaderLabel, + Page, + Progress, + pageTheme, + useApi, +} from '@backstage/core'; +import { useAsync } from 'react-use'; import 'graphiql/graphiql.css'; -import GraphiQL from 'graphiql'; -import { StorageBucket } from 'lib/storage'; - -const tabs = [ - { - id: 'gitlab', - title: 'GitLab', - fetcher: async (params: any) => { - const res = await fetch('https://gitlab.com/api/graphql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(params), - }); - return res.json(); - }, - }, - { - id: 'countries', - title: 'Countries', - fetcher: async (params: any) => { - const res = await fetch('https://countries.trevorblades.com/', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(params), - }); - return res.json(); - }, - }, -]; - -const useStyles = makeStyles({ - root: { - '@global': { - '.graphiql-container': { - boxSizing: 'initial', - }, - }, - }, -}); +import { graphQlBrowseApiRef } from 'lib/api'; +import { GraphiQLBrowser } from 'components'; +import { Typography } from '@material-ui/core'; export const GraphiQLPage: FC<{}> = () => { - const classes = useStyles(); - const [tabIndex, setTabIndex] = useState(0); + const graphQlBrowseApi = useApi(graphQlBrowseApiRef); + const endpoints = useAsync(() => graphQlBrowseApi.getEndpoints()); - const { id, fetcher } = tabs[tabIndex]; - const storage = StorageBucket.forLocalStorage(`plugin/graphiql/data/${id}`); + let content: JSX.Element; + + if (endpoints.loading) { + content = ( + + + + ); + } else if (endpoints.error) { + content = ( + + + Failed to load GraphQL endpoints, {endpoints.error} +
+ We also need a proper error component +
+
+ ); + } else { + content = ( + + + + ); + } return ( @@ -71,14 +66,7 @@ export const GraphiQLPage: FC<{}> = () => {
- - setTabIndex(value)}> - {tabs.map(({ title }, index) => ( - - ))} - - - + {content}
); }; diff --git a/plugins/graphiql/src/components/index.ts b/plugins/graphiql/src/components/index.ts index ed11f03c40..c0d2228951 100644 --- a/plugins/graphiql/src/components/index.ts +++ b/plugins/graphiql/src/components/index.ts @@ -15,3 +15,4 @@ */ export * from './GraphiQLPage'; +export * from './GraphiQLBrowser'; diff --git a/plugins/graphiql/src/index.ts b/plugins/graphiql/src/index.ts index 3a0a0fe2d3..a1f5587fe1 100644 --- a/plugins/graphiql/src/index.ts +++ b/plugins/graphiql/src/index.ts @@ -15,3 +15,4 @@ */ export { plugin } from './plugin'; +export * from './lib/api'; From f75ff200a4bd208edc12e1d7ef3e01f9c6605278 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 13:23:42 +0200 Subject: [PATCH 14/21] plugins/graphiql: make tabs a bit more visible --- .../components/GraphiQLBrowser/GraphiQLBrowser.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx index 69ceec3136..e914fd9649 100644 --- a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx +++ b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx @@ -15,18 +15,22 @@ */ import React, { FC, useState } from 'react'; -import { Tabs, Tab, makeStyles, Typography } from '@material-ui/core'; +import { Tabs, Tab, makeStyles, Typography, Divider } from '@material-ui/core'; import 'graphiql/graphiql.css'; import GraphiQL from 'graphiql'; import { StorageBucket } from 'lib/storage'; import { GraphQLEndpoint } from 'lib/api'; +import { BackstageTheme } from '@backstage/theme'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ root: { height: '100%', display: 'flex', flexFlow: 'column nowrap', }, + tabs: { + background: theme.palette.background.paper, + }, graphiQlWrapper: { flex: 1, '@global': { @@ -35,7 +39,7 @@ const useStyles = makeStyles({ }, }, }, -}); +})); type GraphiQLBrowserProps = { endpoints: GraphQLEndpoint[]; @@ -55,6 +59,7 @@ export const GraphiQLBrowser: FC = ({ endpoints }) => { return (
setTabIndex(value)} indicatorColor="primary" @@ -63,6 +68,7 @@ export const GraphiQLBrowser: FC = ({ endpoints }) => { ))} +
From d8b4a26a76bf18e7f5c056f8247c4be72ee0643c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 14:34:32 +0200 Subject: [PATCH 15/21] plugins/graphiql: rename GraphQLBrowser to GraphQLEndpoints and simplify method names --- plugins/graphiql/dev/index.tsx | 8 ++++---- .../lib/api/{GraphQLBrowser.ts => GraphQLEndpoints.ts} | 9 +++++---- plugins/graphiql/src/lib/api/index.ts | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) rename plugins/graphiql/src/lib/api/{GraphQLBrowser.ts => GraphQLEndpoints.ts} (86%) diff --git a/plugins/graphiql/dev/index.tsx b/plugins/graphiql/dev/index.tsx index bf0304df23..ed987c1c6a 100644 --- a/plugins/graphiql/dev/index.tsx +++ b/plugins/graphiql/dev/index.tsx @@ -28,15 +28,15 @@ import { ApiRegistry, } from '@backstage/core'; import { lightTheme } from '@backstage/theme'; -import { plugin, GraphQLBrowser, graphQlBrowseApiRef } from '../src'; +import { plugin, GraphQLEndpoints, graphQlBrowseApiRef } from '../src'; -const graphQlBrowseApi = GraphQLBrowser.fromEndpoints([ - GraphQLBrowser.createEndpoint({ +const graphQlBrowseApi = GraphQLEndpoints.from([ + GraphQLEndpoints.create({ id: 'gitlab', title: 'GitLab', url: 'https://gitlab.com/api/graphql', }), - GraphQLBrowser.createEndpoint({ + GraphQLEndpoints.create({ id: 'countries', title: 'Countries', url: 'https://countries.trevorblades.com/', diff --git a/plugins/graphiql/src/lib/api/GraphQLBrowser.ts b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts similarity index 86% rename from plugins/graphiql/src/lib/api/GraphQLBrowser.ts rename to plugins/graphiql/src/lib/api/GraphQLEndpoints.ts index 645658237d..c407d92289 100644 --- a/plugins/graphiql/src/lib/api/GraphQLBrowser.ts +++ b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts @@ -28,8 +28,9 @@ export type EndpointConfig = { headers?: { [name in string]: string }; }; -export class GraphQLBrowser implements GraphQLBrowseApi { - static createEndpoint(config: EndpointConfig): GraphQLEndpoint { +export class GraphQLEndpoints implements GraphQLBrowseApi { + // Create a support + static create(config: EndpointConfig): GraphQLEndpoint { const { id, title, url, method = 'POST' } = config; return { id, @@ -50,8 +51,8 @@ export class GraphQLBrowser implements GraphQLBrowseApi { }; } - static fromEndpoints(endpoints: GraphQLEndpoint[]) { - return new GraphQLBrowser(endpoints); + static from(endpoints: GraphQLEndpoint[]) { + return new GraphQLEndpoints(endpoints); } private constructor(private readonly endpoints: GraphQLEndpoint[]) {} diff --git a/plugins/graphiql/src/lib/api/index.ts b/plugins/graphiql/src/lib/api/index.ts index e5ae2131ea..bcc7966cbe 100644 --- a/plugins/graphiql/src/lib/api/index.ts +++ b/plugins/graphiql/src/lib/api/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export * from './GraphQLBrowser'; +export * from './GraphQLEndpoints'; export * from './types'; From 3063bd07bf4102cc970f8c208f4b5691092bb7bc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 14:35:09 +0200 Subject: [PATCH 16/21] plugins/graphiql: update README --- plugins/graphiql/README.md | 66 +++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/plugins/graphiql/README.md b/plugins/graphiql/README.md index da0ca235fd..5882b8229e 100644 --- a/plugins/graphiql/README.md +++ b/plugins/graphiql/README.md @@ -1,6 +1,64 @@ -# Title -Welcome to the graphiql plugin! +# @backstage/plugin-graphiql -## Sub-section 1 +This plugin integrates [GraphiQL](https://github.com/graphql/graphiql) as a tool to browse GraphiQL endpoints inside Backstage. -## Sub-section 2 +The purpose of the plugin is to provide a convenient way for developers to try out GraphQL queries in their own environemnt. +By exposing GraphiQL as a plugin instead of a standalone app, it's possible to provide a preconfigured environment for engineers, and also tie into authentication providers already inside Backstage. + +## Getting Started + +### Installing the plugin + +Start out by installing the plugin in your Backstage app: + +```bash +yarn add @backstage/plugin-graphiql +``` + +Then add an entry to your App's `plugins.ts` to import the plugin. + +The plugin registers a `/graphiql` route, which you can link to from the Sidebar if desired. + +### Adding GraphQL endpoints + +For the plugin to function, you need to supply GraphQL endpoints through the GraphQLBrowse API, which is done by implementing the `GraphQLBrowseApi` exported by this plugin. + +If all you need is a static list of endpoints, the plugin exports a `GraphQLEndpoints` class that implements the `GraphQLBrowseApi` for you. Here's and example of how you could expose two GraphQL endpoints in your App: + +```tsx +import { + graphQlBrowseApiRef, + GraphQLEndpoints, +} from '@backstage/plugin-graphiql'; + +// Implement the Graph QL browse API using a static list of endpoints +const graphQlBrowseApi = GraphQLEndpoints.from([ + // Use the .create function if all you need is a static URL and headers. + GraphQLEndpoints.create({ + id: 'gitlab', + title: 'GitLab', + url: 'https://gitlab.com/api/graphql', + // Optional extra headers + headers: { Extra: 'Header' }, + }), + { + id: 'hooli-search', + title: 'Hooli Search', + // Custom fetch function, this one is equivalent to using GraphQLEndpoints.create() + // with url set to https://internal.hooli.com/search + fetcher: async (params: any) => { + return fetch('https://internal.hooli.com/search', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params), + }).then(res => res.json()); + }, + }, +]); + +// ApiRegistry builder created somewhere in your App +const builder = ApiRegistry.builder(); + +// Add the instance to the API registry +builder.add(graphQlBrowseApiRef, graphQlBrowseApi); +``` From f2f3b47894756a0195e4ac14580c00af5d4635bd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 14:45:47 +0200 Subject: [PATCH 17/21] packages/app: remove graphiql plugin --- packages/app/package.json | 1 - packages/app/src/plugins.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index b596b541bd..2c8b10ffb3 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -20,7 +20,6 @@ "dependencies": { "@backstage/cli": "^0.1.1-alpha.4", "@backstage/core": "^0.1.1-alpha.4", - "@backstage/plugin-graphiql": "^0.1.1-alpha.4", "@backstage/plugin-home-page": "^0.1.1-alpha.4", "@backstage/plugin-lighthouse": "^0.1.1-alpha.4", "@backstage/plugin-welcome": "^0.1.1-alpha.4", diff --git a/packages/app/src/plugins.ts b/packages/app/src/plugins.ts index b39ff948a9..919ea0666b 100644 --- a/packages/app/src/plugins.ts +++ b/packages/app/src/plugins.ts @@ -16,4 +16,3 @@ export { plugin as HomePagePlugin } from '@backstage/plugin-home-page'; export { plugin as WelcomePlugin } from '@backstage/plugin-welcome'; export { plugin as LighthousePlugin } from '@backstage/plugin-lighthouse'; -export { plugin as Graphiql } from '@backstage/plugin-graphiql'; From f1b7dc8b31e1b217e9281bbfdec182d237755d72 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 14:56:04 +0200 Subject: [PATCH 18/21] plugins/graphiql: add publishing config --- plugins/graphiql/package.json | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 8f50c59e66..a068eb24c0 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,10 +1,23 @@ { "name": "@backstage/plugin-graphiql", + "description": "Backstage plugin for browsing GraphQL APIs", "version": "0.1.1-alpha.4", + "private": false, + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/spotify/backstage/tree/master/plugins/graphiql#readme", + "repository": { + "type": "git", + "url": "https://github.com/spotify/backstage", + "directory": "plugins/graphiql" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", "main": "dist/index.cjs.js", "types": "dist/index.d.ts", - "license": "Apache-2.0", - "private": true, "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", @@ -34,5 +47,8 @@ "react": "16.13.1", "react-dom": "16.13.1", "react-use": "^13.0.0" - } + }, + "files": [ + "dist" + ] } From 24e6178392e21879ad9b5d90d90c93d4d4aa962e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 15:50:38 +0200 Subject: [PATCH 19/21] plugins/graphiql: Fix typo in README Co-Authored-By: Himanshu Mishra --- plugins/graphiql/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/graphiql/README.md b/plugins/graphiql/README.md index 5882b8229e..d2f8cafbfb 100644 --- a/plugins/graphiql/README.md +++ b/plugins/graphiql/README.md @@ -2,7 +2,7 @@ This plugin integrates [GraphiQL](https://github.com/graphql/graphiql) as a tool to browse GraphiQL endpoints inside Backstage. -The purpose of the plugin is to provide a convenient way for developers to try out GraphQL queries in their own environemnt. +The purpose of the plugin is to provide a convenient way for developers to try out GraphQL queries in their own environment. By exposing GraphiQL as a plugin instead of a standalone app, it's possible to provide a preconfigured environment for engineers, and also tie into authentication providers already inside Backstage. ## Getting Started From 7897d3488cbda2eb9b83abcc3bab30c0f34094cc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 16:43:18 +0200 Subject: [PATCH 20/21] plugins/graphiql: update tests for GraphiQLPage + fixes --- plugins/graphiql/.eslintrc.js | 4 + plugins/graphiql/package.json | 1 + .../GraphiQLPage/GraphiQLPage.test.tsx | 73 ++++++++++++++++--- .../components/GraphiQLPage/GraphiQLPage.tsx | 5 +- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/plugins/graphiql/.eslintrc.js b/plugins/graphiql/.eslintrc.js index 13573efa9c..6a513fbafe 100644 --- a/plugins/graphiql/.eslintrc.js +++ b/plugins/graphiql/.eslintrc.js @@ -1,3 +1,7 @@ module.exports = { extends: [require.resolve('@backstage/cli/config/eslint')], + rules: { + // Prefer to use rendered.getBy*, which will throw an error + 'jest/expect-expect': 0, + }, }; diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index a068eb24c0..f2cf8b7930 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -38,6 +38,7 @@ }, "dependencies": { "@backstage/core": "^0.1.1-alpha.4", + "@backstage/test-utils": "^0.1.1-alpha.4", "@backstage/theme": "^0.1.1-alpha.4", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx index 4dd18c326f..d19e05524e 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.test.tsx @@ -15,23 +15,74 @@ */ import React from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; import { GraphiQLPage } from './GraphiQLPage'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { renderWithEffects } from '@backstage/test-utils'; +import { GraphQLBrowseApi, graphQlBrowseApiRef } from 'lib/api'; -jest.mock('graphiql', () => () => ''); +jest.mock('components/GraphiQLBrowser', () => ({ + GraphiQLBrowser: () => '', +})); describe('GraphiQLPage', () => { - it('should render', () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render( - - - , + it('should show progress', async () => { + const loadingApi: GraphQLBrowseApi = { + async getEndpoints() { + await new Promise(() => {}); + return []; + }, + }; + + const rendered = await renderWithEffects( + + + + + , + , ); - expect(rendered.getByText('GraphiQL')).toBeInTheDocument(); - expect(rendered.getByText('')).toBeInTheDocument(); + + rendered.getByText('GraphiQL'); + rendered.getByTestId('progress'); + }); + + it('should show error', async () => { + const loadingApi: GraphQLBrowseApi = { + async getEndpoints() { + throw new Error('NOPE'); + }, + }; + + const rendered = await renderWithEffects( + + + + + , + ); + + rendered.getByText('GraphiQL'); + rendered.getByText('Failed to load GraphQL endpoints, Error: NOPE'); + }); + + it('should show GraphiQLBrowser', async () => { + const loadingApi: GraphQLBrowseApi = { + async getEndpoints() { + return []; + }, + }; + + const rendered = await renderWithEffects( + + + + + , + ); + + rendered.getByText('GraphiQL'); + rendered.getByText(''); }); }); diff --git a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx index 90a0bb92c0..39ee015bed 100644 --- a/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx +++ b/plugins/graphiql/src/components/GraphiQLPage/GraphiQLPage.tsx @@ -46,9 +46,8 @@ export const GraphiQLPage: FC<{}> = () => { content = ( - Failed to load GraphQL endpoints, {endpoints.error} -
- We also need a proper error component + {/* TODO: provide a proper error component */} + Failed to load GraphQL endpoints, {String(endpoints.error)}
); From 0e78bc193563941781bbe4100678fbd8a57d7973 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 25 Apr 2020 16:47:29 +0200 Subject: [PATCH 21/21] plugins/graphiql: fix GraphiQLBrowser tests --- .../GraphiQLBrowser/GraphiQLBrowser.test.tsx | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx index 4dd18c326f..1726c188c0 100644 --- a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx +++ b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.test.tsx @@ -16,22 +16,42 @@ import React from 'react'; import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import { GraphiQLPage } from './GraphiQLPage'; +import { GraphiQLBrowser } from './GraphiQLBrowser'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; jest.mock('graphiql', () => () => ''); -describe('GraphiQLPage', () => { - it('should render', () => { - mockFetch.mockResponse(() => new Promise(() => {})); +describe('GraphiQLBrowser', () => { + it('should render error text if there are no endpoints', () => { const rendered = render( - + , ); - expect(rendered.getByText('GraphiQL')).toBeInTheDocument(); - expect(rendered.getByText('')).toBeInTheDocument(); + rendered.getByText('No endpoints available'); + }); + + it('should render endpoint tabs', () => { + const rendered = render( + + + , + ); + rendered.getByText('Endpoint A'); + rendered.getByText('Endpoint B'); }); });