Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[graphiql] fix graphiql plugin lifecycle bugs, simplify #3330

Merged
merged 10 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .changeset/late-tables-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
'@graphiql/plugin-code-exporter': minor
'@graphiql/plugin-explorer': minor
---

**BREAKING CHANGE**: fix lifecycle issue in plugin-explorer, change implementation pattern

`value` and `setValue` is no longer an implementation detail, and are handled internally by plugins.
the plugin signature has changed slightly as well.

now, instead of something like this:

```js
import { useExplorerPlugin } from '@graphiql/plugin-explorer';
import { snippets } from './snippets';
import { useExporterPlugin } from '@graphiql/plugin-code-exporter';

const App = () => {
const [query, setQuery] = React.useState('');
const explorerPlugin = useExplorerPlugin({
query,
onEdit: setQuery,
});
const codeExporterPlugin = useExporterPlugin({
query,
snippets,
});

const plugins = React.useMemo(
() => [explorerPlugin, codeExporterPlugin],
[explorerPlugin, codeExporterPlugin],
);

return (
<GraphiQL
query={query}
onEditQuery={setQuery}
plugins={plugins}
fetcher={fetcher}
/>
);
};
```

you can just do this:

```js
import { explorerPlugin } from '@graphiql/plugin-explorer';
import { snippets } from './snippets';
import { codeExporterPlugin } from '@graphiql/plugin-code-exporter';
import { createGraphiQLFetcher } from '@graphiql/toolkit'

// only invoke these inside the component lifecycle
// if there are dynamic values, and then use useMemo() (see below)
const explorer = explorerPlugin();
const exporter = codeExporterPlugin({ snippets });

const fetcher = createGraphiQLFetcher({ url: '/graphql' })

const App = () => {
return (
<GraphiQL
plugins={[explorer, exporter]}
fetcher={fetcher}
/>
);
};
```

or this, for more complex state-driven needs:

