+
-
- {isRunning ? (
-
- ) : (
-
- )}
-
+ {isRunning ? : }
{hasOptions && optionsOpen ? (
-
+
{operations.map((operation, i) => {
const opName = operation.name
? operation.name.value
: ``;
return (
- setHighlight(operation)}
@@ -100,10 +98,10 @@ export function ExecuteButton() {
run(operation.name?.value);
}}>
{opName}
-
+
);
})}
-
+
) : null}
);
diff --git a/packages/graphiql-react/src/toolbar/index.ts b/packages/graphiql-react/src/toolbar/index.ts
new file mode 100644
index 00000000000..a1e27139637
--- /dev/null
+++ b/packages/graphiql-react/src/toolbar/index.ts
@@ -0,0 +1,2 @@
+export * from './button';
+export * from './execute';
diff --git a/packages/graphiql-react/src/ui/dropdown.css b/packages/graphiql-react/src/ui/dropdown.css
new file mode 100644
index 00000000000..400bb09bd79
--- /dev/null
+++ b/packages/graphiql-react/src/ui/dropdown.css
@@ -0,0 +1,30 @@
+.graphiql-dropdown {
+ background-color: var(--color-neutral-0);
+ box-shadow: var(--box-shadow);
+ border-radius: var(--border-radius-8);
+ margin: 0;
+ max-width: 250px;
+ min-width: 100px;
+ padding: 0;
+ position: absolute;
+ z-index: 1;
+}
+
+.graphiql-dropdown-item {
+ border-radius: var(--border-radius-4);
+ cursor: pointer;
+ margin: var(--px-4);
+ overflow: hidden;
+ padding: var(--px-6) var(--px-8);
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.graphiql-dropdown-item:active,
+.graphiql-dropdown-item:hover {
+ background-color: var(--color-neutral-7);
+}
+
+.graphiql-dropdown-item:not(:first-child) {
+ margin-top: 0;
+}
diff --git a/packages/graphiql-react/src/ui/dropdown.tsx b/packages/graphiql-react/src/ui/dropdown.tsx
new file mode 100644
index 00000000000..043eb7a5d26
--- /dev/null
+++ b/packages/graphiql-react/src/ui/dropdown.tsx
@@ -0,0 +1,21 @@
+import './dropdown.css';
+
+export function Dropdown(props: JSX.IntrinsicElements['ul']) {
+ return (
+
+ );
+}
+
+Dropdown.Item = DropdownItem;
+
+function DropdownItem(props: JSX.IntrinsicElements['li']) {
+ return (
+
+ );
+}
diff --git a/packages/graphiql-react/src/ui/index.ts b/packages/graphiql-react/src/ui/index.ts
index eaf5eea7f1c..7ef3616b216 100644
--- a/packages/graphiql-react/src/ui/index.ts
+++ b/packages/graphiql-react/src/ui/index.ts
@@ -1 +1,2 @@
export * from './button';
+export * from './dropdown';
diff --git a/packages/graphiql/__mocks__/@graphiql/react.tsx b/packages/graphiql/__mocks__/@graphiql/react.tsx
index 06c75ee95fa..775af04683e 100644
--- a/packages/graphiql/__mocks__/@graphiql/react.tsx
+++ b/packages/graphiql/__mocks__/@graphiql/react.tsx
@@ -1,33 +1,5 @@
import {
- ChevronDownIcon,
- ChevronUpIcon,
- EditorContext,
- EditorContextProvider,
- ExecutionContext,
- ExecutionContextProvider,
- ExplorerContext,
- ExplorerContextProvider,
- HistoryContext,
- HistoryContextProvider,
- ImagePreview,
- onHasCompletion,
- SchemaContext,
- SchemaContextProvider,
- StorageContext,
- StorageContextProvider,
- UnStyledButton,
- useAutoCompleteLeafs,
- useCopyQuery,
- useDragResize,
useEditorContext,
- useExecutionContext,
- useExplorerContext,
- useHistoryContext,
- useMergeQuery,
- usePrettifyEditors,
- useSchemaContext,
- useSelectHistoryItem,
- useStorageContext,
HeaderEditor as _HeaderEditor,
QueryEditor as _QueryEditor,
ResponseEditor as _ResponseEditor,
@@ -37,30 +9,16 @@ import {
useResponseEditor as _useResponseEditor,
useVariableEditor as _useVariableEditor,
} from '@graphiql/react';
-import type {
- EditorContextType,
- ExecutionContextType,
- ExplorerContextType,
- ExplorerFieldDef,
- ExplorerNavStack,
- ExplorerNavStackItem,
- HistoryContextType,
- ResponseTooltipType,
- SchemaContextType,
- StorageContextType,
- TabsState,
- UseHeaderEditorArgs,
- UseResponseEditorArgs,
- UseQueryEditorArgs,
- UseVariableEditorArgs,
-} from '@graphiql/react';
import React, { useEffect, useRef, useState } from 'react';
export {
ChevronDownIcon,
ChevronUpIcon,
+ CopyIcon,
+ Dropdown,
EditorContext,
EditorContextProvider,
+ ExecuteButton,
ExecutionContext,
ExecutionContextProvider,
ExplorerContext,
@@ -69,10 +27,15 @@ export {
HistoryContextProvider,
ImagePreview,
onHasCompletion,
+ MergeIcon,
+ PlayIcon,
+ PrettifyIcon,
SchemaContext,
SchemaContextProvider,
+ StopIcon,
StorageContext,
StorageContextProvider,
+ ToolbarButton,
UnStyledButton,
useAutoCompleteLeafs,
useCopyQuery,
@@ -86,7 +49,7 @@ export {
useSchemaContext,
useSelectHistoryItem,
useStorageContext,
-};
+} from '@graphiql/react';
export type {
EditorContextType,
@@ -104,7 +67,7 @@ export type {
UseResponseEditorArgs,
UseQueryEditorArgs,
UseVariableEditorArgs,
-};
+} from '@graphiql/react';
type Name = 'query' | 'variable' | 'header' | 'response';
diff --git a/packages/graphiql/cypress/integration/tabs.spec.ts b/packages/graphiql/cypress/integration/tabs.spec.ts
index 848af058ffb..5112c8e05ce 100644
--- a/packages/graphiql/cypress/integration/tabs.spec.ts
+++ b/packages/graphiql/cypress/integration/tabs.spec.ts
@@ -10,7 +10,7 @@ describe('Tabs', () => {
cy.get('#session-tab-0').should('have.text', '
');
// Run the query
- cy.get('.execute-button').click().wait(500);
+ cy.clickExecuteQuery().wait(500);
// Open a new tab
cy.get('.tab-add').click();
@@ -33,7 +33,7 @@ describe('Tabs', () => {
.type('{"someHeader":"someValue"', { force: true });
// Run the query
- cy.get('.execute-button').click().wait(500);
+ cy.clickExecuteQuery().wait(500);
// Switch back to the first tab
cy.get('#session-tab-0').click();
diff --git a/packages/graphiql/cypress/support/commands.ts b/packages/graphiql/cypress/support/commands.ts
index b6671d59ae4..184e9546edc 100644
--- a/packages/graphiql/cypress/support/commands.ts
+++ b/packages/graphiql/cypress/support/commands.ts
@@ -48,7 +48,7 @@ Cypress.Commands.add('getCy', cyName => {
});
Cypress.Commands.add('clickExecuteQuery', () => {
- return cy.get('.execute-button').click();
+ return cy.get('.graphiql-execute-button').click();
});
Cypress.Commands.add('clickPrettify', () => {
diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx
index 7bb1c6f3eb2..9650be6bc8d 100644
--- a/packages/graphiql/src/components/GraphiQL.tsx
+++ b/packages/graphiql/src/components/GraphiQL.tsx
@@ -24,16 +24,21 @@ import {
import {
ChevronDownIcon,
ChevronUpIcon,
+ CopyIcon,
EditorContextProvider,
+ ExecuteButton,
ExecutionContextProvider,
ExecutionContextType,
ExplorerContextProvider,
HeaderEditor,
HistoryContextProvider,
+ MergeIcon,
+ PrettifyIcon,
QueryEditor,
ResponseEditor,
SchemaContextProvider,
StorageContextProvider,
+ ToolbarButton,
UnStyledButton,
useAutoCompleteLeafs,
useCopyQuery,
@@ -59,9 +64,7 @@ import type {
KeyMap,
} from '@graphiql/react';
-import { ExecuteButton } from './ExecuteButton';
-import { ToolbarButton } from './ToolbarButton';
-import { ToolbarGroup } from './ToolbarGroup';
+import { ToolbarButton as LegacyToolbarButton } from './ToolbarButton';
import { ToolbarMenu, ToolbarMenuItem } from './ToolbarMenu';
import { DocExplorer } from './DocExplorer';
import { QueryHistory } from './QueryHistory';
@@ -421,13 +424,6 @@ export class GraphiQL extends React.Component {
static HeaderEditor = HeaderEditor;
static ResultViewer = ResponseEditor;
- // Add a button to the Toolbar.
- static Button = ToolbarButton;
- static ToolbarButton = ToolbarButton; // Don't break existing API.
-
- // Add a group of buttons to the Toolbar
- static Group = ToolbarGroup;
-
// Add a menu of items to the Toolbar.
static Menu = ToolbarMenu;
static MenuItem = ToolbarMenuItem;
@@ -644,46 +640,35 @@ class GraphiQLWithContext extends React.Component<
const toolbar = find(children, child =>
isChildComponentType(child, GraphiQL.Toolbar),
) || (
-
+ <>
{
this.props.prettify();
}}
title="Prettify Query (Shift-Ctrl-P)"
- label="Prettify"
- />
+ aria-label="Prettify">
+
+
{
this.props.merge();
}}
title="Merge Query (Shift-Ctrl-M)"
- label="Merge"
- />
+ aria-label="Merge">
+
+
{
this.props.copy();
}}
title="Copy Query (Shift-Ctrl-C)"
- label="Copy"
- />
- this.props.historyContext?.toggle()}
- title={
- this.props.historyContext?.isVisible
- ? 'Hide History'
- : 'Show History'
- }
- label="History"
- />
- this.props.schemaContext.introspect()}
- title="Fetch GraphQL schema using introspection (Shift-Ctrl-R)"
- label="Introspect"
- />
+ aria-label="Copy">
+
+
{this.props.toolbar?.additionalContent
? this.props.toolbar.additionalContent
: null}
-
+ >
);
const footer = find(children, child =>
@@ -707,8 +692,20 @@ class GraphiQLWithContext extends React.Component<
{this.props.beforeTopBarContent}
{logo}
-
- {toolbar}
+ this.props.historyContext?.toggle()}
+ title={
+ this.props.historyContext?.isVisible
+ ? 'Hide History'
+ : 'Show History'
+ }
+ label="History"
+ />
+ this.props.schemaContext.introspect()}
+ title="Fetch GraphQL schema using introspection (Shift-Ctrl-R)"
+ label="Introspect"
+ />
{this.props.explorerContext &&
!this.props.explorerContext.isVisible && (
@@ -768,21 +765,32 @@ class GraphiQLWithContext extends React.Component<
- {
- if (this.props.docResize.hiddenElement === 'second') {
- this.props.docResize.setHiddenElement(null);
- }
- }}
- onCopyQuery={this.props.onCopyQuery}
- onEdit={this.props.onEditQuery}
- onEditOperationName={this.props.onEditOperationName}
- readOnly={this.props.readOnly}
- validationRules={this.props.validationRules}
- />
+
+
{
+ if (
+ this.props.docResize.hiddenElement === 'second'
+ ) {
+ this.props.docResize.setHiddenElement(null);
+ }
+ }}
+ onCopyQuery={this.props.onCopyQuery}
+ onEdit={this.props.onEditQuery}
+ onEditOperationName={this.props.onEditOperationName}
+ readOnly={this.props.readOnly}
+ validationRules={this.props.validationRules}
+ />
+
+
+ {toolbar}
+
+