Skip to content

Commit

Permalink
[graphiql] fix #2859, graphiql plugin lifecycle bugs, simplify plugins (
Browse files Browse the repository at this point in the history
#3330)

- simplify plugin development
- simplify plugin implementation
- fix #2859 cursor jumping issue
- drop the `use` prefix, because they should not be invoked as react hooks, but widely used eslint rules will assume they are hooks
- update examples for 0.2.0 breaking change
- consistent naming
  - `@graphiql/plugin-code-exporter` -> exports `codeExporterPlugin()` module
  - `@graphiql/plugin-explorer` ->  exports `explorerPlugin()` module
  • Loading branch information
acao authored Jul 11, 2023
1 parent d7f595e commit bed5fc8
Show file tree
Hide file tree
Showing 17 changed files with 1,380 additions and 175 deletions.
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

0 comments on commit bed5fc8

Please sign in to comment.