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/examples/graphiql-cdn/index.html b/examples/graphiql-cdn/index.html
index 88816672c0c..1312edc635f 100644
--- a/examples/graphiql-cdn/index.html
+++ b/examples/graphiql-cdn/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-doc-explorer/src/components/search.tsx b/packages/graphiql-plugin-doc-explorer/src/components/search.tsx
index 97d5c46c899..95fdd852f7c 100644
--- a/packages/graphiql-plugin-doc-explorer/src/components/search.tsx
+++ b/packages/graphiql-plugin-doc-explorer/src/components/search.tsx
@@ -58,7 +58,11 @@ export const Search: FC = () => {
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 }
@@ -106,9 +110,9 @@ export const Search: FC = () => {
results.types.length +
results.fields.length ===
0 ? (
-
+
No results found
-
+
) : (
results.within.map((result, i) => (
= ({ path }) => {
const [{ width, height }, setDimensions] = useState({
diff --git a/packages/graphiql-react/src/components/query-editor.tsx b/packages/graphiql-react/src/components/query-editor.tsx
index e10c0493341..2bf263550c3 100644
--- a/packages/graphiql-react/src/components/query-editor.tsx
+++ b/packages/graphiql-react/src/components/query-editor.tsx
@@ -89,6 +89,7 @@ export const QueryEditor: FC = ({
useEffect(() => {
onClickReferenceRef.current = onClickReference;
}, [onClickReference]);
+
/*
useEffect(() => {
void importCodeMirrorImports().then(CodeMirror => {
diff --git a/packages/graphiql-react/src/components/toolbar-menu/index.tsx b/packages/graphiql-react/src/components/toolbar-menu/index.tsx
index 7183993d976..a5f54241283 100644
--- a/packages/graphiql-react/src/components/toolbar-menu/index.tsx
+++ b/packages/graphiql-react/src/components/toolbar-menu/index.tsx
@@ -5,10 +5,10 @@ import { DropdownMenu } from '../dropdown-menu';
import { Tooltip } from '../tooltip';
import './index.css';
-type ToolbarMenuProps = {
+interface ToolbarMenuProps {
button: ReactNode;
label: string;
-};
+}
const ToolbarMenuRoot: FC<
ToolbarMenuProps & {
diff --git a/packages/graphiql-react/src/monaco-editor.ts b/packages/graphiql-react/src/monaco-editor.ts
index 3207d3d2ec7..b2d52a854a0 100644
--- a/packages/graphiql-react/src/monaco-editor.ts
+++ b/packages/graphiql-react/src/monaco-editor.ts
@@ -1 +1,7 @@
-export * from 'monaco-graphql/esm/monaco-editor';
+/**
+ * Can't use `monaco-graphql/esm/monaco-editor` due error in esm.sh example:
+ * Uncaught TypeError: Cannot read properties of undefined (reading 'jsonDefaults')
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-restricted-imports
+export * from 'monaco-editor';
diff --git a/packages/graphiql-react/src/stores/execution.ts b/packages/graphiql-react/src/stores/execution.ts
index 5ac41909afc..f1d1b0805f8 100644
--- a/packages/graphiql-react/src/stores/execution.ts
+++ b/packages/graphiql-react/src/stores/execution.ts
@@ -344,7 +344,7 @@ async function tryParseJsonObject(
return parsed;
}
-type IncrementalResult = {
+interface IncrementalResult {
data?: Record | null;
errors?: ReadonlyArray;
extensions?: Record;
@@ -360,7 +360,7 @@ type IncrementalResult = {
}>;
id?: string;
subPath?: ReadonlyArray;
-};
+}
const pathsMap = new WeakMap<
ExecutionResult,
@@ -368,9 +368,9 @@ const pathsMap = new WeakMap<
>();
/**
- * @param executionResult The complete execution result object which will be
+ * @param executionResult - The complete execution result object which will be
* mutated by merging the contents of the incremental result.
- * @param incrementalResult The incremental result that will be merged into the
+ * @param incrementalResult - The incremental result that will be merged into the
* complete execution result.
*/
function mergeIncrementalResult(
diff --git a/packages/graphiql-react/src/stores/schema.ts b/packages/graphiql-react/src/stores/schema.ts
index 7dd3fd1c322..e415a2e95fd 100644
--- a/packages/graphiql-react/src/stores/schema.ts
+++ b/packages/graphiql-react/src/stores/schema.ts
@@ -255,7 +255,7 @@ export interface SchemaProps extends IntrospectionArgs {
schema?: GraphQLSchema | IntrospectionQuery | null;
}
-type IntrospectionArgs = {
+interface IntrospectionArgs {
/**
* Can be used to set the equally named option for introspecting a GraphQL
* server.
@@ -275,7 +275,7 @@ type IntrospectionArgs = {
* @see {@link https://github.com/graphql/graphql-js/blob/main/src/utilities/getIntrospectionQuery.ts|Utility for creating the introspection query}
*/
schemaDescription?: boolean;
-};
+}
function generateIntrospectionQuery({
inputValueDeprecation,
diff --git a/packages/graphiql-react/src/stores/storage.ts b/packages/graphiql-react/src/stores/storage.ts
index e7f920e611f..98ddcccf7a7 100644
--- a/packages/graphiql-react/src/stores/storage.ts
+++ b/packages/graphiql-react/src/stores/storage.ts
@@ -3,11 +3,11 @@ import { FC, ReactElement, ReactNode, useEffect } from 'react';
import { createStore } from 'zustand';
import { createBoundedUseStore } from '../utility';
-type StorageStoreType = {
+interface StorageStoreType {
storage: StorageAPI;
-};
+}
-type StorageStoreProps = {
+interface StorageStoreProps {
children: ReactNode;
/**
* Provide a custom storage API.
@@ -16,7 +16,7 @@ type StorageStoreProps = {
* for details on the required interface.
*/
storage?: Storage;
-};
+}
export const storageStore = createStore(() => ({
storage: null!,
diff --git a/packages/graphiql-react/src/stores/theme.ts b/packages/graphiql-react/src/stores/theme.ts
index 0780a807bf1..c4670ef7d83 100644
--- a/packages/graphiql-react/src/stores/theme.ts
+++ b/packages/graphiql-react/src/stores/theme.ts
@@ -16,15 +16,15 @@ type MonacoTheme =
| (typeof EDITOR_THEME)[keyof typeof EDITOR_THEME]
| ({} & string);
-type ThemeStoreType = {
+interface ThemeStoreType {
theme: Theme;
/**
* Set a new theme
*/
setTheme: (newTheme: Theme) => void;
-};
+}
-type ThemeStoreProps = {
+interface ThemeStoreProps {
children: ReactNode;
/**
* @default null
@@ -38,7 +38,7 @@ type ThemeStoreProps = {
dark: MonacoTheme;
light: MonacoTheme;
};
-};
+}
export const themeStore = createStore(set => ({
theme: null,
diff --git a/packages/graphiql-react/src/types.test-d.ts b/packages/graphiql-react/src/types.test-d.ts
index b21c4366e55..a05b470ac75 100644
--- a/packages/graphiql-react/src/types.test-d.ts
+++ b/packages/graphiql-react/src/types.test-d.ts
@@ -8,10 +8,10 @@ import { AllSlices } from './types';
describe('Types', () => {
it('should not have conflicting types', () => {
- type OverlapError = {
+ interface OverlapError {
ERROR: 'Conflicting keys found';
CONFLICT_KEYS: K;
- };
+ }
type MergeWithoutOverlap = keyof A & keyof B extends never
? A & B
diff --git a/packages/graphiql-react/src/utility/create-editor.ts b/packages/graphiql-react/src/utility/create-editor.ts
index fb168d5ddd4..59afd9222a7 100644
--- a/packages/graphiql-react/src/utility/create-editor.ts
+++ b/packages/graphiql-react/src/utility/create-editor.ts
@@ -21,15 +21,8 @@ export const onEditorContainerKeyDown: KeyboardEventHandler<
export function getOrCreateModel({ uri, value }: { uri: Uri; value: string }) {
const model = monacoEditor.getModel(uri);
- if (model) {
- // eslint-disable-next-line no-console
- console.info('✅ Model', uri.path, 'is already created');
- return model;
- }
- // eslint-disable-next-line no-console
- console.info('🚀 Model', uri.path, "isn't yet created, creating...");
const language = uri.path.split('.').at(-1)!;
- return monacoEditor.createModel(value, language, uri);
+ return model ?? monacoEditor.createModel(value, language, uri);
}
const colors = {
diff --git a/packages/graphiql-react/src/utility/resize.ts b/packages/graphiql-react/src/utility/resize.ts
index 009890b4368..9052c8d6bf5 100644
--- a/packages/graphiql-react/src/utility/resize.ts
+++ b/packages/graphiql-react/src/utility/resize.ts
@@ -4,7 +4,7 @@ import { debounce } from './debounce';
type ResizableElement = 'first' | 'second';
-type UseDragResizeArgs = {
+interface UseDragResizeArgs {
/**
* Set the default sizes for the two resizable halves by passing their ratio
* (first divided by second).
@@ -19,12 +19,14 @@ type UseDragResizeArgs = {
* Choose one of the two halves that should initially be hidden.
*/
initiallyHidden?: ResizableElement;
+
/**
* Invoked when the visibility of one of the halves changes.
* @param hiddenElement The element that is now hidden after the change
* (`null` if both are visible).
*/
onHiddenElementChange?(hiddenElement: ResizableElement | null): void;
+
/**
* The minimum width in pixels for the first half. If it is resized to a
* width smaller than this threshold, the half will be hidden.
@@ -42,7 +44,7 @@ type UseDragResizeArgs = {
* is available).
*/
storageKey?: string;
-};
+}
export function useDragResize({
defaultSizeRelation = 1,
diff --git a/packages/graphiql-react/src/utility/tabs.ts b/packages/graphiql-react/src/utility/tabs.ts
index e8cd9c82140..4b8d3f5812f 100644
--- a/packages/graphiql-react/src/utility/tabs.ts
+++ b/packages/graphiql-react/src/utility/tabs.ts
@@ -2,7 +2,7 @@
import { storageStore } from '../stores';
-export type TabDefinition = {
+export interface TabDefinition {
/**
* The contents of the query editor of this tab.
*/
@@ -15,12 +15,12 @@ export type TabDefinition = {
* The contents of the headers editor of this tab.
*/
headers?: string | null;
-};
+}
/**
* This object describes the state of a single tab.
*/
-export type TabState = TabDefinition & {
+export interface TabState extends TabDefinition {
/**
* A GUID value generated when the tab was created.
*/
@@ -44,7 +44,7 @@ export type TabState = TabDefinition & {
* The contents of the response editor of this tab.
*/
response: string | null;
-};
+}
/**
* This object describes the state of all tabs.
diff --git a/packages/graphiql/cypress/e2e/incremental-delivery.cy.ts b/packages/graphiql/cypress/e2e/incremental-delivery.cy.ts
index b4669a3be41..7215ba9f602 100644
--- a/packages/graphiql/cypress/e2e/incremental-delivery.cy.ts
+++ b/packages/graphiql/cypress/e2e/incremental-delivery.cy.ts
@@ -1,12 +1,4 @@
-import { version } from 'graphql';
-
-let describeOrSkip: Mocha.SuiteFunction | Mocha.PendingSuiteFunction = describe;
-
-if (parseInt(version, 10) < 17) {
- describeOrSkip = describe.skip;
-}
-
-describeOrSkip('IncrementalDelivery support via fetcher', () => {
+describe('IncrementalDelivery support via fetcher', () => {
describe('When operation contains @stream', () => {
const testStreamQuery = /* GraphQL */ `
query StreamQuery($delay: Int) {
diff --git a/packages/graphiql/cypress/support/commands.ts b/packages/graphiql/cypress/support/commands.ts
index 0a03a48808b..0ca3ce67c35 100644
--- a/packages/graphiql/cypress/support/commands.ts
+++ b/packages/graphiql/cypress/support/commands.ts
@@ -8,13 +8,13 @@
///
-type Op = {
+interface Op {
query: string;
variables?: Record;
variablesString?: string;
headersString?: string;
response?: Record;
-};
+}
declare namespace Cypress {
type MockResult =
| { data: any }
diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json
index 9477b301c55..88aae0d771f 100644
--- a/packages/graphiql/package.json
+++ b/packages/graphiql/package.json
@@ -83,5 +83,9 @@
"vite": "^6.3.4",
"vite-plugin-dts": "^4.5.3",
"vitest-canvas-mock": "0.3.3"
+ },
+ "browser": {
+ "//": "esm.sh polyfills `process`, we don't need it",
+ "process": false
}
}
diff --git a/packages/graphiql/src/GraphiQL.spec.tsx b/packages/graphiql/src/GraphiQL.spec.tsx
index 483a261cac5..db20e556bc2 100644
--- a/packages/graphiql/src/GraphiQL.spec.tsx
+++ b/packages/graphiql/src/GraphiQL.spec.tsx
@@ -49,8 +49,10 @@ describe('GraphiQL', () => {
spy.mockRestore();
});
- it('should construct correctly with fetcher', async () => {
- act(() => {
+ it('should construct correctly with fetcher', () => {
+ // `return` fix error:
+ // Warning: An update to GraphiQLInterface inside a test was not wrapped in act(...).
+ return act(() => {
expect(() => render()).not.toThrow();
});
});
diff --git a/packages/graphiql/src/GraphiQL.tsx b/packages/graphiql/src/GraphiQL.tsx
index 6b45c93fbf1..26a1d5f0f77 100644
--- a/packages/graphiql/src/GraphiQL.tsx
+++ b/packages/graphiql/src/GraphiQL.tsx
@@ -40,6 +40,7 @@ import {
EditorProps,
cn,
VisuallyHidden,
+ KEY_MAP,
} from '@graphiql/react';
import { HistoryStore, HISTORY_PLUGIN } from '@graphiql/plugin-history';
import {
@@ -196,6 +197,13 @@ const TAB_CLASS_PREFIX = 'graphiql-session-tab-';
type ButtonHandler = MouseEventHandler;
+const LABEL = {
+ refetchSchema: `Re-fetch GraphQL schema (${KEY_MAP.refetchSchema.key})`,
+ shortCutDialog: 'Open short keys dialog',
+ settingsDialogs: 'Open settings dialog',
+ newTab: 'New tab',
+};
+
const GraphiQLInterface: FC = ({
forcedTheme,
isHeadersEditorEnabled = true,
@@ -309,8 +317,8 @@ const GraphiQLInterface: FC = ({
'settings' | 'short-keys' | null
>(null);
const [clearStorageStatus, setClearStorageStatus] = useState<
- 'success' | 'error' | null
- >(null);
+ 'success' | 'error' | undefined
+ >();
const { logo, toolbar, footer, children } = Children.toArray(
$children,
@@ -412,7 +420,7 @@ const GraphiQLInterface: FC = ({
function handleOpenSettingsDialog(isOpen: boolean) {
if (!isOpen) {
setShowDialog(null);
- setClearStorageStatus(null);
+ setClearStorageStatus(undefined);
}
}
@@ -453,12 +461,12 @@ const GraphiQLInterface: FC = ({
);
})}
-
+
= ({
/>
-
+
-
+
@@ -603,7 +611,7 @@ const GraphiQLInterface: FC = ({