diff --git a/.changeset/beige-months-care.md b/.changeset/beige-months-care.md new file mode 100644 index 00000000000..a8d94cd063c --- /dev/null +++ b/.changeset/beige-months-care.md @@ -0,0 +1,14 @@ +--- +'@graphiql/plugin-doc-explorer': minor +'@graphiql/plugin-explorer': major +'@graphiql/plugin-history': minor +'@graphiql/react': minor +'graphiql': major +--- + +- allow multiple independent instances of GraphiQL on the same page +- store `onClickReference` in query editor in React `ref` +- remove `onClickReference` from variable editor +- fix shortcut text per OS for run query in execute query button's tooltip and in default query +- allow override all default GraphiQL plugins +- adjust operation argument color to be purple from GraphiQL v2 on dark/light theme diff --git a/.changeset/chilly-sloths-heal.md b/.changeset/chilly-sloths-heal.md new file mode 100644 index 00000000000..8e3a2fe9007 --- /dev/null +++ b/.changeset/chilly-sloths-heal.md @@ -0,0 +1,26 @@ +--- +'@graphiql/plugin-doc-explorer': patch +'@graphiql/plugin-explorer': patch +'@graphiql/plugin-history': patch +'codemirror-graphql': patch +'@graphiql/react': minor +'graphiql': patch +--- + +- replace `onCopyQuery` hook with `copyQuery` function +- replace `onMergeQuery` hook with `mergeQuery` function +- replace `onPrettifyEditors` hook with `prettifyEditors` function +- remove `fetcher` prop from `SchemaContextProvider` and `schemaStore` and add `fetcher` to `executionStore` +- add `onCopyQuery` and `onPrettifyQuery` props to `EditorContextProvider` +- remove exports (use `GraphiQLProvider`) + - `EditorContextProvider` + - `ExecutionContextProvider` + - `PluginContextProvider` + - `SchemaContextProvider` + - `StorageContextProvider` + - `ExecutionContextType` + - `PluginContextType` +- feat(@graphiql/react): migrate React context to zustand: + - replace `useExecutionContext` with `useExecutionStore` hook + - replace `useEditorContext` with `useEditorStore` hook +- prefer `getComputedStyle` over `window.getComputedStyle` diff --git a/.changeset/config.json b/.changeset/config.json index 36fce60cf83..76529501bc1 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -6,6 +6,7 @@ "access": "public", "baseBranch": "main", "ignore": [ + "example-graphiql-vite", "example-graphiql-webpack", "example-monaco-graphql-nextjs", "example-monaco-graphql-react-vite", diff --git a/.changeset/dull-balloons-warn.md b/.changeset/dull-balloons-warn.md new file mode 100644 index 00000000000..6051c2248d9 --- /dev/null +++ b/.changeset/dull-balloons-warn.md @@ -0,0 +1,6 @@ +--- +'@graphiql/react': minor +'graphiql': major +--- + +update graphiql-cdn example to show how to load workers with esm.sh diff --git a/.changeset/eleven-rings-complain.md b/.changeset/eleven-rings-complain.md new file mode 100644 index 00000000000..1d92d31366e --- /dev/null +++ b/.changeset/eleven-rings-complain.md @@ -0,0 +1,8 @@ +--- +'@graphiql/plugin-doc-explorer': minor +'@graphiql/plugin-explorer': major +'@graphiql/react': minor +'graphiql': major +--- + +separate store actions from state, add `useGraphiQLActions` state diff --git a/.changeset/fluffy-beers-build.md b/.changeset/fluffy-beers-build.md new file mode 100644 index 00000000000..35437b1343f --- /dev/null +++ b/.changeset/fluffy-beers-build.md @@ -0,0 +1,5 @@ +--- +'@graphiql/plugin-doc-explorer': patch +--- + +push field type on stack too before field diff --git a/.changeset/four-carrots-leave.md b/.changeset/four-carrots-leave.md new file mode 100644 index 00000000000..1b1d50aecea --- /dev/null +++ b/.changeset/four-carrots-leave.md @@ -0,0 +1,5 @@ +--- +'@graphiql/plugin-doc-explorer': patch +--- + +Use an additional `Alt` key for focus doc explorer search input instead of `Cmd/Ctrl+K` because monaco-editor has a built-in shortcut for `Cmd/Ctrl+K` diff --git a/.changeset/good-turtles-speak.md b/.changeset/good-turtles-speak.md new file mode 100644 index 00000000000..074d1bfe29d --- /dev/null +++ b/.changeset/good-turtles-speak.md @@ -0,0 +1,7 @@ +--- +'@graphiql/plugin-code-exporter': major +'@graphiql/plugin-explorer': major +'graphiql': major +--- + +remove UMD builds diff --git a/.changeset/gorgeous-lobsters-run.md b/.changeset/gorgeous-lobsters-run.md new file mode 100644 index 00000000000..402ede4a3ee --- /dev/null +++ b/.changeset/gorgeous-lobsters-run.md @@ -0,0 +1,7 @@ +--- +'@graphiql/react': patch +--- + +- run cypress tests in React strict mode +- fix `defaultQuery` with empty string does not result in an empty default query +- fix `useDidUpdate` in React strict mode diff --git a/.changeset/honest-clouds-brush.md b/.changeset/honest-clouds-brush.md new file mode 100644 index 00000000000..9323b0d60b7 --- /dev/null +++ b/.changeset/honest-clouds-brush.md @@ -0,0 +1,11 @@ +--- +'monaco-graphql': patch +--- + +remove unused types + +- `BaseSchemaConfig` +- `IDisposable` +- `JSONDiagnosticOptions` +- `IEvent` +- `FilePointer` diff --git a/.changeset/kind-timers-exist.md b/.changeset/kind-timers-exist.md new file mode 100644 index 00000000000..3d5317262f2 --- /dev/null +++ b/.changeset/kind-timers-exist.md @@ -0,0 +1,6 @@ +--- +'@graphiql/react': minor +'graphiql': major +--- + +support `externalFragments` prop and remove `validationRules` prop diff --git a/.changeset/many-ducks-visit.md b/.changeset/many-ducks-visit.md new file mode 100644 index 00000000000..eb5807ec8a7 --- /dev/null +++ b/.changeset/many-ducks-visit.md @@ -0,0 +1,7 @@ +--- +'@graphiql/react': minor +'graphiql': major +--- + +remove `readOnly` prop +document `keyMap` prop was removed in migration guide diff --git a/.changeset/nasty-pandas-taste.md b/.changeset/nasty-pandas-taste.md new file mode 100644 index 00000000000..bed977c73ce --- /dev/null +++ b/.changeset/nasty-pandas-taste.md @@ -0,0 +1,7 @@ +--- +'graphiql': major +--- + +Remove examples: `GraphiQL x Parcel` and `GraphiQL x Create React App` + +Add new examples: `GraphiQL x Vite` and `GraphiQL x Next.js` diff --git a/.changeset/orange-spies-poke.md b/.changeset/orange-spies-poke.md new file mode 100644 index 00000000000..a4ac0f649cd --- /dev/null +++ b/.changeset/orange-spies-poke.md @@ -0,0 +1,6 @@ +--- +'@graphiql/react': minor +'graphiql': minor +--- + +extract graphiql sidebar to react component diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 00000000000..f28120293be --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,48 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "example-graphiql-webpack": "0.0.0", + "example-monaco-graphql-nextjs": "0.0.0", + "example-monaco-graphql-react-vite": "0.0.0", + "example-monaco-graphql-webpack": "0.0.0", + "cm6-graphql": "0.2.1", + "codemirror-graphql": "2.2.1", + "graphiql": "4.1.1", + "@graphiql/plugin-code-exporter": "4.0.5", + "@graphiql/plugin-doc-explorer": "0.2.2", + "@graphiql/plugin-explorer": "4.0.6", + "@graphiql/plugin-history": "0.2.2", + "@graphiql/react": "0.34.1", + "@graphiql/toolkit": "0.11.3", + "graphql-language-service": "5.3.1", + "graphql-language-service-cli": "3.5.0", + "graphql-language-service-server": "2.14.8", + "monaco-graphql": "1.6.1", + "vscode-graphql": "0.13.2", + "vscode-graphql-execution": "0.3.2", + "vscode-graphql-syntax": "1.3.8", + "example-graphiql-nextjs": "0.0.0", + "example-graphiql-vite": "0.0.0" + }, + "changesets": [ + "beige-months-care", + "chilly-sloths-heal", + "dull-balloons-warn", + "eleven-rings-complain", + "fluffy-beers-build", + "four-carrots-leave", + "good-turtles-speak", + "honest-clouds-brush", + "kind-timers-exist", + "many-ducks-visit", + "nasty-pandas-taste", + "orange-spies-poke", + "shy-keys-rest", + "soft-cars-notice", + "sweet-lamps-love", + "ten-peas-carry", + "twelve-lies-invent", + "warm-shoes-boil" + ] +} diff --git a/.changeset/shy-keys-rest.md b/.changeset/shy-keys-rest.md new file mode 100644 index 00000000000..6944c8a120e --- /dev/null +++ b/.changeset/shy-keys-rest.md @@ -0,0 +1,5 @@ +--- +'@graphiql/plugin-doc-explorer': patch +--- + +show spinner in doc explorer based on `isIntrospecting` value, and not based on `isFetching` diff --git a/.changeset/soft-cars-notice.md b/.changeset/soft-cars-notice.md new file mode 100644 index 00000000000..c4b585982cc --- /dev/null +++ b/.changeset/soft-cars-notice.md @@ -0,0 +1,14 @@ +--- +'@graphiql/plugin-code-exporter': patch +'@graphiql/plugin-doc-explorer': minor +'@graphiql/plugin-explorer': patch +'@graphiql/plugin-history': patch +'@graphiql/react': minor +'graphiql': major +--- + +Migration from Codemirror to [Monaco Editor](https://github.com/microsoft/monaco-editor) + +Replacing `codemirror-graphql` with [`monaco-graphql`](https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql) + +Support for comments in **Variables** and **Headers** editors diff --git a/.changeset/sweet-lamps-love.md b/.changeset/sweet-lamps-love.md new file mode 100644 index 00000000000..02afd9698ce --- /dev/null +++ b/.changeset/sweet-lamps-love.md @@ -0,0 +1,8 @@ +--- +'@graphiql/plugin-history': minor +'@graphiql/react': minor +--- + +fix execute query shortcut in query editor, run it even there are no operations in query editor + +fix plugin store, save last opened plugin in storage diff --git a/.changeset/ten-peas-carry.md b/.changeset/ten-peas-carry.md new file mode 100644 index 00000000000..9ef67dcc245 --- /dev/null +++ b/.changeset/ten-peas-carry.md @@ -0,0 +1,5 @@ +--- +'@graphiql/react': minor +--- + +extract storage key constants diff --git a/.changeset/twelve-lies-invent.md b/.changeset/twelve-lies-invent.md new file mode 100644 index 00000000000..51788aee68c --- /dev/null +++ b/.changeset/twelve-lies-invent.md @@ -0,0 +1,6 @@ +--- +'graphiql': patch +--- + +- add f1 command as first item in shortcut table +- set color of `quickInputList.focusForeground` in command palette to be primary color diff --git a/.changeset/warm-shoes-boil.md b/.changeset/warm-shoes-boil.md new file mode 100644 index 00000000000..44805346809 --- /dev/null +++ b/.changeset/warm-shoes-boil.md @@ -0,0 +1,16 @@ +--- +'@graphiql/react': minor +'@graphiql/plugin-history': minor +'@graphiql/plugin-doc-explorer': minor +'graphiql': patch +--- + +- remove `useQueryEditor`, `useVariableEditor`, `useHeaderEditor`, `useResponseEditor` hooks +- remove `UseHeaderEditorArgs`, `UseQueryEditorArgs`, `UseResponseEditorArgs`, `UseVariableEditorArgs` exports +- rename components + - `StorageContextProvider` => `StorageStore` + - `EditorContextProvider` => `EditorStore` + - `SchemaContextProvider` => `SchemaStore` + - `ExecutionContextProvider` => `ExecutionStore` + - `HistoryContextProvider` => `HistoryStore` + - `ExplorerContextProvider` => `ExplorerStore` diff --git a/.eslintrc.js b/.eslintrc.js index 5f94c62ea4b..d2011addaac 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,8 +32,11 @@ module.exports = { 'functions/*', 'packages/vscode-graphql-syntax/tests/__fixtures__/*', // symlinks + 'packages/graphiql-react/__mocks__/monaco-editor.ts', 'packages/graphiql-plugin-doc-explorer/__mocks__/zustand.ts', + 'packages/graphiql-plugin-doc-explorer/__mocks__/monaco-editor.ts', 'packages/graphiql-plugin-history/__mocks__/zustand.ts', + 'packages/graphiql-plugin-history/__mocks__/monaco-editor.ts', ], overrides: [ { @@ -142,6 +145,15 @@ module.exports = { property: 'navigator', message: 'Use `navigator` instead', }, + { + object: 'window', + property: 'getComputedStyle', + message: 'Use `getComputedStyle` instead', + }, + { + object: 'self', + message: 'Use `globalThis` instead', + }, ], 'no-return-assign': 'error', 'no-return-await': 'error', @@ -171,7 +183,7 @@ module.exports = { 'init-declarations': 'off', 'no-catch-shadow': 'error', 'no-label-var': 'error', - 'no-restricted-globals': 'off', + 'no-restricted-globals': ['error', 'stop'], 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', 'no-undef-init': 'off', @@ -362,6 +374,7 @@ module.exports = { 'unicorn/no-length-as-slice-end': 'error', 'unicorn/prefer-string-replace-all': 'error', 'unicorn/prefer-array-some': 'error', + // '@typescript-eslint/prefer-for-of': 'error', TODO 'unicorn/no-hex-escape': 'off', // TODO: enable // doesn't catch a lot of cases; we use ESLint builtin `no-restricted-syntax` to forbid `.keyCode` 'unicorn/prefer-keyboard-event-key': 'off', @@ -374,6 +387,13 @@ module.exports = { 'import-x/no-named-as-default-member': 'off', }, }, + { + files: ['packages/{monaco-graphql,graphiql*}/**/*.{ts,tsx,mts,cts}'], + excludedFiles: ['packages/graphiql-toolkit/**/*.{ts,tsx}'], + rules: { + '@typescript-eslint/no-unnecessary-condition': 'error', + }, + }, { // Rules that requires type information files: ['**/*.{ts,tsx,mts,cts}'], @@ -388,6 +408,7 @@ module.exports = { '@typescript-eslint/consistent-type-assertions': 'error', '@typescript-eslint/no-duplicate-type-constituents': 'error', '@typescript-eslint/no-unnecessary-type-conversion': 'error', + // '@typescript-eslint/await-thenable': 'error', // TODO // TODO: Fix all errors for the following rules included in recommended config '@typescript-eslint/no-deprecated': 'off', '@typescript-eslint/no-unsafe-function-type': 'off', @@ -404,11 +425,10 @@ module.exports = { projectService: { allowDefaultProject: [ 'examples/monaco-graphql-react-vite/vite.config.ts', - 'packages/graphiql/vite.config.mts', - 'packages/{codemirror-graphql,graphiql-toolkit,graphql-language-service-cli,graphql-language-service,monaco-graphql,vscode-graphql-syntax,graphiql}/vitest.config.mts', + 'packages/{codemirror-graphql,graphiql-toolkit,graphql-language-service-cli,graphql-language-service,monaco-graphql,vscode-graphql-syntax}/vitest.config.mts', 'packages/cm6-graphql/__tests__/test.spec.ts', - 'packages/graphiql/src/GraphiQL.spec.tsx', + 'packages/graphiql/cypress.config.ts', 'packages/vscode-graphql-syntax/tests/*.spec.ts', 'packages/graphql-language-service-cli/src/__tests__/*.test.ts', 'packages/monaco-graphql/test/monaco-editor.test.ts', @@ -565,6 +585,7 @@ module.exports = { 'react-hooks/rules-of-hooks': 'off', 'sonarjs/no-dead-store': 'off', '@typescript-eslint/no-restricted-imports': 'off', + '@typescript-eslint/no-unnecessary-condition': 'off', }, }, ], diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2413edb267b..6f5c2afdf5a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release on: push: - branches: [main] + branches: [main, graphiql-5] permissions: {} jobs: release: diff --git a/.gitignore b/.gitignore index d2ac4afbd35..1b7064e3923 100644 --- a/.gitignore +++ b/.gitignore @@ -46,11 +46,7 @@ packages/codemirror-graphql/*.d.ts packages/codemirror-graphql/*.map !packages/codemirror-graphql/*.config.js -packages/graphiql/graphiql*.js -packages/graphiql/*.css -packages/graphiql/*.map packages/graphiql/cypress/screenshots/ +packages/graphiql/cypress/downloads/ packages/graphiql/typedoc/ packages/graphiql/webpack/ - -packages/graphiql/cypress/downloads/ diff --git a/cspell.json b/cspell.json index e08e52185cc..5eb97c0b4b2 100644 --- a/cspell.json +++ b/cspell.json @@ -19,6 +19,7 @@ "**/esbuild.js", ".eslintrc.js", ".vscode/extensions.json", + "packages/monaco-graphql/test/monaco-editor.test.ts", "working-group" ], "files": ["**/*.{js,cjs,mjs,ts,jsx,tsx,md,mdx,html,json,css,toml,yaml,yml}"] diff --git a/docs/migration/graphiql-5.0.0.md b/docs/migration/graphiql-5.0.0.md index 985cd2b3bdd..f2841495fb4 100644 --- a/docs/migration/graphiql-5.0.0.md +++ b/docs/migration/graphiql-5.0.0.md @@ -13,3 +13,8 @@ - Added new examples: [**GraphiQL x Vite**](https://github.com/graphql/graphiql/tree/graphiql-5/examples/graphiql-vite) and [**GraphiQL x Next.js**](https://github.com/graphql/graphiql/tree/graphiql-5/examples/graphiql-nextjs) - Removed examples: **GraphiQL x Parcel** and **GraphiQL x Create React App** +- UMD build is removed. Use the ESM-based CDN instead. +- Removed props + - `keyMap`. To use Vim or Emacs keybindings in Monaco, you can use community plugins. Monaco Vim: https://github.com/brijeshb42/monaco-vim. Monaco Emacs: https://github.com/aioutecism/monaco-emacs + - `readOnly` + - `validationRules`. Use custom GraphQL worker, see https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql#custom-webworker-for-passing-non-static-config-to-worker.' diff --git a/examples/graphiql-cdn/index.html b/examples/graphiql-cdn/index.html index d62b9728b87..1312edc635f 100644 --- a/examples/graphiql-cdn/index.html +++ b/examples/graphiql-cdn/index.html @@ -1,5 +1,5 @@ - - diff --git a/examples/graphiql-parcel/src/index.tsx b/examples/graphiql-parcel/src/index.tsx deleted file mode 100644 index d9116626d5b..00000000000 --- a/examples/graphiql-parcel/src/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import { GraphiQL } from 'graphiql'; -import type { Fetcher } from '@graphiql/toolkit'; -import { CSSProperties } from 'react'; - -const fetcher: Fetcher = async graphQLParams => { - const data = await fetch( - 'https://swapi-graphql.netlify.app/.netlify/functions/index', - { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(graphQLParams), - credentials: 'same-origin', - }, - ); - return data.json().catch(() => data.text()); -}; - -const style: CSSProperties = { height: '100vh' }; - -const App = () => ; - -const root = createRoot(document.getElementById('root')); -root.render(); - -// Hot Module Replacement -module.hot?.accept(); diff --git a/examples/graphiql-vite/README.md b/examples/graphiql-vite/README.md new file mode 100644 index 00000000000..a11f2cb0154 --- /dev/null +++ b/examples/graphiql-vite/README.md @@ -0,0 +1,8 @@ +# GraphiQL Vite Example + +This example demonstrates how to use GraphiQL with Vite. + +## Setup + +1. `yarn dev` to start Vite dev server. +1. `yarn build` to build production ready transpiled files. Find the output in `dist` folder. diff --git a/examples/graphiql-vite/index.html b/examples/graphiql-vite/index.html new file mode 100644 index 00000000000..80605d777de --- /dev/null +++ b/examples/graphiql-vite/index.html @@ -0,0 +1,33 @@ + + + + + + + GraphiQL Vite Example + + + + +
+
Loading…
+
+ + + diff --git a/examples/graphiql-vite/package.json b/examples/graphiql-vite/package.json new file mode 100644 index 00000000000..9828c04a61e --- /dev/null +++ b/examples/graphiql-vite/package.json @@ -0,0 +1,20 @@ +{ + "name": "example-graphiql-vite", + "version": "0.0.0", + "private": true, + "dependencies": { + "graphiql": "^5.0.0-rc.4", + "graphql": "^16.11.0", + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.4.1", + "vite": "^6.3.4" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "start": "vite preview" + } +} diff --git a/examples/graphiql-vite/public/favicon.svg b/examples/graphiql-vite/public/favicon.svg new file mode 100644 index 00000000000..29a8fdc208b --- /dev/null +++ b/examples/graphiql-vite/public/favicon.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + diff --git a/examples/graphiql-vite/src/App.jsx b/examples/graphiql-vite/src/App.jsx new file mode 100644 index 00000000000..9a7176aa4f7 --- /dev/null +++ b/examples/graphiql-vite/src/App.jsx @@ -0,0 +1,21 @@ +import { GraphiQL } from 'graphiql'; +import 'graphiql/setup-workers/vite'; +import 'graphiql/style.css'; + +async function fetcher(graphQLParams) { + const response = await fetch('https://graphql.earthdata.nasa.gov/api', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(graphQLParams), + }); + return response.json(); +} + +function App() { + return ; +} + +export default App; diff --git a/examples/graphiql-create-react-app/src/index.jsx b/examples/graphiql-vite/src/index.jsx similarity index 54% rename from examples/graphiql-create-react-app/src/index.jsx rename to examples/graphiql-vite/src/index.jsx index 16e6dbd33a2..78cad336875 100644 --- a/examples/graphiql-create-react-app/src/index.jsx +++ b/examples/graphiql-vite/src/index.jsx @@ -1,6 +1,5 @@ import { createRoot } from 'react-dom/client'; import App from './App'; -import './index.css'; -const root = createRoot(document.getElementById('root')); +const root = createRoot(document.getElementById('graphiql')); root.render(); diff --git a/examples/graphiql-vite/vite.config.mjs b/examples/graphiql-vite/vite.config.mjs new file mode 100644 index 00000000000..7206a00dc88 --- /dev/null +++ b/examples/graphiql-vite/vite.config.mjs @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + worker: { + format: 'es', + }, +}); diff --git a/examples/graphiql-webpack/package.json b/examples/graphiql-webpack/package.json index 4b8e285297e..b09dec3ec70 100644 --- a/examples/graphiql-webpack/package.json +++ b/examples/graphiql-webpack/package.json @@ -3,20 +3,21 @@ "version": "0.0.0", "private": true, "license": "MIT", - "description": "A GraphiQL example with webpack and typescript", + "description": "A GraphiQL example with Webpack", "scripts": { "build-demo": "webpack-cli && mkdirp ../../packages/graphiql/webpack && cp -r dist/** ../../packages/graphiql/webpack", "start": "NODE_ENV=development webpack-cli serve" }, "dependencies": { - "@graphiql/plugin-code-exporter": "^4.0.5", - "@graphiql/plugin-explorer": "^4.0.6", - "@graphiql/react": "^0.34.1", + "@graphiql/plugin-code-exporter": "^5.0.0-rc.1", + "@graphiql/plugin-explorer": "^5.0.0-rc.3", + "@graphiql/react": "^0.35.0-rc.6", "@graphiql/toolkit": "^0.11.3", - "graphiql": "^4.1.2", + "graphiql": "^5.0.0-rc.4", "graphql": "^16.9.0", "graphql-ws": "^5.5.5", "react": "^19.1.0", + "react-dom": "^19.1.0", "regenerator-runtime": "^0.13.9" }, "devDependencies": { @@ -29,8 +30,8 @@ "copy-webpack-plugin": "11.0.0", "cross-env": "^7.0.2", "css-loader": "^6.7.3", + "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.0", - "react-dom": "^19.1.0", "react-hot-loader": "^4.13.1", "style-loader": "^3.3.1", "webpack": "5.94.0", diff --git a/examples/graphiql-webpack/src/index.jsx b/examples/graphiql-webpack/src/index.jsx index 3c40813f3b2..d7c8c00a03d 100644 --- a/examples/graphiql-webpack/src/index.jsx +++ b/examples/graphiql-webpack/src/index.jsx @@ -8,6 +8,7 @@ import { codeExporterPlugin } from '@graphiql/plugin-code-exporter'; import { createGraphiQLFetcher } from '@graphiql/toolkit'; import { useStorage } from '@graphiql/react'; import { serverSelectPlugin, LAST_URL_KEY } from './select-server-plugin'; +import 'graphiql/setup-workers/webpack'; import './index.css'; export const STARTING_URL = 'https://countries.trevorblades.com'; diff --git a/examples/monaco-graphql-nextjs/.gitignore b/examples/monaco-graphql-nextjs/.gitignore deleted file mode 100644 index 536d88c8a6d..00000000000 --- a/examples/monaco-graphql-nextjs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.next/ diff --git a/examples/monaco-graphql-nextjs/README.md b/examples/monaco-graphql-nextjs/README.md index d5901e71bac..9fecd96bd88 100644 --- a/examples/monaco-graphql-nextjs/README.md +++ b/examples/monaco-graphql-nextjs/README.md @@ -3,7 +3,7 @@ ## Getting Started This is a working example of `monaco-editor` and `monaco-graphql` using -`next.js` 13 +Next.js 15 with Turbopack. It shows how to use the latest monaco-editor with next.js and a custom webworker, without using `@monaco/react` or `monaco-editor-react`'s approach of @@ -11,7 +11,7 @@ cdn (AMD) bundles. These approaches avoid using ESM `monaco-editor` or web workers, which prevents introducing custom web workers like with `monaco-graphql`. -For universal loading, we use `@next/loadable` with `{ssr: false}`, but any +For lazy loading, we use `next/dynamic` with `{ssr: false}`, but any similar client-side-only loading (with or without dynamic import) should be fine. For more information on loading `monaco-editor` in esm contexts, you can [read their docs](https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-esm.md) diff --git a/examples/monaco-graphql-nextjs/next-env.d.ts b/examples/monaco-graphql-nextjs/next-env.d.ts index 4f11a03dc6c..1b3be0840f3 100644 --- a/examples/monaco-graphql-nextjs/next-env.d.ts +++ b/examples/monaco-graphql-nextjs/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/examples/monaco-graphql-nextjs/next.config.js b/examples/monaco-graphql-nextjs/next.config.js deleted file mode 100644 index 3aba5c28211..00000000000 --- a/examples/monaco-graphql-nextjs/next.config.js +++ /dev/null @@ -1,64 +0,0 @@ -import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; -import { patchWebpackConfig } from 'next-global-css'; - -/** @type {import('next').NextConfig} */ -const nextConfig = { - eslint: { - ignoreDuringBuilds: true, - }, - trailingSlash: true, - webpack(config, options) { - // this fixes some issues with loading web workers - config.output.publicPath = '/_next/'; - // because next.js doesn't like node_modules that import css files - // this solves the issue for monaco-editor, which relies on importing css files - patchWebpackConfig(config, options); - config.resolve.alias = { - ...config.resolve.alias, - // this solves a bug with more recent `monaco-editor` versions in next.js, - // where vscode contains a version of `marked` with modules pre-transpiled, which seems to break the build. - // - // (the error mentions that exports.Lexer is a const that can't be re-declared) - // this import has moved a bit, so let's make it absolute from the module path - 'monaco-editor/esm/vs/common/marked/marked.js': 'marked', - }; - if (!options.isServer) { - config.plugins.push( - // if you find yourself needing to override - // MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker, - // you probably just need to tweak configuration here. - new MonacoWebpackPlugin({ - // you can add other languages here as needed - languages: ['json', 'graphql'], - filename: 'static/[name].worker.js', - // this is not in the plugin readme, but saves us having to override - // MonacoEnvironment.getWorkerUrl or similar. - customLanguages: [ - { - label: 'graphql', - worker: { - id: 'graphql', - entry: 'monaco-graphql/esm/graphql.worker.js', - }, - }, - // TOD: webpack monaco editor plugin breaks on languages: ['typescript'] - // so this was necessary - // see: https://github.com/microsoft/monaco-editor/issues/2738 - { - label: 'typescript', - worker: { - id: 'typescript', - entry: 'monaco-editor/esm/vs/language/typescript/ts.worker.js', - }, - }, - ], - }), - ); - } - // load monaco-editor provided ttf fonts - config.module.rules.push({ test: /\.ttf$/, type: 'asset/resource' }); - return config; - }, -}; - -export default nextConfig; diff --git a/examples/monaco-graphql-nextjs/next.config.ts b/examples/monaco-graphql-nextjs/next.config.ts new file mode 100644 index 00000000000..b22af960af4 --- /dev/null +++ b/examples/monaco-graphql-nextjs/next.config.ts @@ -0,0 +1,5 @@ +import type { NextConfig } from 'next'; + +const nextConfig: NextConfig = {}; + +export default nextConfig; diff --git a/examples/monaco-graphql-nextjs/package.json b/examples/monaco-graphql-nextjs/package.json index 11cc0a8323e..952704fde55 100644 --- a/examples/monaco-graphql-nextjs/package.json +++ b/examples/monaco-graphql-nextjs/package.json @@ -4,6 +4,7 @@ "private": true, "type": "module", "scripts": { + "types:check": "tsc --noEmit", "dev": "next", "build": "next build", "start": "next start" @@ -11,21 +12,16 @@ "dependencies": { "@graphiql/toolkit": "^0.11.3", "graphql": "^16.9.0", - "graphql-ws": "^5.5.5", "jsonc-parser": "^3.2.0", - "marked": "^4.2.12", - "monaco-editor": "^0.39.0", - "monaco-editor-webpack-plugin": "^7.0.1", - "monaco-graphql": "^1.7.0", - "next": "13.4.7", - "prettier": "3.3.2", + "monaco-editor": "^0.52.2", + "monaco-graphql": "^1.7.1-rc.0", + "next": "15.4.0", "react": "^19.1.0", "react-dom": "^19.1.0" }, "devDependencies": { "@types/node": "^16.18.4", "@types/react": "^19.1.2", - "next-global-css": "1.3.1", "typescript": "^4.6.3" } } diff --git a/examples/monaco-graphql-nextjs/src/app/env.d.ts b/examples/monaco-graphql-nextjs/src/app/env.d.ts new file mode 100644 index 00000000000..69e7472cd3b --- /dev/null +++ b/examples/monaco-graphql-nextjs/src/app/env.d.ts @@ -0,0 +1,4 @@ +declare namespace globalThis { + import type { Environment } from 'monaco-editor/esm/monaco-editor'; + var MonacoEnvironment: Environment; +} diff --git a/examples/monaco-graphql-nextjs/src/app/favicon.ico b/examples/monaco-graphql-nextjs/src/app/favicon.ico new file mode 100644 index 00000000000..718d6fea483 Binary files /dev/null and b/examples/monaco-graphql-nextjs/src/app/favicon.ico differ diff --git a/examples/monaco-graphql-nextjs/src/style.css b/examples/monaco-graphql-nextjs/src/app/globals.css similarity index 93% rename from examples/monaco-graphql-nextjs/src/style.css rename to examples/monaco-graphql-nextjs/src/app/globals.css index 12b19fc2642..e318a0c25f1 100644 --- a/examples/monaco-graphql-nextjs/src/style.css +++ b/examples/monaco-graphql-nextjs/src/app/globals.css @@ -3,7 +3,7 @@ body { height: 100vh; } -#__next { +#root { display: flex; height: inherit; } diff --git a/examples/monaco-graphql-nextjs/src/app/layout.tsx b/examples/monaco-graphql-nextjs/src/app/layout.tsx new file mode 100644 index 00000000000..e65922233fe --- /dev/null +++ b/examples/monaco-graphql-nextjs/src/app/layout.tsx @@ -0,0 +1,17 @@ +import type { FC, ReactNode } from 'react'; +import type { Metadata } from 'next'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'Monaco Next.js Example', +}; + +const RootLayout: FC> = ({ children }) => { + return ( + + {children} + + ); +}; + +export default RootLayout; diff --git a/examples/monaco-graphql-nextjs/src/app/page.tsx b/examples/monaco-graphql-nextjs/src/app/page.tsx new file mode 100644 index 00000000000..54dbdec37d5 --- /dev/null +++ b/examples/monaco-graphql-nextjs/src/app/page.tsx @@ -0,0 +1,49 @@ +'use client'; + +import type { FC } from 'react'; +import dynamic from 'next/dynamic'; + +// dynamically import our GraphiQL component +const DynamicEditor = dynamic(() => import('../editor'), { ssr: false }); + +/** + * Setup Monaco Editor workers for Webpack/Turbopack projects like Next.js. + */ +globalThis.MonacoEnvironment = { + getWorker(_workerId: string, label: string) { + console.info('setup-workers/webpack', { label }); + switch (label) { + case 'json': + return new Worker( + new URL( + 'monaco-editor/esm/vs/language/json/json.worker.js', + import.meta.url, + ), + ); + case 'graphql': + return new Worker( + new URL('monaco-graphql/esm/graphql.worker.js', import.meta.url), + ); + case 'typescript': + return new Worker( + new URL( + 'monaco-editor/esm/vs/language/typescript/ts.worker.js', + import.meta.url, + ), + ); + } + return new Worker( + new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), + ); + }, +}; + +const Page: FC = () => { + return ( +
+ +
+ ); +}; + +export default Page; diff --git a/examples/monaco-graphql-nextjs/src/constants.ts b/examples/monaco-graphql-nextjs/src/constants.ts index 70639a6358c..e93d4f43171 100644 --- a/examples/monaco-graphql-nextjs/src/constants.ts +++ b/examples/monaco-graphql-nextjs/src/constants.ts @@ -1,8 +1,8 @@ -import { editor, Uri } from 'monaco-graphql/esm/monaco-editor'; +import { editor, Uri, languages } from 'monaco-graphql/esm/monaco-editor'; import { initializeMode } from 'monaco-graphql/esm/initializeMode'; import { parse, print } from 'graphql'; -type ModelType = 'operations' | 'variables' | 'response' | 'typescript'; +type ModelType = 'operations' | 'variables' | 'response' | 'ts'; export const GRAPHQL_URL = 'https://countries.trevorblades.com'; @@ -41,13 +41,12 @@ query Example($code: ID!, $filter: LanguageFilterInput!) { /* cSpell:enable */ let prettyOp = ''; -const makeOpTemplate = (op: string) => { +export const makeOpTemplate = (op: string) => { try { prettyOp = print(parse(op)); - return ` - const graphql = (arg: TemplateStringsArray): string => arg[0] + return `const graphql = (arg: TemplateStringsArray): string => arg[0] - const op = graphql\`\n${prettyOp}\n\``; +const op = graphql\`\n${prettyOp}\n\``; } catch { return prettyOp; } @@ -61,25 +60,24 @@ export const DEFAULT_VALUE: Record = { "code": "UA" }`, response: '', - typescript: makeOpTemplate(operations), + ts: makeOpTemplate(operations), }; -export const FILE_SYSTEM_PATH: Record< - ModelType, - `${string}.${'graphql' | 'json' | 'ts'}` -> = { - operations: 'operations.graphql', - variables: 'variables.json', - response: 'response.json', - typescript: 'typescript.ts', -}; +export const OPERATIONS_URI = Uri.file('operations.graphql'); +export const VARIABLES_URI = Uri.file('variables.json'); +export const RESPONSE_URI = Uri.file('response.json'); +export const TS_URI = Uri.file('typescript.ts'); + +// set these early on so that initial variables with comments don't flash an error +languages.json.jsonDefaults.setDiagnosticsOptions({ + allowComments: true, + trailingCommas: 'ignore', +}); export const MONACO_GRAPHQL_API = initializeMode({ diagnosticSettings: { validateVariablesJSON: { - [Uri.file(FILE_SYSTEM_PATH.operations).toString()]: [ - Uri.file(FILE_SYSTEM_PATH.variables).toString(), - ], + [OPERATIONS_URI.toString()]: [VARIABLES_URI.toString()], }, jsonDiagnosticSettings: { validate: true, @@ -91,27 +89,11 @@ export const MONACO_GRAPHQL_API = initializeMode({ }, }); -export const MODEL: Record = { - operations: getOrCreateModel('operations'), - variables: getOrCreateModel('variables'), - response: getOrCreateModel('response'), - typescript: getOrCreateModel('typescript'), -}; - -MODEL.operations.onDidChangeContent(() => { - const value = MODEL.operations.getValue(); - MODEL.typescript.setValue(makeOpTemplate(value)); -}); - -function getOrCreateModel(type: ModelType): editor.ITextModel { - const uri = Uri.file(FILE_SYSTEM_PATH[type]); - const defaultValue = DEFAULT_VALUE[type]; - let language = uri.path.split('.').pop(); - console.log({ language }); +export function getOrCreateModel({ uri, value }: { uri: Uri; value: string }) { + const { path } = uri; + let language = path.split('.').at(-1)!; if (language === 'ts') { language = 'typescript'; } - return ( - editor.getModel(uri) ?? editor.createModel(defaultValue, language, uri) - ); + return editor.getModel(uri) ?? editor.createModel(value, language, uri); } diff --git a/examples/monaco-graphql-nextjs/src/editor.tsx b/examples/monaco-graphql-nextjs/src/editor.tsx index d0f88faaef2..d626a5936d2 100644 --- a/examples/monaco-graphql-nextjs/src/editor.tsx +++ b/examples/monaco-graphql-nextjs/src/editor.tsx @@ -1,11 +1,6 @@ import { ReactElement, useEffect, useRef, useState } from 'react'; import { getIntrospectionQuery, IntrospectionQuery } from 'graphql'; -import { - editor, - KeyMod, - KeyCode, - languages, -} from 'monaco-graphql/esm/monaco-editor'; +import { editor, KeyMod, KeyCode } from 'monaco-graphql/esm/monaco-editor'; // to get typescript mode working import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'; @@ -20,7 +15,13 @@ import { MONACO_GRAPHQL_API, STORAGE_KEY, GRAPHQL_URL, - MODEL, + OPERATIONS_URI, + VARIABLES_URI, + RESPONSE_URI, + TS_URI, + DEFAULT_VALUE, + makeOpTemplate, + getOrCreateModel, } from './constants'; const fetcher = createGraphiQLFetcher({ url: GRAPHQL_URL }); @@ -45,7 +46,7 @@ function debounce any>(duration: number, fn: F) { let timeout = 0; return (...args: Parameters) => { if (timeout) { - window.clearTimeout(timeout); + clearTimeout(timeout); } timeout = window.setTimeout(() => { timeout = 0; @@ -54,58 +55,81 @@ function debounce any>(duration: number, fn: F) { }; } -const queryAction: editor.IActionDescriptor = { - id: 'graphql-run', - label: 'Run Operation', - contextMenuOrder: 0, - contextMenuGroupId: 'graphql', - keybindings: [ - // eslint-disable-next-line no-bitwise - KeyMod.CtrlCmd | KeyCode.Enter, - ], - async run() { - const result = await fetcher({ - query: MODEL.operations.getValue(), - variables: JSONC.parse(MODEL.variables.getValue()), - }); - // TODO: this demo only supports a single iteration for http GET/POST, - // no multipart or subscriptions yet. - // @ts-expect-error - const data = await result.next(); - MODEL.response.setValue(JSON.stringify(data.value, null, 2)); - }, -}; -// set these early on so that initial variables with comments don't flash an error -languages.json.jsonDefaults.setDiagnosticsOptions({ - allowComments: true, - trailingCommas: 'ignore', -}); - export default function Editor(): ReactElement { - const operationsRef = useRef(null); - const variablesRef = useRef(null); - const responseRef = useRef(null); - const typescriptRef = useRef(null); - const [operationsEditor, setOperationsEditor] = - useState(); - const [variablesEditor, setVariablesEditor] = - useState(); - const [responseEditor, setResponseEditor] = - useState(); - const [typescriptEditor, setTypescriptEditor] = - useState(); + const operationsRef = useRef(null!); + const variablesRef = useRef(null!); + const responseRef = useRef(null!); + const tsRef = useRef(null!); + const [schema, setSchema] = useState(); const [loading, setLoading] = useState(false); /** * Create the models & editors */ useEffect(() => { - if (!operationsEditor) { - const codeEditor = editor.create(operationsRef.current!, { + const MODEL = { + operations: getOrCreateModel({ + uri: OPERATIONS_URI, + value: DEFAULT_VALUE.operations, + }), + variables: getOrCreateModel({ + uri: VARIABLES_URI, + value: DEFAULT_VALUE.variables, + }), + response: getOrCreateModel({ + uri: RESPONSE_URI, + value: DEFAULT_VALUE.response, + }), + ts: getOrCreateModel({ + uri: TS_URI, + value: DEFAULT_VALUE.ts, + }), + }; + const EDITOR = { + operations: editor.create(operationsRef.current, { model: MODEL.operations, ...DEFAULT_EDITOR_OPTIONS, - }); - codeEditor.addAction(queryAction); + }), + variables: editor.create(variablesRef.current, { + model: MODEL.variables, + ...DEFAULT_EDITOR_OPTIONS, + }), + response: editor.create(responseRef.current, { + model: MODEL.response, + ...DEFAULT_EDITOR_OPTIONS, + readOnly: true, + smoothScrolling: true, + }), + ts: editor.create(tsRef.current, { + model: MODEL.ts, + ...DEFAULT_EDITOR_OPTIONS, + smoothScrolling: true, + 'semanticHighlighting.enabled': true, + language: 'typescript', + }), + }; + const queryAction: editor.IActionDescriptor = { + id: 'graphql-run', + label: 'Run Operation', + contextMenuOrder: 0, + contextMenuGroupId: 'graphql', + // eslint-disable-next-line no-bitwise + keybindings: [KeyMod.CtrlCmd | KeyCode.Enter], + async run() { + const result = await fetcher({ + query: MODEL.operations.getValue(), + variables: JSONC.parse(MODEL.variables.getValue()), + }); + // TODO: this demo only supports a single iteration for http GET/POST, + // no multipart or subscriptions yet. + // @ts-expect-error + const data = await result.next(); + MODEL.response.setValue(JSON.stringify(data.value, null, 2)); + }, + }; + + const disposables = [ + EDITOR.operations.addAction(queryAction), MODEL.operations.onDidChangeContent( debounce(300, () => { localStorage.setItem( @@ -113,15 +137,12 @@ export default function Editor(): ReactElement { MODEL.operations.getValue(), ); }), - ); - setOperationsEditor(codeEditor); - } - if (!variablesEditor) { - const codeEditor = editor.create(variablesRef.current!, { - model: MODEL.variables, - ...DEFAULT_EDITOR_OPTIONS, - }); - codeEditor.addAction(queryAction); + ), + MODEL.operations.onDidChangeContent(() => { + const value = MODEL.operations.getValue(); + MODEL.ts.setValue(makeOpTemplate(value)); + }), + EDITOR.variables.addAction(queryAction), MODEL.variables.onDidChangeContent( debounce(300, () => { localStorage.setItem( @@ -129,32 +150,17 @@ export default function Editor(): ReactElement { MODEL.variables.getValue(), ); }), - ); - setVariablesEditor(codeEditor); - } - if (!responseEditor) { - setResponseEditor( - editor.create(responseRef.current!, { - model: MODEL.response, - ...DEFAULT_EDITOR_OPTIONS, - readOnly: true, - smoothScrolling: true, - }), - ); - } - if (!typescriptEditor) { - setTypescriptEditor( - editor.create(typescriptRef.current!, { - model: MODEL.typescript, - ...DEFAULT_EDITOR_OPTIONS, - smoothScrolling: true, - readOnly: false, - 'semanticHighlighting.enabled': true, - language: 'typescript', - }), - ); - } - }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only run once on mount + ), + ...Object.values(EDITOR), + ...Object.values(MODEL), + ]; + // Clean‑up on unmount + return () => { + for (const disposable of disposables) { + disposable.dispose(); // remove the listener + } + }; + }, []); /** * Handle the initial schema load */ @@ -180,7 +186,7 @@ export default function Editor(): ReactElement {
-
+
); diff --git a/examples/monaco-graphql-nextjs/src/pages/_app.tsx b/examples/monaco-graphql-nextjs/src/pages/_app.tsx deleted file mode 100644 index 70321d1ef2f..00000000000 --- a/examples/monaco-graphql-nextjs/src/pages/_app.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import type { AppProps } from 'next/app'; -import '../style.css'; - -export default function App({ Component, pageProps }: AppProps) { - return ; -} diff --git a/examples/monaco-graphql-nextjs/src/pages/index.tsx b/examples/monaco-graphql-nextjs/src/pages/index.tsx deleted file mode 100644 index f6541d67d6a..00000000000 --- a/examples/monaco-graphql-nextjs/src/pages/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import Head from 'next/head'; -import dynamic from 'next/dynamic'; - -const DynamicEditor = dynamic(() => import('../editor'), { ssr: false }); - -export default function Home() { - return ( - <> - - Monaco Next.js Example - - - - - - - ); -} diff --git a/examples/monaco-graphql-nextjs/tsconfig.json b/examples/monaco-graphql-nextjs/tsconfig.json index 2159bf45c40..03c3a9c0c87 100644 --- a/examples/monaco-graphql-nextjs/tsconfig.json +++ b/examples/monaco-graphql-nextjs/tsconfig.json @@ -17,8 +17,13 @@ "baseUrl": ".", "paths": { "@/*": ["./src/*"] - } + }, + "plugins": [ + { + "name": "next" + } + ] }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts"], "exclude": ["node_modules"] } diff --git a/examples/monaco-graphql-react-vite/index.html b/examples/monaco-graphql-react-vite/index.html index 8e69c0f6159..ef1a28f59b6 100644 --- a/examples/monaco-graphql-react-vite/index.html +++ b/examples/monaco-graphql-react-vite/index.html @@ -17,7 +17,7 @@ -
+
Loading…
diff --git a/examples/monaco-graphql-react-vite/package.json b/examples/monaco-graphql-react-vite/package.json index 2fac914cb05..b6a583cbf0f 100644 --- a/examples/monaco-graphql-react-vite/package.json +++ b/examples/monaco-graphql-react-vite/package.json @@ -8,18 +8,17 @@ "graphql-language-service": "^5.4.0", "jsonc-parser": "^3.2.0", "monaco-editor": "^0.52.2", - "monaco-graphql": "^1.7.0", - "prettier": "3.3.2", + "monaco-graphql": "^1.7.1-rc.0", "react": "^19.1.0", "react-dom": "^19.1.0" }, "devDependencies": { "@vitejs/plugin-react": "^4.4.1", - "vite": "^6.3.4", - "vite-plugin-monaco-editor": "^1.1.0" + "vite": "^6.3.4" }, "scripts": { "dev": "vite", - "build": "vite build" + "build": "vite build", + "start": "vite preview" } } diff --git a/examples/monaco-graphql-react-vite/src/env.d.ts b/examples/monaco-graphql-react-vite/src/env.d.ts new file mode 100644 index 00000000000..69e7472cd3b --- /dev/null +++ b/examples/monaco-graphql-react-vite/src/env.d.ts @@ -0,0 +1,4 @@ +declare namespace globalThis { + import type { Environment } from 'monaco-editor/esm/monaco-editor'; + var MonacoEnvironment: Environment; +} diff --git a/examples/monaco-graphql-react-vite/src/globals.css b/examples/monaco-graphql-react-vite/src/globals.css new file mode 120000 index 00000000000..1eaa2083163 --- /dev/null +++ b/examples/monaco-graphql-react-vite/src/globals.css @@ -0,0 +1 @@ +../../monaco-graphql-nextjs/src/app/globals.css \ No newline at end of file diff --git a/examples/monaco-graphql-react-vite/src/index.tsx b/examples/monaco-graphql-react-vite/src/index.tsx index 83f9e302e91..46b183ab3e6 100644 --- a/examples/monaco-graphql-react-vite/src/index.tsx +++ b/examples/monaco-graphql-react-vite/src/index.tsx @@ -1,5 +1,35 @@ +/* eslint-disable import-x/default */ import { createRoot } from 'react-dom/client'; +import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker.js?worker'; +import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker'; +import TSWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker.js?worker'; +import GraphQLWorker from 'monaco-graphql/esm/graphql.worker.js?worker'; import Editor from './editor'; +import './globals.css'; -const root = createRoot(document.getElementById('__next')!); +/** + * Setup Monaco Editor workers for Vite. + * + * Vite doesn’t support instantiating web workers directly from bare module imports like this: + * ``` + * new Worker(new URL('monaco-editor/esm/vs/language/json/json.worker.js', import.meta.url)) + * ``` + * Vite needs to know ahead of time that you are loading a web worker. + */ +globalThis.MonacoEnvironment = { + getWorker(_workerId: string, label: string) { + console.info('setup-workers/vite', { label }); + switch (label) { + case 'json': + return new JsonWorker(); + case 'graphql': + return new GraphQLWorker(); + case 'typescript': + return new TSWorker(); + } + return new EditorWorker(); + }, +}; + +const root = createRoot(document.getElementById('root')!); root.render(); diff --git a/examples/monaco-graphql-react-vite/src/style.css b/examples/monaco-graphql-react-vite/src/style.css deleted file mode 120000 index 32a7938addd..00000000000 --- a/examples/monaco-graphql-react-vite/src/style.css +++ /dev/null @@ -1 +0,0 @@ -../../monaco-graphql-nextjs/src/style.css \ No newline at end of file diff --git a/examples/monaco-graphql-react-vite/vite.config.ts b/examples/monaco-graphql-react-vite/vite.config.ts index 263927a7cfd..118a23dd471 100644 --- a/examples/monaco-graphql-react-vite/vite.config.ts +++ b/examples/monaco-graphql-react-vite/vite.config.ts @@ -1,26 +1,30 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -import monacoEditorPlugin from 'vite-plugin-monaco-editor'; export default defineConfig({ build: { minify: false, + rollupOptions: { + output: { + entryFileNames: '[name].js', + chunkFileNames: 'assets/[name].js', + assetFileNames: 'assets/[name].[ext]', + }, + }, }, plugins: [ react(), - monacoEditorPlugin({ - publicPath: 'workers', - // note that this only loads the worker, not the full main process language support - languageWorkers: ['json', 'typescript', 'editorWorkerService'], - customWorkers: [ - { - label: 'graphql', - entry: 'monaco-graphql/esm/graphql.worker', - }, - ], - }), watchPackages(['monaco-graphql', 'graphql-language-service']), ], + worker: { + format: 'es', + rollupOptions: { + output: { + entryFileNames: 'workers/[name].js', + chunkFileNames: 'workers/[name].js', + }, + }, + }, }); function watchPackages(packageNames: string[]) { diff --git a/examples/monaco-graphql-webpack/package.json b/examples/monaco-graphql-webpack/package.json index 482938d9fc0..bbf283fd7c6 100644 --- a/examples/monaco-graphql-webpack/package.json +++ b/examples/monaco-graphql-webpack/package.json @@ -14,8 +14,8 @@ "graphql-language-service": "^5.4.0", "json-schema": "^0.4.0", "jsonc-parser": "^3.2.0", - "monaco-editor": "^0.39.0", - "monaco-graphql": "^1.7.0", + "monaco-editor": "^0.52.2", + "monaco-graphql": "^1.7.1-rc.0", "prettier": "3.3.2" }, "devDependencies": { diff --git a/package.json b/package.json index 688cdd8cf11..cca4802b942 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "examples/monaco-graphql-webpack", "examples/monaco-graphql-nextjs", "examples/monaco-graphql-react-vite", + "examples/graphiql-vite", "examples/graphiql-webpack" ] }, @@ -18,6 +19,8 @@ "scripts": { "types:check": "turbo run types:check", "dev:graphiql": "turbo run dev --filter=graphiql...", + "dev:example-nextjs": "turbo run dev --filter=example-graphiql-nextjs...", + "dev:example-vite": "turbo run dev --filter=example-graphiql-vite...", "build:graphiql": "turbo run build --filter=graphiql...", "build": "yarn build-clean && yarn tsc && yarn build:nontsc", "build-bundles": "yarn prebuild-bundles && yarn wsrun:noexamples --stages build-bundles", @@ -32,7 +35,7 @@ "watch-vscode": "yarn tsc && yarn workspace vscode-graphql compile", "watch-vscode-exec": "yarn workspace vscode-graphql-execution compile", "check": "yarn tsc --noEmit", - "cypress-open": "yarn workspace graphiql cypress-open", + "cypress-open": "concurrently 'yarn dev:graphiql' 'yarn workspace graphiql cypress-open'", "e2e": "yarn workspace graphiql e2e", "eslint": "NODE_OPTIONS=--max-old-space-size=4096 ESLINT_USE_FLAT_CONFIG=false eslint --max-warnings=0 --ignore-path .gitignore --cache .", "format": "yarn eslint --fix && yarn pretty", @@ -59,7 +62,7 @@ "test:coverage": "yarn jest --coverage", "test:watch": "yarn jest --watch", "tsc": "tsc --build && node resources/patch-monaco-editor-type.mjs", - "vitest": "yarn wsrun -p -m test", + "vitest": "turbo run test", "wsrun:noexamples": "wsrun --exclude-missing --exclude example-monaco-graphql-react-vite --exclude example-monaco-graphql-nextjs --exclude example-monaco-graphql-webpack --exclude example-graphiql-webpack", "gen-agenda": "wgutils agenda gen" }, diff --git a/packages/codemirror-graphql/CHANGELOG.md b/packages/codemirror-graphql/CHANGELOG.md index 463d8d79867..2d2434162b3 100644 --- a/packages/codemirror-graphql/CHANGELOG.md +++ b/packages/codemirror-graphql/CHANGELOG.md @@ -1,5 +1,27 @@ # Change Log +## 2.2.3-rc.0 + +### Patch Changes + +- [#3949](https://github.com/graphql/graphiql/pull/3949) [`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - replace `onCopyQuery` hook with `copyQuery` function + - replace `onMergeQuery` hook with `mergeQuery` function + - replace `onPrettifyEditors` hook with `prettifyEditors` function + - remove `fetcher` prop from `SchemaContextProvider` and `schemaStore` and add `fetcher` to `executionStore` + - add `onCopyQuery` and `onPrettifyQuery` props to `EditorContextProvider` + - remove exports (use `GraphiQLProvider`) + - `EditorContextProvider` + - `ExecutionContextProvider` + - `PluginContextProvider` + - `SchemaContextProvider` + - `StorageContextProvider` + - `ExecutionContextType` + - `PluginContextType` + - feat(@graphiql/react): migrate React context to zustand: + - replace `useExecutionContext` with `useExecutionStore` hook + - replace `useEditorContext` with `useEditorStore` hook + - prefer `getComputedStyle` over `window.getComputedStyle` + ## 2.2.2 ### Patch Changes diff --git a/packages/codemirror-graphql/package.json b/packages/codemirror-graphql/package.json index de777a2c9a6..8fcbd7d52cf 100644 --- a/packages/codemirror-graphql/package.json +++ b/packages/codemirror-graphql/package.json @@ -1,6 +1,6 @@ { "name": "codemirror-graphql", - "version": "2.2.2", + "version": "2.2.3-rc.0", "description": "GraphQL mode and helpers for CodeMirror.", "contributors": [ "Hyohyeon Jeong ", diff --git a/packages/codemirror-graphql/src/utils/info-addon.ts b/packages/codemirror-graphql/src/utils/info-addon.ts index cff916607ce..d1837d1927a 100644 --- a/packages/codemirror-graphql/src/utils/info-addon.ts +++ b/packages/codemirror-graphql/src/utils/info-addon.ts @@ -119,17 +119,19 @@ function showPopup(cm: CodeMirror.Editor, box: DOMRect, info: HTMLDivElement) { document.body.append(popup); const popupBox = popup.getBoundingClientRect(); - const popupStyle = window.getComputedStyle(popup); + const { marginLeft, marginRight, marginBottom, marginTop } = + getComputedStyle(popup); + const popupWidth = popupBox.right - popupBox.left + - parseFloat(popupStyle.marginLeft) + - parseFloat(popupStyle.marginRight); + parseFloat(marginLeft) + + parseFloat(marginRight); const popupHeight = popupBox.bottom - popupBox.top + - parseFloat(popupStyle.marginTop) + - parseFloat(popupStyle.marginBottom); + parseFloat(marginTop) + + parseFloat(marginBottom); let topPos = box.bottom; if ( diff --git a/packages/graphiql-plugin-code-exporter/CHANGELOG.md b/packages/graphiql-plugin-code-exporter/CHANGELOG.md index 8913c66ba6a..d64af0b97a0 100644 --- a/packages/graphiql-plugin-code-exporter/CHANGELOG.md +++ b/packages/graphiql-plugin-code-exporter/CHANGELOG.md @@ -1,5 +1,24 @@ # @graphiql/plugin-code-exporter +## 5.0.0-rc.1 + +### Major Changes + +- [#4002](https://github.com/graphql/graphiql/pull/4002) [`2d9faec`](https://github.com/graphql/graphiql/commit/2d9faec57830b38aa175929c47a55c959c327535) Thanks [@dimaMachina](https://github.com/dimaMachina)! - remove UMD builds + +## 4.0.6-rc.0 + +### Patch Changes + +- [#3234](https://github.com/graphql/graphiql/pull/3234) [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Migration from Codemirror to [Monaco Editor](https://github.com/microsoft/monaco-editor) + + Replacing `codemirror-graphql` with [`monaco-graphql`](https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql) + + Support for comments in **Variables** and **Headers** editors + +- Updated dependencies [[`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e), [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91), [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb)]: + - @graphiql/react@0.35.0-rc.0 + ## 4.0.5 ### Patch Changes diff --git a/packages/graphiql-plugin-code-exporter/example/index.html b/packages/graphiql-plugin-code-exporter/example/index.html index f4ce66c2100..4ee59cead79 100644 --- a/packages/graphiql-plugin-code-exporter/example/index.html +++ b/packages/graphiql-plugin-code-exporter/example/index.html @@ -1,5 +1,5 @@ @@ -62,12 +61,29 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; // Import GraphiQL and the Explorer plugin - import { GraphiQL } from 'graphiql'; + import { GraphiQL, HISTORY_PLUGIN } from 'graphiql'; import { createGraphiQLFetcher } from '@graphiql/toolkit'; // Required to be before `@graphiql/plugin-code-exporter` import 'regenerator-runtime/runtime'; import { codeExporterPlugin } from '@graphiql/plugin-code-exporter'; + import createJSONWorker from 'https://esm.sh/monaco-editor/esm/vs/language/json/json.worker.js?worker'; + import createGraphQLWorker from 'https://esm.sh/monaco-graphql/esm/graphql.worker.js?worker'; + import createEditorWorker from 'https://esm.sh/monaco-editor/esm/vs/editor/editor.worker.js?worker'; + + globalThis.MonacoEnvironment = { + getWorker(_workerId, label) { + console.info('MonacoEnvironment.getWorker', { label }); + switch (label) { + case 'json': + return createJSONWorker(); + case 'graphql': + return createGraphQLWorker(); + } + return createEditorWorker(); + }, + }; + const fetcher = createGraphiQLFetcher({ url: 'https://countries.trevorblades.com', }); @@ -106,10 +122,14 @@ }, ], }); + + const plugins = [HISTORY_PLUGIN, codeExporter]; + function App() { return React.createElement(GraphiQL, { fetcher, - plugins: [codeExporter], + plugins, + defaultEditorToolsVisibility: true, }); } diff --git a/packages/graphiql-plugin-code-exporter/package.json b/packages/graphiql-plugin-code-exporter/package.json index 5ef1825444f..b19a526582f 100644 --- a/packages/graphiql-plugin-code-exporter/package.json +++ b/packages/graphiql-plugin-code-exporter/package.json @@ -1,6 +1,6 @@ { "name": "@graphiql/plugin-code-exporter", - "version": "4.0.5", + "version": "5.0.0-rc.1", "sideEffects": false, "repository": { "type": "git", @@ -28,21 +28,21 @@ }, "scripts": { "types:check": "tsc --noEmit", - "dev": "vite build --watch", - "build": "vite build && UMD=true vite build", + "dev": "vite build --watch --emptyOutDir=false", + "build": "vite build", "postbuild": "cp src/graphiql-code-exporter.d.ts dist/graphiql-code-exporter.d.ts" }, "dependencies": { "graphiql-code-exporter": "^3.0.3" }, "peerDependencies": { - "@graphiql/react": "^0.34.0", + "@graphiql/react": "^0.35.0-rc.0", "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2", "react": "^18 || ^19", "react-dom": "^18 || ^19" }, "devDependencies": { - "@graphiql/react": "^0.34.0", + "@graphiql/react": "^0.35.0-rc.2", "@vitejs/plugin-react": "^4.4.1", "graphql": "^16.9.0", "react": "^19.1.0", diff --git a/packages/graphiql-plugin-code-exporter/src/index.tsx b/packages/graphiql-plugin-code-exporter/src/index.tsx index 6fd177d5126..74df1324f2c 100644 --- a/packages/graphiql-plugin-code-exporter/src/index.tsx +++ b/packages/graphiql-plugin-code-exporter/src/index.tsx @@ -1,10 +1,8 @@ import { useOperationsEditorState, type GraphiQLPlugin } from '@graphiql/react'; -import React, { FC } from 'react'; +import { FC } from 'react'; import GraphiQLCodeExporter, { GraphiQLCodeExporterProps, } from 'graphiql-code-exporter'; - -import './graphiql-code-exporter.d.ts'; import './index.css'; type GraphiQLCodeExporterPluginProps = Omit; diff --git a/packages/graphiql-plugin-code-exporter/src/vite-env.d.ts b/packages/graphiql-plugin-code-exporter/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2a00..00000000000 --- a/packages/graphiql-plugin-code-exporter/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/packages/graphiql-plugin-code-exporter/tsconfig.json b/packages/graphiql-plugin-code-exporter/tsconfig.json index 2dd9b41294b..ee54eec68ce 100644 --- a/packages/graphiql-plugin-code-exporter/tsconfig.json +++ b/packages/graphiql-plugin-code-exporter/tsconfig.json @@ -1,19 +1,3 @@ { - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "declaration": true, - "jsx": "react" - } + "extends": "../graphiql-react/tsconfig.json" } diff --git a/packages/graphiql-plugin-code-exporter/vite.config.mts b/packages/graphiql-plugin-code-exporter/vite.config.mts index 627e266b78f..bffeeb91ee1 100644 --- a/packages/graphiql-plugin-code-exporter/vite.config.mts +++ b/packages/graphiql-plugin-code-exporter/vite.config.mts @@ -3,44 +3,28 @@ import react from '@vitejs/plugin-react'; import packageJSON from './package.json'; import dts from 'vite-plugin-dts'; -const IS_UMD = process.env.UMD === 'true'; - export default defineConfig({ - plugins: [ - react({ jsxRuntime: 'classic' }), - !IS_UMD && dts({ include: ['src/**'] }), - ], + plugins: [react(), dts({ include: ['src/**'] })], css: { transformer: 'lightningcss', }, build: { - minify: IS_UMD - ? 'terser' // produce better bundle size than esbuild - : false, - // avoid clean cjs/es builds - emptyOutDir: !IS_UMD, + minify: false, lib: { entry: 'src/index.tsx', - fileName: (format, filePath) => - `${filePath}.${format === 'umd' ? 'umd.' : ''}js`, - name: 'GraphiQLPluginCodeExporter', - formats: IS_UMD ? ['umd'] : ['es'], + fileName: (_format, filePath) => `${filePath}.js`, + formats: ['es'], cssFileName: 'style', }, rollupOptions: { external: [ + 'react/jsx-runtime', // Exclude peer dependencies and dependencies from bundle - ...Object.keys(packageJSON.peerDependencies), - ...(IS_UMD ? [] : Object.keys(packageJSON.dependencies)), + ...Object.keys({ + ...packageJSON.peerDependencies, + ...packageJSON.dependencies, + }), ], - output: { - globals: { - '@graphiql/react': 'GraphiQL.React', - graphql: 'GraphiQL.GraphQL', - react: 'React', - 'react-dom': 'ReactDOM', - }, - }, }, }, }); diff --git a/packages/graphiql-plugin-doc-explorer/CHANGELOG.md b/packages/graphiql-plugin-doc-explorer/CHANGELOG.md index 0cde83d54b5..c1943d9f997 100644 --- a/packages/graphiql-plugin-doc-explorer/CHANGELOG.md +++ b/packages/graphiql-plugin-doc-explorer/CHANGELOG.md @@ -1,5 +1,87 @@ # @graphiql/plugin-doc-explorer +## 0.3.0-rc.3 + +### Minor Changes + +- [#4009](https://github.com/graphql/graphiql/pull/4009) [`4936492`](https://github.com/graphql/graphiql/commit/49364924d0da05a86f7c6c3139d44aed0e474531) Thanks [@dimaMachina](https://github.com/dimaMachina)! - separate store actions from state, add `useGraphiQLActions` state + +### Patch Changes + +- Updated dependencies [[`4936492`](https://github.com/graphql/graphiql/commit/49364924d0da05a86f7c6c3139d44aed0e474531)]: + - @graphiql/react@0.35.0-rc.3 + +## 0.3.0-rc.2 + +### Patch Changes + +- [#4006](https://github.com/graphql/graphiql/pull/4006) [`7792dc9`](https://github.com/graphql/graphiql/commit/7792dc98814abcd6dc5f5cd94ae84c308a260dcf) Thanks [@dimaMachina](https://github.com/dimaMachina)! - push field type on stack too before field + +- [#4007](https://github.com/graphql/graphiql/pull/4007) [`f9780bd`](https://github.com/graphql/graphiql/commit/f9780bd44f67acad0a9bb10f57eb6059db60e1ec) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Use an additional `Alt` key for focus doc explorer search input instead of `Cmd/Ctrl+K` because monaco-editor has a built-in shortcut for `Cmd/Ctrl+K` + +- [#4004](https://github.com/graphql/graphiql/pull/4004) [`16fdd6a`](https://github.com/graphql/graphiql/commit/16fdd6a16684c9f250ee53ea2dfbb24435cee6a9) Thanks [@dimaMachina](https://github.com/dimaMachina)! - show spinner in doc explorer based on `isIntrospecting` value, and not based on `isFetching` + +- Updated dependencies [[`866a8f3`](https://github.com/graphql/graphiql/commit/866a8f39a27d213315ccc55ec06353bb3280b270), [`1e3ec84`](https://github.com/graphql/graphiql/commit/1e3ec8455706e62e6cae306df58d3343ec6b612d), [`0c8e390`](https://github.com/graphql/graphiql/commit/0c8e3906cf58055f898cb173b2e912a494ae8439)]: + - @graphiql/react@0.35.0-rc.2 + +## 0.3.0-rc.1 + +### Minor Changes + +- [#3990](https://github.com/graphql/graphiql/pull/3990) [`27e7eb6`](https://github.com/graphql/graphiql/commit/27e7eb60247437d992c1fcdcc6870cb7892d4b92) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - allow multiple independent instances of GraphiQL on the same page + - store `onClickReference` in query editor in React `ref` + - remove `onClickReference` from variable editor + - fix shortcut text per OS for run query in execute query button's tooltip and in default query + - allow override all default GraphiQL plugins + - adjust operation argument color to be purple from GraphiQL v2 on dark/light theme + +### Patch Changes + +- Updated dependencies [[`27e7eb6`](https://github.com/graphql/graphiql/commit/27e7eb60247437d992c1fcdcc6870cb7892d4b92)]: + - @graphiql/react@0.35.0-rc.1 + +## 0.3.0-rc.0 + +### Minor Changes + +- [#3234](https://github.com/graphql/graphiql/pull/3234) [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Migration from Codemirror to [Monaco Editor](https://github.com/microsoft/monaco-editor) + + Replacing `codemirror-graphql` with [`monaco-graphql`](https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql) + + Support for comments in **Variables** and **Headers** editors + +- [#3950](https://github.com/graphql/graphiql/pull/3950) [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - remove `useQueryEditor`, `useVariableEditor`, `useHeaderEditor`, `useResponseEditor` hooks + - remove `UseHeaderEditorArgs`, `UseQueryEditorArgs`, `UseResponseEditorArgs`, `UseVariableEditorArgs` exports + - rename components + - `StorageContextProvider` => `StorageStore` + - `EditorContextProvider` => `EditorStore` + - `SchemaContextProvider` => `SchemaStore` + - `ExecutionContextProvider` => `ExecutionStore` + - `HistoryContextProvider` => `HistoryStore` + - `ExplorerContextProvider` => `ExplorerStore` + +### Patch Changes + +- [#3949](https://github.com/graphql/graphiql/pull/3949) [`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - replace `onCopyQuery` hook with `copyQuery` function + - replace `onMergeQuery` hook with `mergeQuery` function + - replace `onPrettifyEditors` hook with `prettifyEditors` function + - remove `fetcher` prop from `SchemaContextProvider` and `schemaStore` and add `fetcher` to `executionStore` + - add `onCopyQuery` and `onPrettifyQuery` props to `EditorContextProvider` + - remove exports (use `GraphiQLProvider`) + - `EditorContextProvider` + - `ExecutionContextProvider` + - `PluginContextProvider` + - `SchemaContextProvider` + - `StorageContextProvider` + - `ExecutionContextType` + - `PluginContextType` + - feat(@graphiql/react): migrate React context to zustand: + - replace `useExecutionContext` with `useExecutionStore` hook + - replace `useEditorContext` with `useEditorStore` hook + - prefer `getComputedStyle` over `window.getComputedStyle` +- Updated dependencies [[`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e), [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91), [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb)]: + - @graphiql/react@0.35.0-rc.0 + ## 0.2.2 ### Patch Changes diff --git a/packages/graphiql-plugin-doc-explorer/README.md b/packages/graphiql-plugin-doc-explorer/README.md index 7a0dfdbbba8..34d7ec909b9 100644 --- a/packages/graphiql-plugin-doc-explorer/README.md +++ b/packages/graphiql-plugin-doc-explorer/README.md @@ -1 +1,6 @@ # `@graphiql/plugin-doc-explorer` + +## API + +- `useDocExplorer`: Handles the state for the doc explorer +- `useDocExplorerActions`: Actions related to the doc explorer diff --git a/packages/graphiql-plugin-doc-explorer/__mocks__/monaco-editor.ts b/packages/graphiql-plugin-doc-explorer/__mocks__/monaco-editor.ts new file mode 120000 index 00000000000..b1964294fb0 --- /dev/null +++ b/packages/graphiql-plugin-doc-explorer/__mocks__/monaco-editor.ts @@ -0,0 +1 @@ +../../graphiql/__mocks__/monaco-editor.ts \ No newline at end of file diff --git a/packages/graphiql-plugin-doc-explorer/package.json b/packages/graphiql-plugin-doc-explorer/package.json index eb12f00fc74..9235da93b7f 100644 --- a/packages/graphiql-plugin-doc-explorer/package.json +++ b/packages/graphiql-plugin-doc-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@graphiql/plugin-doc-explorer", - "version": "0.2.2", + "version": "0.3.0-rc.3", "sideEffects": false, "repository": { "type": "git", @@ -31,7 +31,7 @@ ], "scripts": { "types:check": "tsc --noEmit", - "dev": "vite build --watch", + "dev": "vite build --watch --emptyOutDir=false", "build": "vite build", "test": "vitest" }, @@ -41,7 +41,7 @@ "react-dom": "^18 || ^19" }, "dependencies": { - "@graphiql/react": "^0.34.1", + "@graphiql/react": "^0.35.0-rc.3", "@headlessui/react": "^2.2", "react-compiler-runtime": "19.1.0-rc.1", "zustand": "^5" diff --git a/packages/graphiql-plugin-doc-explorer/setup-files.ts b/packages/graphiql-plugin-doc-explorer/setup-files.ts index 276c7c4f7a3..da20690a55f 100644 --- a/packages/graphiql-plugin-doc-explorer/setup-files.ts +++ b/packages/graphiql-plugin-doc-explorer/setup-files.ts @@ -2,4 +2,6 @@ import '@testing-library/jest-dom'; -vi.mock('zustand'); // to make it works like Jest (auto-mocking) +// to make it works like Jest (auto-mocking) +vi.mock('zustand'); +vi.mock('monaco-editor'); diff --git a/packages/graphiql-plugin-doc-explorer/src/components/__tests__/doc-explorer.spec.tsx b/packages/graphiql-plugin-doc-explorer/src/components/__tests__/doc-explorer.spec.tsx index 76c11e54e30..d3ce99e1d99 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/__tests__/doc-explorer.spec.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/__tests__/doc-explorer.spec.tsx @@ -1,13 +1,25 @@ -import { act, render } from '@testing-library/react'; +import { Mock } from 'vitest'; +import { useGraphiQL as $useGraphiQL } from '@graphiql/react'; +import { render } from '@testing-library/react'; import { GraphQLInt, GraphQLObjectType, GraphQLSchema } from 'graphql'; import { FC, useEffect } from 'react'; import { - DocExplorerContextProvider, + DocExplorerStore, useDocExplorer, useDocExplorerActions, } from '../../context'; import { DocExplorer } from '../doc-explorer'; -import { schemaStore } from '../../../../graphiql-react/dist/schema'; + +const useGraphiQL = $useGraphiQL as Mock; + +vi.mock('@graphiql/react', async () => { + const originalModule = + await vi.importActual('@graphiql/react'); + return { + ...originalModule, + useGraphiQL: vi.fn(), + }; +}); function makeSchema(fieldName = 'field') { return new GraphQLSchema({ @@ -29,42 +41,49 @@ function makeSchema(fieldName = 'field') { } const defaultSchemaContext = { - ...schemaStore.getInitialState(), - async introspect() {}, + introspect() {}, schema: makeSchema(), + validationErrors: [], }; const withErrorSchemaContext = { - ...schemaStore.getInitialState(), + ...defaultSchemaContext, fetchError: 'Error fetching schema', - async introspect() {}, schema: new GraphQLSchema({ description: 'GraphQL Schema for testing' }), }; const DocExplorerWithContext: FC = () => { return ( - + - + ); }; describe('DocExplorer', () => { + beforeEach(() => { + vi.resetModules(); + }); + it('renders spinner when the schema is loading', () => { - schemaStore.setState({ isFetching: true }); + useGraphiQL.mockImplementation(cb => + cb({ ...defaultSchemaContext, isIntrospecting: true }), + ); const { container } = render(); const spinner = container.querySelectorAll('.graphiql-spinner'); expect(spinner).toHaveLength(1); }); it('renders with null schema', () => { - schemaStore.setState({ ...defaultSchemaContext, schema: null }); + useGraphiQL.mockImplementation(cb => + cb({ ...defaultSchemaContext, schema: null }), + ); const { container } = render(); const error = container.querySelectorAll('.graphiql-doc-explorer-error'); expect(error).toHaveLength(1); expect(error[0]).toHaveTextContent('No GraphQL schema available'); }); it('renders with schema', () => { - schemaStore.setState(defaultSchemaContext); + useGraphiQL.mockImplementation(cb => cb(defaultSchemaContext)); const { container } = render(); const error = container.querySelectorAll('.graphiql-doc-explorer-error'); expect(error).toHaveLength(0); @@ -73,14 +92,11 @@ describe('DocExplorer', () => { ).toHaveTextContent('GraphQL Schema for testing'); }); it('renders correctly with schema error', () => { - schemaStore.setState(withErrorSchemaContext); + useGraphiQL.mockImplementation(cb => cb(withErrorSchemaContext)); const { rerender, container } = render(); const error = container.querySelector('.graphiql-doc-explorer-error'); expect(error).toHaveTextContent('Error fetching schema'); - - act(() => { - schemaStore.setState(defaultSchemaContext); - }); + useGraphiQL.mockImplementation(cb => cb(defaultSchemaContext)); rerender(); const errors = container.querySelectorAll('.graphiql-doc-explorer-error'); expect(errors).toHaveLength(0); @@ -104,45 +120,38 @@ describe('DocExplorer', () => { }; // Initial render, set initial state - schemaStore.setState({ - ...defaultSchemaContext, - schema: initialSchema, - }); + useGraphiQL.mockImplementation(cb => + cb({ ...defaultSchemaContext, schema: initialSchema }), + ); const { container, rerender } = render( - + - , + , ); // First proper render of doc explorer - act(() => { - schemaStore.setState({ - ...defaultSchemaContext, - schema: initialSchema, - }); - }); rerender( - + - , + , ); - const [title] = container.querySelectorAll('.graphiql-doc-explorer-title'); + const title = container.querySelector('.graphiql-doc-explorer-title')!; expect(title.textContent).toEqual('field'); // Second render of doc explorer, this time with a new schema, with _same_ field name - act(() => { - schemaStore.setState({ + useGraphiQL.mockImplementation(cb => + cb({ ...defaultSchemaContext, schema: makeSchema(), // <<< New, but equivalent, schema - }); - }); + }), + ); rerender( - + - , + , ); - const [title2] = container.querySelectorAll('.graphiql-doc-explorer-title'); + const title2 = container.querySelector('.graphiql-doc-explorer-title')!; // Because `Query.field` still exists in the new schema, we can still render it expect(title2.textContent).toEqual('field'); }); @@ -166,43 +175,36 @@ describe('DocExplorer', () => { }; // Initial render, set initial state - schemaStore.setState({ - ...defaultSchemaContext, - schema: initialSchema, - }); + useGraphiQL.mockImplementation(cb => + cb({ ...defaultSchemaContext, schema: initialSchema }), + ); const { container, rerender } = render( - + - , + , ); // First proper render of doc explorer - act(() => { - schemaStore.setState({ - ...defaultSchemaContext, - schema: initialSchema, - }); - }); rerender( - + - , + , ); const title = container.querySelector('.graphiql-doc-explorer-title')!; expect(title.textContent).toEqual('field'); // Second render of doc explorer, this time with a new schema, with a different field name - act(() => { - schemaStore.setState({ + useGraphiQL.mockImplementation(cb => + cb({ ...defaultSchemaContext, schema: makeSchema('field2'), // <<< New schema with a new field name - }); - }); + }), + ); rerender( - + - , + , ); const title2 = container.querySelector('.graphiql-doc-explorer-title')!; // Because `Query.field` doesn't exist anymore, the top-most item we can render is `Query` diff --git a/packages/graphiql-plugin-doc-explorer/src/components/__tests__/field-documentation.spec.tsx b/packages/graphiql-plugin-doc-explorer/src/components/__tests__/field-documentation.spec.tsx index 9904a2c7aca..a8689eee9b3 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/__tests__/field-documentation.spec.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/__tests__/field-documentation.spec.tsx @@ -73,7 +73,7 @@ describe('FieldDocumentation', () => { it('should render a simple string field', () => { const { container } = render( , ); expect( @@ -90,7 +90,7 @@ describe('FieldDocumentation', () => { it('should re-render on field change', () => { const { container, rerender } = render( , ); expect( @@ -105,7 +105,7 @@ describe('FieldDocumentation', () => { rerender( , ); expect( @@ -119,7 +119,7 @@ describe('FieldDocumentation', () => { it('should render a string field with arguments', () => { const { container, getByText } = render( , ); expect( @@ -150,7 +150,7 @@ describe('FieldDocumentation', () => { it('should render a string field with directives', () => { const { container } = render( , ); expect( diff --git a/packages/graphiql-plugin-doc-explorer/src/components/__tests__/type-documentation.spec.tsx b/packages/graphiql-plugin-doc-explorer/src/components/__tests__/type-documentation.spec.tsx index 728f5d53e21..7858aae5de0 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/__tests__/type-documentation.spec.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/__tests__/type-documentation.spec.tsx @@ -10,13 +10,24 @@ import { import { docExplorerStore } from '../../context'; import { TypeDocumentation } from '../type-documentation'; import { unwrapType } from './test-utils'; -import { schemaStore } from '../../../../graphiql-react/dist/schema'; +import { SlicesWithActions } from '@graphiql/react'; + +vi.mock('@graphiql/react', async () => { + const originalModule = + await vi.importActual('@graphiql/react'); + const useGraphiQL: (typeof originalModule)['useGraphiQL'] = cb => + cb({ schema: ExampleSchema } as SlicesWithActions); + + return { + ...originalModule, + useGraphiQL, + }; +}); const TypeDocumentationWithContext: FC<{ type: GraphQLNamedType }> = ({ type, }) => { useEffect(() => { - schemaStore.setState({ schema: ExampleSchema }); docExplorerStore.setState({ explorerNavStack: [ { @@ -75,8 +86,8 @@ describe('TypeDocumentation', () => { ); const title = container.querySelector( '.graphiql-doc-explorer-section-title', - ); - title?.childNodes[0].remove(); + )!; + title.childNodes[0]!.remove(); expect(title).toHaveTextContent('Possible Types'); }); @@ -86,8 +97,8 @@ describe('TypeDocumentation', () => { ); const title = container.querySelector( '.graphiql-doc-explorer-section-title', - ); - title?.childNodes[0].remove(); + )!; + title.childNodes[0]!.remove(); expect(title).toHaveTextContent('Enum Values'); const enums = container.querySelectorAll( '.graphiql-doc-explorer-enum-value', @@ -105,8 +116,8 @@ describe('TypeDocumentation', () => { const title = container.querySelector( '.graphiql-doc-explorer-section-title', - ); - title?.childNodes[0].remove(); + )!; + title.childNodes[0]!.remove(); expect(title).toHaveTextContent('Enum Values'); let enums = container.querySelectorAll('.graphiql-doc-explorer-enum-value'); @@ -118,8 +129,8 @@ describe('TypeDocumentation', () => { const deprecatedTitle = container.querySelectorAll( '.graphiql-doc-explorer-section-title', - )[1]; - deprecatedTitle.childNodes[0].remove(); + )[1]!; + deprecatedTitle.childNodes[0]!.remove(); expect(deprecatedTitle).toHaveTextContent('Deprecated Enum Values'); enums = container.querySelectorAll('.graphiql-doc-explorer-enum-value'); diff --git a/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.css b/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.css index 8e8daa3bc35..88178fa122d 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.css +++ b/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.css @@ -38,7 +38,7 @@ &:not(:focus-within) [role='combobox'] { height: 24px; - width: 5ch; + width: 6.5ch; } & [role='combobox']:focus { diff --git a/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.tsx b/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.tsx index 5ecd33d0452..44241334616 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/doc-explorer.tsx @@ -1,6 +1,6 @@ import { isType } from 'graphql'; import { FC, ReactNode } from 'react'; -import { ChevronLeftIcon, Spinner, useSchemaStore } from '@graphiql/react'; +import { ChevronLeftIcon, Spinner, useGraphiQL, pick } from '@graphiql/react'; import { useDocExplorer, useDocExplorerActions } from '../context'; import { FieldDocumentation } from './field-documentation'; import { SchemaDocumentation } from './schema-documentation'; @@ -9,7 +9,9 @@ import { TypeDocumentation } from './type-documentation'; import './doc-explorer.css'; export const DocExplorer: FC = () => { - const { fetchError, isFetching, schema, validationErrors } = useSchemaStore(); + const { fetchError, isIntrospecting, schema, validationErrors } = useGraphiQL( + pick('fetchError', 'isIntrospecting', 'schema', 'validationErrors'), + ); const explorerNavStack = useDocExplorer(); const { pop } = useDocExplorerActions(); const navItem = explorerNavStack.at(-1)!; @@ -19,13 +21,13 @@ export const DocExplorer: FC = () => { content = (
Error fetching schema
); - } else if (validationErrors.length > 0) { + } else if (validationErrors[0]) { content = (
Schema is invalid: {validationErrors[0].message}
); - } else if (isFetching) { + } else if (isIntrospecting) { // Schema is undefined when it is being loaded via introspection. content = ; } else if (!schema) { diff --git a/packages/graphiql-plugin-doc-explorer/src/components/schema-documentation.tsx b/packages/graphiql-plugin-doc-explorer/src/components/schema-documentation.tsx index 545a178138e..24ae74e6e5f 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/schema-documentation.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/schema-documentation.tsx @@ -16,8 +16,8 @@ export const SchemaDocumentation: FC = ({ schema, }) => { const queryType = schema.getQueryType(); - const mutationType = schema.getMutationType?.(); - const subscriptionType = schema.getSubscriptionType?.(); + const mutationType = schema.getMutationType(); + const subscriptionType = schema.getSubscriptionType(); const typeMap = schema.getTypeMap(); const ignoreTypesInAllSchema = [ queryType?.name, @@ -57,24 +57,22 @@ export const SchemaDocumentation: FC = ({ )} - {typeMap && ( -
- {Object.values(typeMap).map(type => { - if ( - ignoreTypesInAllSchema.includes(type.name) || - type.name.startsWith('__') - ) { - return null; - } +
+ {Object.values(typeMap).map(type => { + if ( + ignoreTypesInAllSchema.includes(type.name) || + type.name.startsWith('__') + ) { + return null; + } - return ( -
- -
- ); - })} -
- )} + return ( +
+ +
+ ); + })} +
); diff --git a/packages/graphiql-plugin-doc-explorer/src/components/search.tsx b/packages/graphiql-plugin-doc-explorer/src/components/search.tsx index 2e215dc1355..8e52a424618 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/search.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/search.tsx @@ -15,10 +15,11 @@ import { ComboboxOption, } from '@headlessui/react'; import { - isMacOs, - useSchemaStore, + formatShortcutForOS, + useGraphiQL, MagnifyingGlassIcon, debounce, + KEY_MAP, } from '@graphiql/react'; import { useDocExplorer, useDocExplorerActions } from '../context'; import { renderType } from './utils'; @@ -44,20 +45,13 @@ export const Search: FC = () => { debouncedGetSearchResults(searchValue); }, [debouncedGetSearchResults, searchValue]); - useEffect(() => { - function handleKeyDown(event: KeyboardEvent) { - if (event.metaKey && event.key === 'k') { - inputRef.current.focus(); - } - } - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, []); - const navItem = explorerNavStack.at(-1)!; - const onSelect = (def: TypeMatch | FieldMatch) => { + const onSelect = (def: TypeMatch | FieldMatch | null) => { + // `null` when we remove search value + if (!def) { + return; + } push( 'field' in def ? { name: def.field.name, def: def.field } @@ -91,7 +85,9 @@ export const Search: FC = () => { setSearchValue(event.target.value)} - placeholder={`${isMacOs ? '⌘' : 'Ctrl'} K`} + placeholder={formatShortcutForOS( + formatShortcutForOS(KEY_MAP.searchInDocs.key).replaceAll('-', ' '), + )} ref={inputRef} value={searchValue} data-cy="doc-explorer-input" @@ -103,9 +99,9 @@ export const Search: FC = () => { results.types.length + results.fields.length === 0 ? ( -
  • +
    No results found -
  • +
    ) : ( results.within.map((result, i) => ( state.schema); const navItem = explorerNavStack.at(-1)!; @@ -195,7 +191,7 @@ export function useSearchResults() { break; } - const type = typeMap[typeName]; + const type = typeMap[typeName]!; if (withinType !== type && isMatch(typeName, searchValue)) { matches.types.push({ type }); } @@ -210,7 +206,7 @@ export function useSearchResults() { const fields = type.getFields(); for (const fieldName in fields) { - const field = fields[fieldName]; + const field = fields[fieldName]!; let matchingArgs: GraphQLArgument[] | undefined; if (!isMatch(fieldName, searchValue)) { diff --git a/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx b/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx index 29dde9b71f2..905b045c530 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/type-documentation.tsx @@ -9,7 +9,7 @@ import { isNamedType, isObjectType, } from 'graphql'; -import { useSchemaStore, Button, MarkdownContent } from '@graphiql/react'; +import { useGraphiQL, Button, MarkdownContent } from '@graphiql/react'; import { DocExplorerFieldDef } from '../context'; import { Argument } from './argument'; import { DefaultValue } from './default-value'; @@ -75,7 +75,8 @@ const Fields: FC<{ type: GraphQLNamedType }> = ({ type }) => { const fields: DocExplorerFieldDef[] = []; const deprecatedFields: DocExplorerFieldDef[] = []; - for (const field of Object.keys(fieldMap).map(name => fieldMap[name])) { + // TODO: maybe can be refactored to Object.values(fieldMap) ? + for (const field of Object.keys(fieldMap).map(name => fieldMap[name]!)) { if (field.deprecationReason) { deprecatedFields.push(field); } else { @@ -172,15 +173,15 @@ const EnumValues: FC<{ type: GraphQLNamedType }> = ({ type }) => { return ( <> - {values.length > 0 ? ( + {values.length > 0 && ( {values.map(value => ( ))} - ) : null} - {deprecatedValues.length > 0 ? ( - showDeprecated || values.length === 0 ? ( + )} + {deprecatedValues.length > 0 && + (showDeprecated || !values.length ? ( {deprecatedValues.map(value => ( @@ -190,8 +191,7 @@ const EnumValues: FC<{ type: GraphQLNamedType }> = ({ type }) => { - ) - ) : null} + ))} ); }; @@ -215,7 +215,7 @@ const EnumValue: FC<{ value: GraphQLEnumValue }> = ({ value }) => { }; const PossibleTypes: FC<{ type: GraphQLNamedType }> = ({ type }) => { - const { schema } = useSchemaStore(); + const schema = useGraphiQL(state => state.schema); if (!schema || !isAbstractType(type)) { return null; } diff --git a/packages/graphiql-plugin-doc-explorer/src/components/type-link.tsx b/packages/graphiql-plugin-doc-explorer/src/components/type-link.tsx index a4acdb99741..4932a97e3ac 100644 --- a/packages/graphiql-plugin-doc-explorer/src/components/type-link.tsx +++ b/packages/graphiql-plugin-doc-explorer/src/components/type-link.tsx @@ -14,20 +14,16 @@ type TypeLinkProps = { export const TypeLink: FC = ({ type }) => { const { push } = useDocExplorerActions(); - if (!type) { - return null; - } - - return renderType(type, namedType => ( + return renderType(type, def => ( { event.preventDefault(); - push({ name: namedType.name, def: namedType }); + push({ name: def.name, def }); }} href="#" > - {namedType.name} + {def.name} )); }; diff --git a/packages/graphiql-plugin-doc-explorer/src/context.ts b/packages/graphiql-plugin-doc-explorer/src/context.tsx similarity index 72% rename from packages/graphiql-plugin-doc-explorer/src/context.ts rename to packages/graphiql-plugin-doc-explorer/src/context.tsx index 380cec0e8a2..e0bb2c97a8a 100644 --- a/packages/graphiql-plugin-doc-explorer/src/context.ts +++ b/packages/graphiql-plugin-doc-explorer/src/context.tsx @@ -16,11 +16,31 @@ import { } from 'graphql'; import { FC, ReactElement, ReactNode, useEffect } from 'react'; import { + SchemaReference, + useGraphiQL, + pick, createBoundedUseStore, - SchemaContextType, - useSchemaStore, + GraphiQLPlugin, + DocsFilledIcon, + DocsIcon, + isMacOs, } from '@graphiql/react'; import { createStore } from 'zustand'; +import { getSchemaReference } from './schema-reference'; +import { DocExplorer } from './components'; + +export const DOC_EXPLORER_PLUGIN: GraphiQLPlugin = { + title: 'Documentation Explorer', + icon: function Icon() { + const visiblePlugin = useGraphiQL(state => state.visiblePlugin); + return visiblePlugin === DOC_EXPLORER_PLUGIN ? ( + + ) : ( + + ); + }, + content: DocExplorer, +}; export type DocExplorerFieldDef = | GraphQLField @@ -45,7 +65,7 @@ export type DocExplorerNavStack = [ ...DocExplorerNavStackItem[], ]; -export type DocExplorerContextType = { +export type DocExplorerStoreType = { /** * A stack of navigation items. The last item in the list is the current one. * This list always contains at least one item. @@ -67,7 +87,7 @@ export type DocExplorerContextType = { */ reset(): void; resolveSchemaReferenceToNavItem( - schemaReference: SchemaContextType['schemaReference'], + schemaReference: SchemaReference | null, ): void; /** * Replace the nav stack with an updated version using the new schema. @@ -78,7 +98,7 @@ export type DocExplorerContextType = { const INITIAL_NAV_STACK: DocExplorerNavStack = [{ name: 'Docs' }]; -export const docExplorerStore = createStore( +export const docExplorerStore = createStore( (set, get) => ({ explorerNavStack: INITIAL_NAV_STACK, actions: { @@ -114,36 +134,49 @@ export const docExplorerStore = createStore( if (!schemaReference) { return; } + const { kind, typeInfo } = schemaReference; + const ref = getSchemaReference(kind, typeInfo); + if (!ref) { + return; + } + const { push } = get().actions; - switch (schemaReference.kind) { + switch (ref.kind) { case 'Type': { push({ - name: schemaReference.type.name, - def: schemaReference.type, + name: ref.type.name, + def: ref.type, }); break; } case 'Field': { + // Show a field type on stack + if (ref.type) { + push({ + name: ref.type.name, + def: ref.type, + }); + } push({ - name: schemaReference.field.name, - def: schemaReference.field, + name: ref.field.name, + def: ref.field, }); break; } case 'Argument': { - if (schemaReference.field) { + if (ref.field) { push({ - name: schemaReference.field.name, - def: schemaReference.field, + name: ref.field.name, + def: ref.field, }); } break; } case 'EnumValue': { - if (schemaReference.type) { + if (ref.type) { push({ - name: schemaReference.type.name, - def: schemaReference.type, + name: ref.type.name, + def: ref.type, }); } break; @@ -235,10 +268,12 @@ export const docExplorerStore = createStore( }), ); -export const DocExplorerContextProvider: FC<{ +export const DocExplorerStore: FC<{ children: ReactNode; }> = ({ children }) => { - const { schema, validationErrors, schemaReference } = useSchemaStore(); + const { schema, validationErrors, schemaReference } = useGraphiQL( + pick('schema', 'validationErrors', 'schemaReference'), + ); useEffect(() => { const { resolveSchemaReferenceToNavItem } = @@ -258,6 +293,38 @@ export const DocExplorerContextProvider: FC<{ } }, [schema, validationErrors]); + useEffect(() => { + function handleKeyDown(event: KeyboardEvent) { + const shouldFocusInput = + // Use an additional `Alt` key instead of `Cmd/Ctrl+K` because monaco-editor has a built-in + // shortcut for `Cmd/Ctrl+K` + event.altKey && + event[isMacOs ? 'metaKey' : 'ctrlKey'] && + // Using `event.code` because `event.key` will trigger different character + // in English `˚` and in French `È` + event.code === 'KeyK'; + if (!shouldFocusInput) { + return; + } + const button = document.querySelector( + '.graphiql-sidebar button[aria-label="Show Documentation Explorer"]', + ); + button?.click(); + // Execute on next tick when doc explorer is opened and input exists in DOM + requestAnimationFrame(() => { + const el = document.querySelector( + '.graphiql-doc-explorer-search-input', + ); + el?.click(); + }); + } + + window.addEventListener('keydown', handleKeyDown); + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, []); + return children as ReactElement; }; @@ -265,5 +332,10 @@ const useDocExplorerStore = createBoundedUseStore(docExplorerStore); export const useDocExplorer = () => useDocExplorerStore(state => state.explorerNavStack); + +/** + * Actions are functions used to update values in your store. They are static and never change. + * @see https://tkdodo.eu/blog/working-with-zustand#separate-actions-from-state + */ export const useDocExplorerActions = () => useDocExplorerStore(state => state.actions); diff --git a/packages/graphiql-plugin-doc-explorer/src/index.ts b/packages/graphiql-plugin-doc-explorer/src/index.ts new file mode 100644 index 00000000000..811d0603fb8 --- /dev/null +++ b/packages/graphiql-plugin-doc-explorer/src/index.ts @@ -0,0 +1,14 @@ +export * from './components'; + +export { + DocExplorerStore, + useDocExplorer, + useDocExplorerActions, + DOC_EXPLORER_PLUGIN, +} from './context'; + +export type { + DocExplorerFieldDef, + DocExplorerNavStack, + DocExplorerNavStackItem, +} from './context'; diff --git a/packages/graphiql-plugin-doc-explorer/src/index.tsx b/packages/graphiql-plugin-doc-explorer/src/index.tsx deleted file mode 100644 index 076c484dfae..00000000000 --- a/packages/graphiql-plugin-doc-explorer/src/index.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { - DocsFilledIcon, - DocsIcon, - GraphiQLPlugin, - usePluginStore, -} from '@graphiql/react'; -import { DocExplorer } from './components'; - -export * from './components'; - -export { - DocExplorerContextProvider, - useDocExplorer, - useDocExplorerActions, -} from './context'; - -export type { - DocExplorerContextType, - DocExplorerFieldDef, - DocExplorerNavStack, - DocExplorerNavStackItem, -} from './context'; - -export const DOC_EXPLORER_PLUGIN: GraphiQLPlugin = { - title: 'Documentation Explorer', - icon: function Icon() { - const { visiblePlugin } = usePluginStore(); - return visiblePlugin === DOC_EXPLORER_PLUGIN ? ( - - ) : ( - - ); - }, - content: DocExplorer, -}; diff --git a/packages/graphiql-plugin-doc-explorer/src/schema-reference.ts b/packages/graphiql-plugin-doc-explorer/src/schema-reference.ts new file mode 100644 index 00000000000..7bac0a639c8 --- /dev/null +++ b/packages/graphiql-plugin-doc-explorer/src/schema-reference.ts @@ -0,0 +1,147 @@ +import { getNamedType } from 'graphql'; +import type { + GraphQLEnumType, + GraphQLNamedType, + GraphQLField, + GraphQLArgument, + GraphQLDirective, + GraphQLSchema, + GraphQLEnumValue, + GraphQLInputFieldMap, + GraphQLInputType, + GraphQLType, +} from 'graphql'; +import { Maybe } from 'graphql/jsutils/Maybe'; + +/** + * Copied from packages/codemirror-graphql/src/jump.ts + */ +export function getSchemaReference(kind: string, typeInfo: any) { + if ( + (kind === 'Field' && typeInfo.fieldDef) || + (kind === 'AliasedField' && typeInfo.fieldDef) + ) { + return getFieldReference(typeInfo); + } + if (kind === 'Directive' && typeInfo.directiveDef) { + return getDirectiveReference(typeInfo); + } + if (kind === 'Argument' && typeInfo.argDef) { + return getArgumentReference(typeInfo); + } + if (kind === 'EnumValue' && typeInfo.enumValue) { + return getEnumValueReference(typeInfo); + } + if (kind === 'NamedType' && typeInfo.type) { + return getTypeReference(typeInfo); + } +} + +function getArgumentReference(typeInfo: any): ArgumentReference { + return typeInfo.directiveDef + ? { + kind: 'Argument', + schema: typeInfo.schema, + argument: typeInfo.argDef, + directive: typeInfo.directiveDef, + } + : { + kind: 'Argument', + schema: typeInfo.schema, + argument: typeInfo.argDef, + field: typeInfo.fieldDef, + type: isMetaField(typeInfo.fieldDef) ? null : typeInfo.parentType, + }; +} + +function getDirectiveReference(typeInfo: any): DirectiveReference { + return { + kind: 'Directive', + schema: typeInfo.schema, + directive: typeInfo.directiveDef, + }; +} + +function getFieldReference(typeInfo: any): FieldReference { + return { + kind: 'Field', + schema: typeInfo.schema, + field: typeInfo.fieldDef, + type: isMetaField(typeInfo.fieldDef) ? null : typeInfo.parentType, + }; +} + +// Note: for reusability, getTypeReference can produce a reference to any type, +// though it defaults to the current type. +function getTypeReference( + typeInfo: any, + type?: Maybe, +): TypeReference { + return { + kind: 'Type', + schema: typeInfo.schema, + type: type || typeInfo.type, + }; +} + +function getEnumValueReference(typeInfo: TypeInfo): EnumValueReference { + return { + kind: 'EnumValue', + value: typeInfo.enumValue || undefined, + type: typeInfo.inputType + ? (getNamedType(typeInfo.inputType) as GraphQLEnumType) + : undefined, + }; +} + +function isMetaField(fieldDef: GraphQLField) { + return fieldDef.name.slice(0, 2) === '__'; +} + +type ArgumentReference = { + kind: 'Argument'; + argument: GraphQLArgument; + field?: GraphQLField; + type?: GraphQLNamedType; + directive?: GraphQLDirective; + schema?: GraphQLSchema; +}; + +type DirectiveReference = { + kind: 'Directive'; + directive: GraphQLDirective; + schema?: GraphQLSchema; +}; + +type EnumValueReference = { + kind: 'EnumValue'; + value?: GraphQLEnumValue; + type?: GraphQLEnumType; + schema?: GraphQLSchema; +}; + +type FieldReference = { + kind: 'Field'; + field: GraphQLField; + type: Maybe; + schema?: GraphQLSchema; +}; + +type TypeReference = { + kind: 'Type'; + type: GraphQLNamedType; + schema?: GraphQLSchema; +}; + +interface TypeInfo { + schema: GraphQLSchema; + type?: Maybe; + parentType?: Maybe; + inputType?: Maybe; + directiveDef?: Maybe; + fieldDef?: Maybe>; + argDef?: Maybe; + argDefs?: Maybe; + enumValue?: Maybe; + objectFieldDefs?: Maybe; +} diff --git a/packages/graphiql-plugin-doc-explorer/vite.config.mts b/packages/graphiql-plugin-doc-explorer/vite.config.mts index b09b69d4085..77e85815727 100644 --- a/packages/graphiql-plugin-doc-explorer/vite.config.mts +++ b/packages/graphiql-plugin-doc-explorer/vite.config.mts @@ -36,7 +36,7 @@ export default defineConfig({ minify: false, sourcemap: true, lib: { - entry: 'src/index.tsx', + entry: 'src/index.ts', fileName(_format, entryName) { const filePath = entryName.replace(/\.svg$/, ''); return `${filePath}.js`; diff --git a/packages/graphiql-plugin-doc-explorer/vitest.config.mts b/packages/graphiql-plugin-doc-explorer/vitest.config.mts index 2a31c6fda84..310795fe31f 100644 --- a/packages/graphiql-plugin-doc-explorer/vitest.config.mts +++ b/packages/graphiql-plugin-doc-explorer/vitest.config.mts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { defineConfig } from 'vitest/config'; import { plugins } from './vite.config.mjs'; @@ -7,5 +8,14 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: ['./setup-files.ts'], + alias: [ + { + // Fixes Error: Failed to resolve entry for package "monaco-editor". The package may have incorrect main/module/exports specified in its package.json. + find: /^monaco-editor$/, + replacement: path.resolve( + '../../node_modules/monaco-editor/esm/vs/editor/editor.api', + ), + }, + ], }, }); diff --git a/packages/graphiql-plugin-explorer/CHANGELOG.md b/packages/graphiql-plugin-explorer/CHANGELOG.md index 64c466ca7cd..2df3c2caac6 100644 --- a/packages/graphiql-plugin-explorer/CHANGELOG.md +++ b/packages/graphiql-plugin-explorer/CHANGELOG.md @@ -1,5 +1,60 @@ # @graphiql/plugin-explorer +## 5.0.0-rc.3 + +### Major Changes + +- [#4009](https://github.com/graphql/graphiql/pull/4009) [`4936492`](https://github.com/graphql/graphiql/commit/49364924d0da05a86f7c6c3139d44aed0e474531) Thanks [@dimaMachina](https://github.com/dimaMachina)! - separate store actions from state, add `useGraphiQLActions` state + +## 5.0.0-rc.2 + +### Major Changes + +- [#4002](https://github.com/graphql/graphiql/pull/4002) [`2d9faec`](https://github.com/graphql/graphiql/commit/2d9faec57830b38aa175929c47a55c959c327535) Thanks [@dimaMachina](https://github.com/dimaMachina)! - remove UMD builds + +## 5.0.0-rc.1 + +### Major Changes + +- [#3990](https://github.com/graphql/graphiql/pull/3990) [`27e7eb6`](https://github.com/graphql/graphiql/commit/27e7eb60247437d992c1fcdcc6870cb7892d4b92) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - allow multiple independent instances of GraphiQL on the same page + - store `onClickReference` in query editor in React `ref` + - remove `onClickReference` from variable editor + - fix shortcut text per OS for run query in execute query button's tooltip and in default query + - allow override all default GraphiQL plugins + - adjust operation argument color to be purple from GraphiQL v2 on dark/light theme + +## 4.0.7-rc.0 + +### Patch Changes + +- [#3949](https://github.com/graphql/graphiql/pull/3949) [`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - replace `onCopyQuery` hook with `copyQuery` function + + - replace `onMergeQuery` hook with `mergeQuery` function + - replace `onPrettifyEditors` hook with `prettifyEditors` function + - remove `fetcher` prop from `SchemaContextProvider` and `schemaStore` and add `fetcher` to `executionStore` + - add `onCopyQuery` and `onPrettifyQuery` props to `EditorContextProvider` + - remove exports (use `GraphiQLProvider`) + - `EditorContextProvider` + - `ExecutionContextProvider` + - `PluginContextProvider` + - `SchemaContextProvider` + - `StorageContextProvider` + - `ExecutionContextType` + - `PluginContextType` + - feat(@graphiql/react): migrate React context to zustand: + - replace `useExecutionContext` with `useExecutionStore` hook + - replace `useEditorContext` with `useEditorStore` hook + - prefer `getComputedStyle` over `window.getComputedStyle` + +- [#3234](https://github.com/graphql/graphiql/pull/3234) [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Migration from Codemirror to [Monaco Editor](https://github.com/microsoft/monaco-editor) + + Replacing `codemirror-graphql` with [`monaco-graphql`](https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql) + + Support for comments in **Variables** and **Headers** editors + +- Updated dependencies [[`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e), [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91), [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb)]: + - @graphiql/react@0.35.0-rc.0 + ## 4.0.6 ### Patch Changes diff --git a/packages/graphiql-plugin-explorer/package.json b/packages/graphiql-plugin-explorer/package.json index 7f8dfdfdfeb..7a746dcaa19 100644 --- a/packages/graphiql-plugin-explorer/package.json +++ b/packages/graphiql-plugin-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@graphiql/plugin-explorer", - "version": "4.0.6", + "version": "5.0.0-rc.3", "sideEffects": false, "repository": { "type": "git", @@ -27,21 +27,21 @@ }, "scripts": { "types:check": "tsc --noEmit", - "dev": "vite build --watch", - "build": "vite build && UMD=true vite build", + "dev": "vite build --watch --emptyOutDir=false", + "build": "vite build", "postbuild": "cp src/graphiql-explorer.d.ts dist/graphiql-explorer.d.ts" }, "dependencies": { "graphiql-explorer": "^0.9.0" }, "peerDependencies": { - "@graphiql/react": "^0.34.0", + "@graphiql/react": "^0.35.0-rc.0", "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2", "react": "^18 || ^19", "react-dom": "^18 || ^19" }, "devDependencies": { - "@graphiql/react": "^0.34.1", + "@graphiql/react": "^0.35.0-rc.3", "@vitejs/plugin-react": "^4.4.1", "graphql": "^16.9.0", "react": "^19.1.0", diff --git a/packages/graphiql-plugin-explorer/src/index.tsx b/packages/graphiql-plugin-explorer/src/index.tsx index 2a1704be668..9c51eb03b03 100644 --- a/packages/graphiql-plugin-explorer/src/index.tsx +++ b/packages/graphiql-plugin-explorer/src/index.tsx @@ -1,9 +1,8 @@ -import React, { CSSProperties, FC, useCallback } from 'react'; +import { CSSProperties, FC, useCallback } from 'react'; import { GraphiQLPlugin, - useEditorContext, - useExecutionContext, - useSchemaStore, + useGraphiQL, + useGraphiQLActions, useOperationsEditorState, useOptimisticState, } from '@graphiql/react'; @@ -62,9 +61,8 @@ export type GraphiQLExplorerPluginProps = Omit< >; const ExplorerPlugin: FC = props => { - const { setOperationName } = useEditorContext({ nonNull: true }); - const { schema } = useSchemaStore(); - const { run } = useExecutionContext({ nonNull: true }); + const { setOperationName, run } = useGraphiQLActions(); + const schema = useGraphiQL(state => state.schema); // handle running the current operation from the plugin const handleRunOperation = useCallback( diff --git a/packages/graphiql-plugin-explorer/tsconfig.json b/packages/graphiql-plugin-explorer/tsconfig.json index 2dd9b41294b..ee54eec68ce 100644 --- a/packages/graphiql-plugin-explorer/tsconfig.json +++ b/packages/graphiql-plugin-explorer/tsconfig.json @@ -1,19 +1,3 @@ { - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "declaration": true, - "jsx": "react" - } + "extends": "../graphiql-react/tsconfig.json" } diff --git a/packages/graphiql-plugin-explorer/vite.config.mts b/packages/graphiql-plugin-explorer/vite.config.mts index aa65e0ba7f0..72070a95d85 100644 --- a/packages/graphiql-plugin-explorer/vite.config.mts +++ b/packages/graphiql-plugin-explorer/vite.config.mts @@ -4,54 +4,36 @@ import svgr from 'vite-plugin-svgr'; import dts from 'vite-plugin-dts'; import packageJSON from './package.json'; -const IS_UMD = process.env.UMD === 'true'; - export default defineConfig({ plugins: [ - react({ jsxRuntime: 'classic' }), + react(), svgr({ svgrOptions: { titleProp: true, }, }), - !IS_UMD && [dts({ include: ['src/**'] })], + dts({ include: ['src/**'] }), ], css: { transformer: 'lightningcss', }, build: { - minify: IS_UMD - ? 'terser' // produce better bundle size than esbuild - : false, - // avoid clean cjs/es builds - emptyOutDir: !IS_UMD, + minify: false, lib: { entry: 'src/index.tsx', - fileName: (format, filePath) => - `${filePath}.${format === 'umd' ? 'umd.' : ''}js`, - name: 'GraphiQLPluginExplorer', - formats: IS_UMD ? ['umd'] : ['es'], + fileName: (_format, filePath) => `${filePath}.js`, + formats: ['es'], cssFileName: 'style', }, rollupOptions: { external: [ + 'react/jsx-runtime', // Exclude peer dependencies and dependencies from bundle - ...Object.keys(packageJSON.peerDependencies), - ...(IS_UMD ? [] : Object.keys(packageJSON.dependencies)), + ...Object.keys({ + ...packageJSON.peerDependencies, + ...packageJSON.dependencies, + }), ], - output: { - chunkFileNames: '[name].[format].js', - globals: { - '@graphiql/react': 'GraphiQL.React', - graphql: 'GraphiQL.GraphQL', - react: 'React', - 'react-dom': 'ReactDOM', - }, - }, - }, - commonjsOptions: { - esmExternals: true, - requireReturnsDefault: 'auto', }, }, }); diff --git a/packages/graphiql-plugin-history/CHANGELOG.md b/packages/graphiql-plugin-history/CHANGELOG.md index 0f5ca164543..d6871680c12 100644 --- a/packages/graphiql-plugin-history/CHANGELOG.md +++ b/packages/graphiql-plugin-history/CHANGELOG.md @@ -1,5 +1,78 @@ # @graphiql/plugin-history +## 0.3.0-rc.2 + +### Minor Changes + +- [#4011](https://github.com/graphql/graphiql/pull/4011) [`30bc3f9`](https://github.com/graphql/graphiql/commit/30bc3f9cae4dbb11649a0952dad092e192ad653c) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix execute query shortcut in query editor, run it even there are no operations in query editor + + fix plugin store, save last opened plugin in storage + +### Patch Changes + +- Updated dependencies [[`30bc3f9`](https://github.com/graphql/graphiql/commit/30bc3f9cae4dbb11649a0952dad092e192ad653c)]: + - @graphiql/react@0.35.0-rc.4 + +## 0.3.0-rc.1 + +### Minor Changes + +- [#3990](https://github.com/graphql/graphiql/pull/3990) [`27e7eb6`](https://github.com/graphql/graphiql/commit/27e7eb60247437d992c1fcdcc6870cb7892d4b92) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - allow multiple independent instances of GraphiQL on the same page + - store `onClickReference` in query editor in React `ref` + - remove `onClickReference` from variable editor + - fix shortcut text per OS for run query in execute query button's tooltip and in default query + - allow override all default GraphiQL plugins + - adjust operation argument color to be purple from GraphiQL v2 on dark/light theme + +### Patch Changes + +- Updated dependencies [[`27e7eb6`](https://github.com/graphql/graphiql/commit/27e7eb60247437d992c1fcdcc6870cb7892d4b92)]: + - @graphiql/react@0.35.0-rc.1 + +## 0.3.0-rc.0 + +### Minor Changes + +- [#3950](https://github.com/graphql/graphiql/pull/3950) [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - remove `useQueryEditor`, `useVariableEditor`, `useHeaderEditor`, `useResponseEditor` hooks + - remove `UseHeaderEditorArgs`, `UseQueryEditorArgs`, `UseResponseEditorArgs`, `UseVariableEditorArgs` exports + - rename components + - `StorageContextProvider` => `StorageStore` + - `EditorContextProvider` => `EditorStore` + - `SchemaContextProvider` => `SchemaStore` + - `ExecutionContextProvider` => `ExecutionStore` + - `HistoryContextProvider` => `HistoryStore` + - `ExplorerContextProvider` => `ExplorerStore` + +### Patch Changes + +- [#3949](https://github.com/graphql/graphiql/pull/3949) [`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - replace `onCopyQuery` hook with `copyQuery` function + + - replace `onMergeQuery` hook with `mergeQuery` function + - replace `onPrettifyEditors` hook with `prettifyEditors` function + - remove `fetcher` prop from `SchemaContextProvider` and `schemaStore` and add `fetcher` to `executionStore` + - add `onCopyQuery` and `onPrettifyQuery` props to `EditorContextProvider` + - remove exports (use `GraphiQLProvider`) + - `EditorContextProvider` + - `ExecutionContextProvider` + - `PluginContextProvider` + - `SchemaContextProvider` + - `StorageContextProvider` + - `ExecutionContextType` + - `PluginContextType` + - feat(@graphiql/react): migrate React context to zustand: + - replace `useExecutionContext` with `useExecutionStore` hook + - replace `useEditorContext` with `useEditorStore` hook + - prefer `getComputedStyle` over `window.getComputedStyle` + +- [#3234](https://github.com/graphql/graphiql/pull/3234) [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Migration from Codemirror to [Monaco Editor](https://github.com/microsoft/monaco-editor) + + Replacing `codemirror-graphql` with [`monaco-graphql`](https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql) + + Support for comments in **Variables** and **Headers** editors + +- Updated dependencies [[`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e), [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91), [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb)]: + - @graphiql/react@0.35.0-rc.0 + ## 0.2.2 ### Patch Changes diff --git a/packages/graphiql-plugin-history/README.md b/packages/graphiql-plugin-history/README.md index 7a5f86745f6..7b0bfc28a01 100644 --- a/packages/graphiql-plugin-history/README.md +++ b/packages/graphiql-plugin-history/README.md @@ -1 +1,6 @@ # `@graphiql/plugin-history` + +## API + +- `useHistory`: Persists executed requests in storage +- `useHistoryActions`: Actions related to the history diff --git a/packages/graphiql-plugin-history/__mocks__/monaco-editor.ts b/packages/graphiql-plugin-history/__mocks__/monaco-editor.ts new file mode 120000 index 00000000000..b1964294fb0 --- /dev/null +++ b/packages/graphiql-plugin-history/__mocks__/monaco-editor.ts @@ -0,0 +1 @@ +../../graphiql/__mocks__/monaco-editor.ts \ No newline at end of file diff --git a/packages/graphiql-plugin-history/package.json b/packages/graphiql-plugin-history/package.json index 94e2e3bfc08..10f53d0a579 100644 --- a/packages/graphiql-plugin-history/package.json +++ b/packages/graphiql-plugin-history/package.json @@ -1,6 +1,6 @@ { "name": "@graphiql/plugin-history", - "version": "0.2.2", + "version": "0.3.0-rc.2", "sideEffects": false, "repository": { "type": "git", @@ -30,7 +30,7 @@ ], "scripts": { "types:check": "tsc --noEmit", - "dev": "vite build --watch", + "dev": "vite build --watch --emptyOutDir=false", "build": "vite build", "test": "vitest" }, @@ -39,7 +39,7 @@ "react-dom": "^18 || ^19" }, "dependencies": { - "@graphiql/react": "^0.34.1", + "@graphiql/react": "^0.35.0-rc.4", "@graphiql/toolkit": "^0.11.3", "react-compiler-runtime": "19.1.0-rc.1", "zustand": "^5" diff --git a/packages/graphiql-plugin-history/setup-files.ts b/packages/graphiql-plugin-history/setup-files.ts index 276c7c4f7a3..da20690a55f 100644 --- a/packages/graphiql-plugin-history/setup-files.ts +++ b/packages/graphiql-plugin-history/setup-files.ts @@ -2,4 +2,6 @@ import '@testing-library/jest-dom'; -vi.mock('zustand'); // to make it works like Jest (auto-mocking) +// to make it works like Jest (auto-mocking) +vi.mock('zustand'); +vi.mock('monaco-editor'); diff --git a/packages/graphiql-plugin-history/src/__tests__/components.spec.tsx b/packages/graphiql-plugin-history/src/__tests__/components.spec.tsx index 749ccdcb3ae..caa55c9f916 100644 --- a/packages/graphiql-plugin-history/src/__tests__/components.spec.tsx +++ b/packages/graphiql-plugin-history/src/__tests__/components.spec.tsx @@ -1,13 +1,9 @@ -import type { Mock } from 'vitest'; +import { Mock } from 'vitest'; import { fireEvent, render } from '@testing-library/react'; import type { ComponentProps } from 'react'; import { formatQuery, HistoryItem } from '../components'; -import { HistoryContextProvider } from '../context'; -import { - useEditorContext, - Tooltip, - StorageContextProvider, -} from '@graphiql/react'; +import { HistoryStore } from '../context'; +import { Tooltip, GraphiQLProvider, useGraphiQL } from '@graphiql/react'; vi.mock('@graphiql/react', async () => { const originalModule = await vi.importActual('@graphiql/react'); @@ -16,7 +12,7 @@ vi.mock('@graphiql/react', async () => { const mockedSetHeaderEditor = vi.fn(); return { ...originalModule, - useEditorContext() { + useGraphiQL() { return { queryEditor: { setValue: mockedSetQueryEditor }, variableEditor: { setValue: mockedSetVariableEditor }, @@ -24,9 +20,6 @@ vi.mock('@graphiql/react', async () => { tabs: [], }; }, - useExecutionContext() { - return {}; - }, }; }); @@ -49,11 +42,11 @@ type QueryHistoryItemProps = ComponentProps; const QueryHistoryItemWithContext: typeof HistoryItem = props => { return ( - - + + - - + + ); }; @@ -78,17 +71,19 @@ function getMockProps( } describe('QueryHistoryItem', () => { - const mockedSetQueryEditor = useEditorContext()!.queryEditor! - .setValue as Mock; - const mockedSetVariableEditor = useEditorContext()!.variableEditor! - .setValue as Mock; - const mockedSetHeaderEditor = useEditorContext()!.headerEditor! - .setValue as Mock; + const { queryEditor, variableEditor, headerEditor } = useGraphiQL( + state => state, + ); + const mockedSetQueryEditor = queryEditor!.setValue as Mock; + const mockedSetVariableEditor = variableEditor!.setValue as Mock; + const mockedSetHeaderEditor = headerEditor!.setValue as Mock; + beforeEach(() => { mockedSetQueryEditor.mockClear(); mockedSetVariableEditor.mockClear(); mockedSetHeaderEditor.mockClear(); }); + it('renders operationName if label is not provided', () => { const otherMockProps = { item: { operationName: mockOperationName } }; const props = getMockProps(otherMockProps); diff --git a/packages/graphiql-plugin-history/src/components.tsx b/packages/graphiql-plugin-history/src/components.tsx index e2e54d86e9a..1710eaf6ee7 100644 --- a/packages/graphiql-plugin-history/src/components.tsx +++ b/packages/graphiql-plugin-history/src/components.tsx @@ -7,7 +7,8 @@ import { StarFilledIcon, StarIcon, TrashIcon, - useEditorContext, + useGraphiQL, + pick, Button, Tooltip, UnStyledButton, @@ -112,10 +113,9 @@ type QueryHistoryItemProps = { export const HistoryItem: FC = props => { const { editLabel, toggleFavorite, deleteFromHistory, setActive } = useHistoryActions(); - const { headerEditor, queryEditor, variableEditor } = useEditorContext({ - nonNull: true, - caller: HistoryItem, - }); + const { headerEditor, queryEditor, variableEditor } = useGraphiQL( + pick('headerEditor', 'queryEditor', 'variableEditor'), + ); const inputRef = useRef(null); const buttonRef = useRef(null); const [isEditable, setIsEditable] = useState(false); diff --git a/packages/graphiql-plugin-history/src/context.tsx b/packages/graphiql-plugin-history/src/context.tsx index 14bc898e00c..666fb81430d 100644 --- a/packages/graphiql-plugin-history/src/context.tsx +++ b/packages/graphiql-plugin-history/src/context.tsx @@ -1,43 +1,47 @@ // eslint-disable-next-line react/jsx-filename-extension -- TODO import { FC, ReactElement, ReactNode, useEffect } from 'react'; import { createStore } from 'zustand'; -import { HistoryStore, QueryStoreItem } from '@graphiql/toolkit'; import { - useExecutionContext, - useEditorContext, + HistoryStore as ToolkitHistoryStore, + QueryStoreItem, +} from '@graphiql/toolkit'; +import { + useGraphiQL, + pick, useStorage, createBoundedUseStore, } from '@graphiql/react'; -const historyStore = createStore((set, get) => ({ - historyStorage: null!, +const historyStore = createStore((set, get) => ({ + historyStorage: null, actions: { addToHistory(operation) { const { historyStorage } = get(); - historyStorage.updateHistory(operation); + historyStorage?.updateHistory(operation); set({}); // trigger rerender }, editLabel(operation, index) { const { historyStorage } = get(); - historyStorage.editLabel(operation, index); + historyStorage?.editLabel(operation, index); set({}); // trigger rerender }, toggleFavorite(operation) { const { historyStorage } = get(); - historyStorage.toggleFavorite(operation); + historyStorage?.toggleFavorite(operation); set({}); // trigger rerender }, setActive: item => item, deleteFromHistory(item, clearFavorites) { const { historyStorage } = get(); - historyStorage.deleteHistory(item, clearFavorites); + historyStorage?.deleteHistory(item, clearFavorites); set({}); // trigger rerender }, }, })); -type HistoryContextType = { - historyStorage: HistoryStore; +type HistoryStoreType = { + // Can be `null` if History plugin saved in `localStorage` as `visiblePlugin` + historyStorage: ToolkitHistoryStore | null; actions: { /** * Add an operation to the history. @@ -100,7 +104,7 @@ type HistoryContextType = { }; }; -type HistoryContextProviderProps = { +type HistoryStoreProps = { children: ReactNode; /** * The maximum number of executed operations to store. @@ -110,22 +114,23 @@ type HistoryContextProviderProps = { }; /** - * The functions send the entire operation so users can customize their own application with - * and get access to the operation plus - * any additional props they added for their needs (i.e., build their own functions that may save - * to a backend instead of localStorage and might need an id property added to the QueryStoreItem) + * The functions send the entire operation so users can customize their own application and get + * access to the operation plus any additional props they added for their needs (i.e., build their + * own functions that may save to a backend instead of localStorage and might need an id property + * added to the `QueryStoreItem`) */ -export const HistoryContextProvider: FC = ({ +export const HistoryStore: FC = ({ maxHistoryLength = 20, children, }) => { - const { isFetching } = useExecutionContext({ nonNull: true }); - const { tabs, activeTabIndex } = useEditorContext({ nonNull: true }); - const activeTab = tabs[activeTabIndex]; + const { isFetching, tabs, activeTabIndex } = useGraphiQL( + pick('isFetching', 'tabs', 'activeTabIndex'), + ); + const activeTab = tabs[activeTabIndex]!; const storage = useStorage(); const historyStorage = // eslint-disable-line react-hooks/exhaustive-deps -- false positive, code is optimized by React Compiler - new HistoryStore(storage, maxHistoryLength); + new ToolkitHistoryStore(storage, maxHistoryLength); useEffect(() => { historyStore.setState({ historyStorage }); @@ -149,6 +154,13 @@ export const HistoryContextProvider: FC = ({ const useHistoryStore = createBoundedUseStore(historyStore); +const EMPTY_ARRAY: QueryStoreItem[] = []; + export const useHistory = () => - useHistoryStore(state => state.historyStorage.queries); + useHistoryStore(state => state.historyStorage?.queries ?? EMPTY_ARRAY); + +/** + * Actions are functions used to update values in your store. They are static and never change. + * @see https://tkdodo.eu/blog/working-with-zustand#separate-actions-from-state + */ export const useHistoryActions = () => useHistoryStore(state => state.actions); diff --git a/packages/graphiql-plugin-history/src/index.ts b/packages/graphiql-plugin-history/src/index.ts index 7f16625bd91..1ea1e91b7b3 100644 --- a/packages/graphiql-plugin-history/src/index.ts +++ b/packages/graphiql-plugin-history/src/index.ts @@ -11,8 +11,4 @@ export const HISTORY_PLUGIN: GraphiQLPlugin = { export { History }; -export { - HistoryContextProvider, - useHistory, - useHistoryActions, -} from './context'; +export { HistoryStore, useHistory, useHistoryActions } from './context'; diff --git a/packages/graphiql-plugin-history/vitest.config.mts b/packages/graphiql-plugin-history/vitest.config.mts index 2a31c6fda84..310795fe31f 100644 --- a/packages/graphiql-plugin-history/vitest.config.mts +++ b/packages/graphiql-plugin-history/vitest.config.mts @@ -1,3 +1,4 @@ +import path from 'node:path'; import { defineConfig } from 'vitest/config'; import { plugins } from './vite.config.mjs'; @@ -7,5 +8,14 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: ['./setup-files.ts'], + alias: [ + { + // Fixes Error: Failed to resolve entry for package "monaco-editor". The package may have incorrect main/module/exports specified in its package.json. + find: /^monaco-editor$/, + replacement: path.resolve( + '../../node_modules/monaco-editor/esm/vs/editor/editor.api', + ), + }, + ], }, }); diff --git a/packages/graphiql-react/CHANGELOG.md b/packages/graphiql-react/CHANGELOG.md index ac4ebace2dc..76c44de1193 100644 --- a/packages/graphiql-react/CHANGELOG.md +++ b/packages/graphiql-react/CHANGELOG.md @@ -1,5 +1,97 @@ # @graphiql/react +## 0.35.0-rc.6 + +### Minor Changes + +- [#4017](https://github.com/graphql/graphiql/pull/4017) [`cff3da5`](https://github.com/graphql/graphiql/commit/cff3da541184d36d1c2e5c919dd4231e9905ccbb) Thanks [@dimaMachina](https://github.com/dimaMachina)! - extract graphiql sidebar to react component + +## 0.35.0-rc.5 + +### Minor Changes + +- [#4014](https://github.com/graphql/graphiql/pull/4014) [`4b39f11`](https://github.com/graphql/graphiql/commit/4b39f1118d008c2fac6e2df9c94a3f3271c4eeb9) Thanks [@dimaMachina](https://github.com/dimaMachina)! - extract storage key constants + +## 0.35.0-rc.4 + +### Minor Changes + +- [#4011](https://github.com/graphql/graphiql/pull/4011) [`30bc3f9`](https://github.com/graphql/graphiql/commit/30bc3f9cae4dbb11649a0952dad092e192ad653c) Thanks [@dimaMachina](https://github.com/dimaMachina)! - fix execute query shortcut in query editor, run it even there are no operations in query editor + + fix plugin store, save last opened plugin in storage + +## 0.35.0-rc.3 + +### Minor Changes + +- [#4009](https://github.com/graphql/graphiql/pull/4009) [`4936492`](https://github.com/graphql/graphiql/commit/49364924d0da05a86f7c6c3139d44aed0e474531) Thanks [@dimaMachina](https://github.com/dimaMachina)! - separate store actions from state, add `useGraphiQLActions` state + +## 0.35.0-rc.2 + +### Minor Changes + +- [#3999](https://github.com/graphql/graphiql/pull/3999) [`866a8f3`](https://github.com/graphql/graphiql/commit/866a8f39a27d213315ccc55ec06353bb3280b270) Thanks [@dimaMachina](https://github.com/dimaMachina)! - update graphiql-cdn example to show how to load workers with esm.sh + +- [#4005](https://github.com/graphql/graphiql/pull/4005) [`1e3ec84`](https://github.com/graphql/graphiql/commit/1e3ec8455706e62e6cae306df58d3343ec6b612d) Thanks [@dimaMachina](https://github.com/dimaMachina)! - support `externalFragments` prop and remove `validationRules` prop + +- [#4003](https://github.com/graphql/graphiql/pull/4003) [`0c8e390`](https://github.com/graphql/graphiql/commit/0c8e3906cf58055f898cb173b2e912a494ae8439) Thanks [@dimaMachina](https://github.com/dimaMachina)! - remove `readOnly` prop + document `keyMap` prop was removed in migration guide + +### Patch Changes + +- Updated dependencies [[`1e3ec84`](https://github.com/graphql/graphiql/commit/1e3ec8455706e62e6cae306df58d3343ec6b612d)]: + - monaco-graphql@1.7.1-rc.0 + +## 0.35.0-rc.1 + +### Minor Changes + +- [#3990](https://github.com/graphql/graphiql/pull/3990) [`27e7eb6`](https://github.com/graphql/graphiql/commit/27e7eb60247437d992c1fcdcc6870cb7892d4b92) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - allow multiple independent instances of GraphiQL on the same page + - store `onClickReference` in query editor in React `ref` + - remove `onClickReference` from variable editor + - fix shortcut text per OS for run query in execute query button's tooltip and in default query + - allow override all default GraphiQL plugins + - adjust operation argument color to be purple from GraphiQL v2 on dark/light theme + +## 0.35.0-rc.0 + +### Minor Changes + +- [#3949](https://github.com/graphql/graphiql/pull/3949) [`0844dc1`](https://github.com/graphql/graphiql/commit/0844dc1ca89a5d8fce0dc23658cca6987ff8443e) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - replace `onCopyQuery` hook with `copyQuery` function + + - replace `onMergeQuery` hook with `mergeQuery` function + - replace `onPrettifyEditors` hook with `prettifyEditors` function + - remove `fetcher` prop from `SchemaContextProvider` and `schemaStore` and add `fetcher` to `executionStore` + - add `onCopyQuery` and `onPrettifyQuery` props to `EditorContextProvider` + - remove exports (use `GraphiQLProvider`) + - `EditorContextProvider` + - `ExecutionContextProvider` + - `PluginContextProvider` + - `SchemaContextProvider` + - `StorageContextProvider` + - `ExecutionContextType` + - `PluginContextType` + - feat(@graphiql/react): migrate React context to zustand: + - replace `useExecutionContext` with `useExecutionStore` hook + - replace `useEditorContext` with `useEditorStore` hook + - prefer `getComputedStyle` over `window.getComputedStyle` + +- [#3234](https://github.com/graphql/graphiql/pull/3234) [`86a96e5`](https://github.com/graphql/graphiql/commit/86a96e5f1779b5d0e84ad4179dbd6c5d4947fb91) Thanks [@dimaMachina](https://github.com/dimaMachina)! - Migration from Codemirror to [Monaco Editor](https://github.com/microsoft/monaco-editor) + + Replacing `codemirror-graphql` with [`monaco-graphql`](https://github.com/graphql/graphiql/tree/main/packages/monaco-graphql) + + Support for comments in **Variables** and **Headers** editors + +- [#3950](https://github.com/graphql/graphiql/pull/3950) [`2455907`](https://github.com/graphql/graphiql/commit/245590708cea52ff6f1bcce8664781f7e56029cb) Thanks [@dimaMachina](https://github.com/dimaMachina)! - - remove `useQueryEditor`, `useVariableEditor`, `useHeaderEditor`, `useResponseEditor` hooks + - remove `UseHeaderEditorArgs`, `UseQueryEditorArgs`, `UseResponseEditorArgs`, `UseVariableEditorArgs` exports + - rename components + - `StorageContextProvider` => `StorageStore` + - `EditorContextProvider` => `EditorStore` + - `SchemaContextProvider` => `SchemaStore` + - `ExecutionContextProvider` => `ExecutionStore` + - `HistoryContextProvider` => `HistoryStore` + - `ExplorerContextProvider` => `ExplorerStore` + ## 0.34.1 ### Patch Changes diff --git a/packages/graphiql-react/README.md b/packages/graphiql-react/README.md index 0f3d461c6ff..586a7b38a96 100644 --- a/packages/graphiql-react/README.md +++ b/packages/graphiql-react/README.md @@ -81,24 +81,21 @@ Further details on how to use `@graphiql/react` can be found in the reference implementation of a GraphQL IDE - Graph*i*QL - in the [`graphiql` package](https://github.com/graphql/graphiql/blob/main/packages/graphiql/src/components/GraphiQL.tsx). -## Available contexts +## Available stores -There are multiple contexts that own different parts of the state that make up a -complete GraphQL IDE. For each context there is a provider component -(`ContextProvider`) that makes sure the context is initialized and managed +There are multiple stores that own different parts of the state that make up a +complete GraphQL IDE. For each store there is a component +(`Store`) that makes sure the store is initialized and managed properly. These components contains all the logic related to state management. -In addition, for each context there is also a hook (`useContext`) that -allows you to consume its current value. -Here is a list of all contexts that come with `@graphiql/react` +In addition, for each store, there is also a hook that +allows you to consume its current value: -- `StorageContext`: Provides a storage API that can be used to persist state in +- `useStorage`: Provides a storage API that can be used to persist state in the browser (by default using `localStorage`) -- `EditorContext`: Manages all the editors and tabs -- `SchemaContext`: Fetches, validates and stores the GraphQL schema -- `ExecutionContext`: Executes GraphQL requests -- `HistoryContext`: Persists executed requests in storage -- `ExplorerContext`: Handles the state for the docs explorer +- `useEditorStore`: Manages all the editors and tabs +- `useSchemaStore`: Fetches, validates and stores the GraphQL schema +- `useExecutionStore`: Executes GraphQL requests All context properties are documented using JSDoc comments. If you're using an IDE like VSCode for development these descriptions will show up in auto-complete diff --git a/packages/graphiql-react/__mocks__/monaco-editor.ts b/packages/graphiql-react/__mocks__/monaco-editor.ts new file mode 120000 index 00000000000..b1964294fb0 --- /dev/null +++ b/packages/graphiql-react/__mocks__/monaco-editor.ts @@ -0,0 +1 @@ +../../graphiql/__mocks__/monaco-editor.ts \ No newline at end of file diff --git a/packages/graphiql-react/package.json b/packages/graphiql-react/package.json index 5fcbac2d665..adbe9f81abd 100644 --- a/packages/graphiql-react/package.json +++ b/packages/graphiql-react/package.json @@ -1,7 +1,10 @@ { "name": "@graphiql/react", - "version": "0.34.1", - "sideEffects": false, + "version": "0.35.0-rc.6", + "sideEffects": [ + "dist/setup-workers/webpack.js", + "dist/setup-workers/vite.js" + ], "repository": { "type": "git", "url": "https://github.com/graphql/graphiql", @@ -16,14 +19,20 @@ "./package.json": "./package.json", "./style.css": "./dist/style.css", "./font/*": "./font/*", - ".": "./dist/index.js" + ".": "./dist/index.js", + "./setup-workers/*": { + "types": "./dist/setup-workers/*.d.ts", + "import": "./dist/setup-workers/*.js" + } }, "types": "dist/index.d.ts", "keywords": [ "react", "graphql", "sdk", - "codemirror" + "monaco-editor", + "monaco-graphql", + "monaco" ], "files": [ "dist", @@ -31,9 +40,9 @@ ], "scripts": { "types:check": "tsc --noEmit", - "dev": "vite build --watch", + "dev": "vite build --watch --emptyOutDir=false", "build": "vite build", - "test": "vitest" + "test": "vitest --typecheck" }, "peerDependencies": { "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", @@ -46,15 +55,15 @@ "@radix-ui/react-dropdown-menu": "^2.1", "@radix-ui/react-tooltip": "^1.2", "@radix-ui/react-visually-hidden": "^1.2", - "@types/codemirror": "^5.60.8", "clsx": "^1.2.1", - "codemirror": "^5.65.3", - "codemirror-graphql": "^2.2.1", - "copy-to-clipboard": "^3.2.0", - "framer-motion": "^12", + "framer-motion": "^12.12", "get-value": "^3.0.1", "graphql-language-service": "^5.3.1", + "jsonc-parser": "^3.3.1", "markdown-it": "^14.1.0", + "monaco-editor": "^0.52.2", + "monaco-graphql": "^1.7.1-rc.0", + "prettier": "^3.5.3", "react-compiler-runtime": "19.1.0-rc.1", "set-value": "^4.1.0", "zustand": "^5" @@ -74,5 +83,10 @@ "vite": "^6.3.4", "vite-plugin-dts": "^4.5.3", "vite-plugin-svgr": "^4.3.0" + }, + "browser": { + "//": "Prevents esm.sh from injecting Node.js globals like `process`, which can break browser features (e.g., Mac `Cmd` key) when loading from CDN.", + "buffer": false, + "process": false } } diff --git a/packages/graphiql-react/setup-files.ts b/packages/graphiql-react/setup-files.ts new file mode 100644 index 00000000000..b460f6b0ccc --- /dev/null +++ b/packages/graphiql-react/setup-files.ts @@ -0,0 +1,4 @@ +// to make it works like Jest (auto-mocking) +vi.mock('monaco-editor'); + +export {}; diff --git a/packages/graphiql-react/src/ui/button-group.css b/packages/graphiql-react/src/components/button-group/index.css similarity index 100% rename from packages/graphiql-react/src/ui/button-group.css rename to packages/graphiql-react/src/components/button-group/index.css diff --git a/packages/graphiql-react/src/components/button-group/index.tsx b/packages/graphiql-react/src/components/button-group/index.tsx new file mode 100644 index 00000000000..3534425da4c --- /dev/null +++ b/packages/graphiql-react/src/components/button-group/index.tsx @@ -0,0 +1,15 @@ +import { ComponentPropsWithoutRef, forwardRef } from 'react'; +import { cn } from '../../utility'; +import './index.css'; + +export const ButtonGroup = forwardRef< + HTMLDivElement, + ComponentPropsWithoutRef<'div'> +>((props, ref) => ( +
    +)); +ButtonGroup.displayName = 'ButtonGroup'; diff --git a/packages/graphiql-react/src/ui/button.css b/packages/graphiql-react/src/components/button/index.css similarity index 100% rename from packages/graphiql-react/src/ui/button.css rename to packages/graphiql-react/src/components/button/index.css diff --git a/packages/graphiql-react/src/components/button/index.tsx b/packages/graphiql-react/src/components/button/index.tsx new file mode 100644 index 00000000000..b5d94fab7f8 --- /dev/null +++ b/packages/graphiql-react/src/components/button/index.tsx @@ -0,0 +1,39 @@ +import { ComponentPropsWithoutRef, forwardRef } from 'react'; +import { cn } from '../../utility'; +import './index.css'; + +type UnStyledButtonProps = ComponentPropsWithoutRef<'button'>; + +export const UnStyledButton = forwardRef< + HTMLButtonElement, + UnStyledButtonProps +>((props, ref) => ( +