```js
import { useMemo } from 'react'
import { explorerPlugin } from '@graphiql/plugin-explorer';
import { snippets } from './snippets';
import { codeExporterPlugin } from '@graphiql/plugin-code-exporter';

const explorer = explorerPlugin();
const fetcher = createGraphiQLFetcher({ url: '/graphql' })

const App = () => {
const {snippets} = useMyUserSuppliedState()
const exporter = useMemo(() => codeExporterPlugin({ snippets }), [snippets])

return (
<GraphiQL
plugins={[explorer, exporter]}
fetcher={fetcher}
/>
);
};
```
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ packages/graphiql/*.css
packages/graphiql/*.map
packages/graphiql/cypress/screenshots/
packages/graphiql/typedoc/
packages/graphiql/webpack/
3 changes: 3 additions & 0 deletions custom-words.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// (user-)names
arthurgeron
Divyendu
Leko
LekoArts
Expand Down Expand Up @@ -94,6 +95,8 @@ vitest
vitejs
wonka
urql
tsup
usememo

// identifiers used in code and configs
acmerc
Expand Down
12 changes: 6 additions & 6 deletions examples/graphiql-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
"license": "MIT",
"description": "A GraphiQL example with webpack and typescript",
"scripts": {
"build-demo": "webpack-cli",
"start": "cross-env NODE_ENV=development webpack-dev-server"
"build-demo": "webpack-cli && mkdirp ../../packages/graphiql/webpack && cp -r dist/** ../../packages/graphiql/webpack",
"start": "NODE_ENV=development webpack-dev-server"
},
"dependencies": {
"@graphiql/plugin-code-exporter": "^0.1.1",
"@graphiql/plugin-explorer": "^0.1.12",
"@graphiql/toolkit": "^0.8.0",
"graphiql": "^2.2.0",
"@graphiql/plugin-code-exporter": "^0.2.0",
"@graphiql/plugin-explorer": "^0.2.0",
"@graphiql/toolkit": "^0.8.4",
"graphiql": "^3.0.0",
"graphql": "^16.4.0",
"graphql-ws": "^5.5.5",
"react": "^18.2.0",
Expand Down
80 changes: 17 additions & 63 deletions examples/graphiql-webpack/src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,18 @@ import 'regenerator-runtime/runtime.js';
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { GraphiQL } from 'graphiql';
import { useExplorerPlugin } from '@graphiql/plugin-explorer';
import { useExporterPlugin } from '@graphiql/plugin-code-exporter';
import { explorerPlugin } from '@graphiql/plugin-explorer';
import { snippets } from './snippets';
import { codeExporterPlugin } from '@graphiql/plugin-code-exporter';
import 'graphiql/graphiql.css';
import '@graphiql/plugin-explorer/dist/style.css';
import '@graphiql/plugin-code-exporter/dist/style.css';

const removeQueryName = query =>
query.replace(
/^[^{(]+([{(])/,
(_match, openingCurlyBracketsOrParenthesis) =>
`query ${openingCurlyBracketsOrParenthesis}`,
);

const getQuery = (arg, spaceCount) => {
const { operationDataList } = arg;
const { query } = operationDataList[0];
const anonymousQuery = removeQueryName(query);
return (
' '.repeat(spaceCount) +
anonymousQuery.replaceAll('\n', '\n' + ' '.repeat(spaceCount))
);
};

const exampleSnippetOne = {
name: 'Example One',
language: 'JavaScript',
codeMirrorMode: 'jsx',
options: [],
generate: arg => `export const query = graphql\`
${getQuery(arg, 2)}
\`
`,
};

const exampleSnippetTwo = {
name: 'Example Two',
language: 'JavaScript',
codeMirrorMode: 'jsx',
options: [],
generate: arg => `import { graphql } from 'graphql'

export const query = graphql\`
${getQuery(arg, 2)}
\`
`,
};

const snippets = [exampleSnippetOne, exampleSnippetTwo];

/**
* A manual fetcher implementation, you should probably
* just use `createGraphiQLFetcher` from `@graphiql/toolkit
* @returns
*/
const fetcher = async (graphQLParams, options) => {
const data = await fetch(
'https://swapi-graphql.netlify.app/.netlify/functions/index',
Expand All @@ -69,29 +32,20 @@ const fetcher = async (graphQLParams, options) => {
};

const style = { height: '100vh' };
/**
* instantiate outside of the component lifecycle
* unless you need to pass it dynamic values from your react app,
* then use the `useMemo` hook
*/
const explorer = explorerPlugin();
const exporter = codeExporterPlugin({ snippets });

const App = () => {
const [query, setQuery] = React.useState('');
const explorerPlugin = useExplorerPlugin({
query,
onEdit: setQuery,
});
const exporterPlugin = useExporterPlugin({
query,
snippets,
});

const plugins = React.useMemo(
() => [explorerPlugin, exporterPlugin],
[explorerPlugin, exporterPlugin],
);

return (
<GraphiQL
style={style}
query={query}
onEditQuery={setQuery}
plugins={plugins}
// eslint-disable-next-line @arthurgeron/react-usememo/require-usememo
plugins={[explorer, exporter]}
fetcher={fetcher}
/>
);
Expand Down
41 changes: 41 additions & 0 deletions examples/graphiql-webpack/src/snippets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const removeQueryName = query =>
query.replace(
/^[^{(]+([{(])/,
(_match, openingCurlyBracketsOrParenthesis) =>
`query ${openingCurlyBracketsOrParenthesis}`,
);

const getQuery = (arg, spaceCount) => {
const { operationDataList } = arg;
const { query } = operationDataList[0];
const anonymousQuery = removeQueryName(query);
return (
' '.repeat(spaceCount) +
anonymousQuery.replaceAll('\n', '\n' + ' '.repeat(spaceCount))
);
};

const exampleSnippetOne = {
name: 'Example One',
language: 'JavaScript',
codeMirrorMode: 'jsx',
options: [],
generate: arg => `export const query = graphql\`
${getQuery(arg, 2)}
\`
`,
};

const exampleSnippetTwo = {
name: 'Example Two',
language: 'JavaScript',
codeMirrorMode: 'jsx',
options: [],
generate: arg => `import { graphql } from 'graphql'
export const query = graphql\`
${getQuery(arg, 2)}
\`
`,
};

export const snippets = [exampleSnippetOne, exampleSnippetTwo];
2 changes: 1 addition & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
publish = "packages/graphiql"

# Default build command.
command = "yarn build && yarn build-bundles && yarn build-docs"
command = "yarn build && yarn build-bundles && yarn build-docs && yarn build-demo"
environment = { YARN_FLAGS = "--frozen-lockfile --immutable"}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"packages/*",
"examples/monaco-graphql-webpack",
"examples/monaco-graphql-nextjs",
"examples/monaco-graphql-react-vite"
"examples/monaco-graphql-react-vite",
"examples/graphiql-webpack"
]
},
"lint-staged": {
Expand Down Expand Up @@ -36,7 +37,6 @@
"build-bundles": "yarn prebuild-bundles && wsrun -p -m -s build-bundles",
"build-bundles-clean": "rimraf '{packages,examples,plugins}/**/{bundle,cdn,webpack}' && yarn workspace graphiql build-bundles-clean",
"build-clean": "wsrun -m build-clean",
"build-demo": "wsrun -m -s build-demo",
"build-docs": "rimraf packages/graphiql/typedoc && typedoc packages",
"build:clean": "yarn tsc --clean && yarn tsc --clean resources/tsconfig.graphiql.json",
"build:cm6-graphql": "yarn workspace cm6-graphql build",
Expand All @@ -46,6 +46,7 @@
"build:graphiql-react": "yarn workspace @graphiql/react build",
"build:packages": "yarn tsc",
"build:watch": "yarn tsc --watch",
"build-demo": "wsrun -m build-demo",
"watch": "yarn build:watch",
"watch-vscode": "yarn workspace vscode-graphql compile",
"watch-vscode-exec": "yarn workspace vscode-graphql-execution compile",
Expand Down
20 changes: 7 additions & 13 deletions packages/graphiql-plugin-code-exporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ for all details on available `props` and how to
[create snippets](https://github.com/OneGraph/graphiql-code-exporter#snippets).

```jsx
import { useExporterPlugin } from '@graphiql/plugin-code-exporter';
import { codeExporterPlugin } from '@graphiql/plugin-code-exporter';
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { GraphiQL } from 'graphiql';
import { useState } from 'react';
Expand Down Expand Up @@ -86,20 +86,14 @@ ${getQuery(arg, 2)}

const snippets = [exampleSnippetOne, exampleSnippetTwo];

const exporter = codeExporterPlugin({
snippets,
codeMirrorTheme: 'graphiql',
});

function GraphiQLWithExplorer() {
const [query, setQuery] = useState(DEFAULT_QUERY);
const exporterPlugin = useExporterPlugin({
query,
snippets,
codeMirrorTheme: 'graphiql',
});
return (
<GraphiQL
fetcher={fetcher}
query={query}
onEditQuery={setQuery}
plugins={[exporterPlugin]}
/>
<GraphiQL fetcher={fetcher} defaultQuery={query} plugins={[exporter]} />
);
}
```
Expand Down
20 changes: 9 additions & 11 deletions packages/graphiql-plugin-code-exporter/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
crossorigin="anonymous"
></script>
<script
src="https://unpkg.com/@graphiql/plugin-code-exporter/dist/graphiql-plugin-code-exporter.umd.js"
src="https://unpkg.com/@graphiql/plugin-code-exporter/dist/index.umd.js"
integrity="sha512-NwP+k36ExLYeIqp2lniZCblbz/FLJ/lQlBV55B6vafZWIYppwHUp1gCdvlaaUjV95RWPInQy4z/sIa56psJy/g=="
crossorigin="anonymous"
></script>
Expand Down Expand Up @@ -100,21 +100,19 @@
};

var snippets = [exampleSnippetOne, exampleSnippetTwo];
var codeExporterPlugin = GraphiQLPluginCodeExporter.codeExporterPlugin({
snippets,
});

var query =
'query AllFilms {\n allFilms {\n films {\n title\n }\n }\n}';

function GraphiQLWithExporter() {
var [query, setQuery] = React.useState(
'query AllFilms {\n allFilms {\n films {\n title\n }\n }\n}',
);
var exporterPlugin = GraphiQLPluginCodeExporter.useExporterPlugin({
query: query,
snippets,
});
return React.createElement(GraphiQL, {
fetcher: fetcher,
defaultEditorToolsVisibility: true,
plugins: [exporterPlugin],
query: query,
onEditQuery: setQuery,
plugins: [codeExporterPlugin],
defaultQuery: query,
});
}

Expand Down
3 changes: 2 additions & 1 deletion packages/graphiql-plugin-code-exporter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"peerDependencies": {
"graphql": "^15.5.0 || ^16.0.0",
"react": "^16.8.0 || ^17 || ^18",
"react-dom": "^16.8.0 || ^17 || ^18"
"react-dom": "^16.8.0 || ^17 || ^18",
"@graphiql/react": "^0.18.0"
},
"devDependencies": {
"@graphiql/react": "^0.18.0",
Expand Down
Loading