{this.props.explorerContext ? (
- {
- if (this.props.explorerContext?.isVisible) {
- this.props.explorerContext?.hide();
- this.props.pluginResize.setHiddenElement('first');
- } else {
- this.props.explorerContext?.show();
- this.props.pluginResize.setHiddenElement(null);
- if (this.props.historyContext?.isVisible) {
- this.props.historyContext.hide();
- }
- }
- }}
- title={
+
-
-
- ) : null}
- {this.props.historyContext ? (
- {
- if (!this.props.historyContext) {
- return;
+ {
if (this.props.explorerContext?.isVisible) {
- this.props.explorerContext.hide();
+ this.props.explorerContext?.hide();
+ this.props.pluginResize.setHiddenElement('first');
+ } else {
+ this.props.explorerContext?.show();
+ this.props.pluginResize.setHiddenElement(null);
+ if (this.props.historyContext?.isVisible) {
+ this.props.historyContext.hide();
+ }
}
+ }}
+ aria-label={
+ this.props.explorerContext.isVisible
+ ? 'Hide Documentation Explorer'
+ : 'Show Documentation Explorer'
}
- }}
- title={
+ >
+
+
+
+ ) : null}
+ {this.props.historyContext ? (
+
-
-
+ {
+ if (!this.props.historyContext) {
+ return;
+ }
+ this.props.historyContext.toggle();
+ if (this.props.historyContext.isVisible) {
+ this.props.pluginResize.setHiddenElement('first');
+ } else {
+ this.props.pluginResize.setHiddenElement(null);
+ if (this.props.explorerContext?.isVisible) {
+ this.props.explorerContext.hide();
+ }
+ }
+ }}
+ aria-label={
+ this.props.historyContext.isVisible
+ ? 'Hide History'
+ : 'Show History'
+ }
+ >
+
+
+
) : null}
- this.props.schemaContext.introspect()}
- >
-
-
- {
- this.setState({ showShortKeys: true });
- }}
- >
-
-
- {
- this.setState({ showSettings: true });
- }}
- >
-
-
+
+ this.props.schemaContext.introspect()}
+ aria-label="Re-fetch GraphQL schema"
+ >
+
+
+
+
+ {
+ this.setState({ showShortKeys: true });
+ }}
+ aria-label="Open short keys dialog"
+ >
+
+
+
+
+ {
+ this.setState({ showSettings: true });
+ }}
+ aria-label="Open settings dialog"
+ >
+
+
+
@@ -838,7 +861,6 @@ class GraphiQLWithContext extends React.Component<
isActive={
index === this.props.editorContext.activeTabIndex
}
- title={tab.title}
>
))}
+
+ {
+ this.props.editorContext.addTab();
+ }}
+ aria-label="Add tab"
+ >
+
+
+
+ >
+ ) : null}
+
+
+ {this.props.editorContext.tabs.length === 1 ? (
+
{
this.props.editorContext.addTab();
}}
+ aria-label="Add tab"
>
-
+
- >
- ) : null}
-
-
- {this.props.editorContext.tabs.length === 1 ? (
-
{
- this.props.editorContext.addTab();
- }}
- >
-
-
+
) : null}
{logo}
@@ -981,24 +1009,45 @@ class GraphiQLWithContext extends React.Component<
) : null}
-
{
- this.props.editorToolsResize.setHiddenElement(
- this.props.editorToolsResize.hiddenElement ===
- 'second'
- ? null
- : 'second',
- );
- }}
+
- {this.props.editorToolsResize.hiddenElement ===
- 'second' ? (
-
- ) : (
-
- )}
-
+
{
+ this.props.editorToolsResize.setHiddenElement(
+ this.props.editorToolsResize.hiddenElement ===
+ 'second'
+ ? null
+ : 'second',
+ );
+ }}
+ aria-label={
+ this.props.editorToolsResize.hiddenElement ===
+ 'second'
+ ? 'Show editor tools'
+ : 'Hide editor tools'
+ }
+ >
+ {this.props.editorToolsResize.hiddenElement ===
+ 'second' ? (
+
+ ) : (
+
+ )}
+
+
diff --git a/packages/graphiql/src/components/ToolbarMenu.tsx b/packages/graphiql/src/components/ToolbarMenu.tsx
deleted file mode 100644
index 2693a8ab1ac..00000000000
--- a/packages/graphiql/src/components/ToolbarMenu.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Copyright (c) 2021 GraphQL Contributors.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import React, { FC, MouseEventHandler, ReactNode } from 'react';
-
-type ToolbarMenuProps = {
- title: string;
- label: string;
- children?: ReactNode;
-};
-
-type ToolbarMenuState = {
- visible: boolean;
-};
-
-/**
- * ToolbarMenu
- *
- * A menu style button to use within the Toolbar.
- */
-export class ToolbarMenu extends React.Component<
- ToolbarMenuProps,
- ToolbarMenuState
-> {
- private _node: HTMLAnchorElement | null = null;
- private _listener: this['handleClick'] | null = null;
-
- constructor(props: ToolbarMenuProps) {
- super(props);
- this.state = { visible: false };
- }
-
- componentWillUnmount() {
- this._release();
- }
-
- render() {
- const visible = this.state.visible;
- return (
-
{
- if (node) {
- this._node = node;
- }
- }}
- title={this.props.title}
- >
- {this.props.label}
-
-
- {this.props.children}
-
-
- );
- }
-
- _subscribe() {
- if (!this._listener) {
- this._listener = this.handleClick.bind(this);
- document.addEventListener('click', this._listener);
- }
- }
-
- _release() {
- if (this._listener) {
- document.removeEventListener('click', this._listener);
- this._listener = null;
- }
- }
-
- handleClick(e: MouseEvent | React.MouseEvent
) {
- if (this._node !== e.target) {
- e.preventDefault();
- this.setState({ visible: false });
- this._release();
- }
- }
-
- handleOpen: MouseEventHandler = e => {
- preventDefault(e);
- this.setState({ visible: true });
- this._subscribe();
- };
-}
-
-type ToolbarMenuItemProps = {
- onSelect: () => void;
- title: string;
- label: string;
-};
-
-export const ToolbarMenuItem: FC = ({
- onSelect,
- title,
- label,
-}) => {
- return (
- {
- e.currentTarget.className = 'hover';
- }}
- onMouseOut={e => {
- e.currentTarget.className = '';
- }}
- onMouseDown={preventDefault}
- onMouseUp={onSelect}
- title={title}
- >
- {label}
-
- );
-};
-
-function preventDefault(e: MouseEvent | React.MouseEvent) {
- e.preventDefault();
-}
diff --git a/packages/graphiql/src/components/ToolbarSelect.tsx b/packages/graphiql/src/components/ToolbarSelect.tsx
deleted file mode 100644
index 2c769902748..00000000000
--- a/packages/graphiql/src/components/ToolbarSelect.tsx
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * Copyright (c) 2021 GraphQL Contributors.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import React, { MouseEventHandler, ReactNode } from 'react';
-
-type ToolbarSelectProps = {
- title?: string;
- label?: string;
- onSelect?: (selection: string) => void;
- children?: ReactNode;
-};
-
-type ToolbarSelectState = {
- visible: boolean;
-};
-
-type HasProps = T extends { props: any } ? T : never;
-
-function hasProps(
- child: Child,
-): child is HasProps {
- if (!child || typeof child !== 'object' || !('props' in child)) {
- return false;
- }
- return true;
-}
-
-/**
- * ToolbarSelect
- *
- * A select-option style button to use within the Toolbar.
- *
- */
-
-export class ToolbarSelect extends React.Component<
- ToolbarSelectProps,
- ToolbarSelectState
-> {
- private _node: HTMLAnchorElement | null = null;
- private _listener: ((this: Document, ev: MouseEvent) => any) | null = null;
- constructor(props: ToolbarSelectProps) {
- super(props);
- this.state = { visible: false };
- }
-
- componentWillUnmount() {
- this._release();
- }
-
- render() {
- let selectedChild: HasProps | undefined;
- const visible = this.state.visible;
- const optionChildren = React.Children.map(
- this.props.children,
- (child, i) => {
- if (!hasProps(child)) {
- return null;
- }
- if (!selectedChild || child.props.selected) {
- selectedChild = child;
- }
- const onChildSelect =
- child.props.onSelect ||
- this.props.onSelect?.bind(null, child.props.value, i);
- return (
-
- );
- },
- );
- return (
- {
- this._node = node;
- }}
- title={this.props.title}
- >
- {selectedChild?.props.label}
-
-
-
- );
- }
-
- _subscribe() {
- if (!this._listener) {
- this._listener = this.handleClick.bind(this);
- document.addEventListener('click', this._listener);
- }
- }
-
- _release() {
- if (this._listener) {
- document.removeEventListener('click', this._listener);
- this._listener = null;
- }
- }
-
- handleClick(e: MouseEvent) {
- if (this._node !== e.target) {
- preventDefault(e);
- this.setState({ visible: false });
- this._release();
- }
- }
-
- handleOpen = (e: React.MouseEvent) => {
- preventDefault(e);
- this.setState({ visible: true });
- this._subscribe();
- };
-}
-
-type ToolbarSelectOptionProps = {
- onSelect: MouseEventHandler;
- label: string;
- selected: boolean;
- value?: any;
-};
-
-export function ToolbarSelectOption({
- onSelect,
- label,
- selected,
-}: ToolbarSelectOptionProps) {
- return (
- {
- e.currentTarget.className = 'hover';
- }}
- onMouseOut={e => {
- e.currentTarget.className = '';
- }}
- onMouseDown={preventDefault}
- onMouseUp={onSelect}
- >
- {label}
- {selected && (
-
- )}
-
- );
-}
-
-function preventDefault(e: any) {
- e.preventDefault();
-}
diff --git a/packages/graphiql/src/components/__tests__/GraphiQL.spec.tsx b/packages/graphiql/src/components/__tests__/GraphiQL.spec.tsx
index 06635b8a6fb..a770bf1dd70 100644
--- a/packages/graphiql/src/components/__tests__/GraphiQL.spec.tsx
+++ b/packages/graphiql/src/components/__tests__/GraphiQL.spec.tsx
@@ -173,7 +173,7 @@ describe('GraphiQL', () => {
});
it('will save history item even when history panel is closed', () => {
- const { getByTitle, container } = render(
+ const { getByLabelText, container } = render(
{
fetcher={noOpFetcher}
/>,
);
- fireEvent.click(getByTitle('Execute Query (Ctrl-Enter)'));
- fireEvent.click(getByTitle('Show History'));
+ fireEvent.click(getByLabelText('Execute query (Ctrl-Enter)'));
+ fireEvent.click(getByLabelText('Show History'));
expect(
container.querySelectorAll('.graphiql-history-items li'),
).toHaveLength(1);
});
it('adds a history item when the execute query function button is clicked', () => {
- const { getByTitle, container } = render(
+ const { getByLabelText, container } = render(
{
fetcher={noOpFetcher}
/>,
);
- fireEvent.click(getByTitle('Show History'));
- fireEvent.click(getByTitle('Execute Query (Ctrl-Enter)'));
+ fireEvent.click(getByLabelText('Show History'));
+ fireEvent.click(getByLabelText('Execute query (Ctrl-Enter)'));
expect(
container.querySelectorAll('.graphiql-history-items li'),
).toHaveLength(1);
});
it('will not save invalid queries', () => {
- const { getByTitle, container } = render(
+ const { getByLabelText, container } = render(
,
);
- fireEvent.click(getByTitle('Show History'));
- fireEvent.click(getByTitle('Execute Query (Ctrl-Enter)'));
+ fireEvent.click(getByLabelText('Show History'));
+ fireEvent.click(getByLabelText('Execute query (Ctrl-Enter)'));
expect(
container.querySelectorAll('.graphiql-history-items li'),
).toHaveLength(0);
});
it('will save if there was not a previously saved query', () => {
- const { getByTitle, container } = render(
+ const { getByLabelText, container } = render(
{
headers={mockHeaders1}
/>,
);
- fireEvent.click(getByTitle('Show History'));
- fireEvent.click(getByTitle('Execute Query (Ctrl-Enter)'));
+ fireEvent.click(getByLabelText('Show History'));
+ fireEvent.click(getByLabelText('Execute query (Ctrl-Enter)'));
expect(
container.querySelectorAll('.graphiql-history-items li'),
).toHaveLength(1);
});
- it('will not save a query if the query is the same as previous query', () => {
- const { getByTitle, container } = render(
+ it('will not save a query if the query is the same as previous query', async () => {
+ const { getByLabelText, findByLabelText, container } = render(
{
headers={mockHeaders1}
/>,
);
- fireEvent.click(getByTitle('Show History'));
- fireEvent.click(getByTitle('Execute Query (Ctrl-Enter)'));
+ fireEvent.click(getByLabelText('Show History'));
+ fireEvent.click(getByLabelText('Execute query (Ctrl-Enter)'));
expect(
container.querySelectorAll('.graphiql-history-items li'),
).toHaveLength(1);
- fireEvent.click(getByTitle('Execute Query (Ctrl-Enter)'));
+ fireEvent.click(await findByLabelText('Execute query (Ctrl-Enter)'));
expect(
container.querySelectorAll('.graphiql-history-items li'),
).toHaveLength(1);
});
it('will save if new query is different than previous query', async () => {
- const { getByTitle, container } = render(
+ const { getByLabelText, container } = render(
{
/>,
);
await wait();
- fireEvent.click(getByTitle('Show History'));
- const executeQueryButton = getByTitle('Execute Query (Ctrl-Enter)');
+ fireEvent.click(getByLabelText('Show History'));
+ const executeQueryButton = getByLabelText('Execute query (Ctrl-Enter)');
fireEvent.click(executeQueryButton);
expect(container.querySelectorAll('.graphiql-history-item')).toHaveLength(
1,
@@ -290,7 +290,7 @@ describe('GraphiQL', () => {
});
it('will save query if variables are different', async () => {
- const { getByTitle, container } = render(
+ const { getByLabelText, container } = render(
{
/>,
);
await wait();
- fireEvent.click(getByTitle('Show History'));
- const executeQueryButton = getByTitle('Execute Query (Ctrl-Enter)');
+ fireEvent.click(getByLabelText('Show History'));
+ const executeQueryButton = getByLabelText('Execute query (Ctrl-Enter)');
fireEvent.click(executeQueryButton);
expect(container.querySelectorAll('.graphiql-history-item')).toHaveLength(
1,
@@ -322,7 +322,7 @@ describe('GraphiQL', () => {
});
it('will save query if headers are different', async () => {
- const { getByTitle, getByText, container } = render(
+ const { getByLabelText, getByText, container } = render(
{
);
await wait();
- fireEvent.click(getByTitle('Show History'));
- const executeQueryButton = getByTitle('Execute Query (Ctrl-Enter)');
+ fireEvent.click(getByLabelText('Show History'));
+ const executeQueryButton = getByLabelText('Execute query (Ctrl-Enter)');
fireEvent.click(executeQueryButton);
expect(container.querySelectorAll('.graphiql-history-item')).toHaveLength(
1,
@@ -570,7 +570,7 @@ describe('GraphiQL', () => {
const { container } = render();
fireEvent.click(
- container.querySelector('[title="Show Documentation Explorer"]'),
+ container.querySelector('[aria-label="Show Documentation Explorer"]'),
);
const dragBar = container.querySelectorAll(
'.graphiql-horizontal-drag-bar',
diff --git a/packages/graphiql/src/index.tsx b/packages/graphiql/src/index.tsx
index a7fbef3c735..ef0051cc834 100644
--- a/packages/graphiql/src/index.tsx
+++ b/packages/graphiql/src/index.tsx
@@ -31,12 +31,6 @@ export type {
SyncFetcherResult,
} from '@graphiql/toolkit';
-/**
- * Toolbar
- */
-export { ToolbarMenu, ToolbarMenuItem } from './components/ToolbarMenu';
-export { ToolbarSelect, ToolbarSelectOption } from './components/ToolbarSelect';
-
/**
* Legacy exports
*/
diff --git a/packages/graphiql/src/style.css b/packages/graphiql/src/style.css
index 662108de716..1f6c60dfe6c 100644
--- a/packages/graphiql/src/style.css
+++ b/packages/graphiql/src/style.css
@@ -30,7 +30,6 @@
.graphiql-container .graphiql-sidebar button > svg {
height: calc(var(--sidebar-width) - (2 * var(--px-12)));
padding: var(--px-12);
- pointer-events: none;
width: calc(var(--sidebar-width) - (2 * var(--px-12)));
}
@@ -291,3 +290,8 @@ reach-portal .graphiql-key {
border-radius: var(--border-radius-4);
padding: var(--px-4);
}
+
+/* Avoid showing native tooltips for icons with titles */
+.graphiql-container svg {
+ pointer-events: none;
+}
diff --git a/yarn.lock b/yarn.lock
index da7c8598769..0bf93569495 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4495,6 +4495,27 @@
"@reach/utils" "0.17.0"
tslib "^2.3.0"
+"@reach/listbox@^0.17.0":
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/@reach/listbox/-/listbox-0.17.0.tgz#e709f31056bb77781e74c9f0b69bf9ec8efbbc8b"
+ integrity sha512-AMnH1P6/3VKy2V/nPb4Es441arYR+t4YRdh9jdcFVrCOD6y7CQrlmxsYjeg9Ocdz08XpdoEBHM3PKLJqNAUr7A==
+ dependencies:
+ "@reach/auto-id" "0.17.0"
+ "@reach/descendants" "0.17.0"
+ "@reach/machine" "0.17.0"
+ "@reach/popover" "0.17.0"
+ "@reach/utils" "0.17.0"
+ prop-types "^15.7.2"
+
+"@reach/machine@0.17.0":
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/@reach/machine/-/machine-0.17.0.tgz#4e4bbf66e3c3934e65243485ac84f6f8fa3d9a24"
+ integrity sha512-9EHnuPgXzkbRENvRUzJvVvYt+C2jp7PGN0xon7ffmKoK8rTO6eA/bb7P0xgloyDDQtu88TBUXKzW0uASqhTXGA==
+ dependencies:
+ "@reach/utils" "0.17.0"
+ "@xstate/fsm" "1.4.0"
+ tslib "^2.3.0"
+
"@reach/menu-button@^0.17.0":
version "0.17.0"
resolved "https://registry.yarnpkg.com/@reach/menu-button/-/menu-button-0.17.0.tgz#9f40979129b61f8bdc19590c527f7ed4883d2dce"
@@ -4543,6 +4564,20 @@
tiny-warning "^1.0.3"
tslib "^2.3.0"
+"@reach/tooltip@^0.17.0":
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/@reach/tooltip/-/tooltip-0.17.0.tgz#044b43de248a05b18641b4220310983cb54675a2"
+ integrity sha512-HP8Blordzqb/Cxg+jnhGmWQfKgypamcYLBPlcx6jconyV5iLJ5m93qipr1giK7MqKT2wlsKWy44ZcOrJ+Wrf8w==
+ dependencies:
+ "@reach/auto-id" "0.17.0"
+ "@reach/portal" "0.17.0"
+ "@reach/rect" "0.17.0"
+ "@reach/utils" "0.17.0"
+ "@reach/visually-hidden" "0.17.0"
+ prop-types "^15.7.2"
+ tiny-warning "^1.0.3"
+ tslib "^2.3.0"
+
"@reach/utils@0.17.0":
version "0.17.0"
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.17.0.tgz#3d1d2ec56d857f04fe092710d8faee2b2b121303"
@@ -4551,7 +4586,7 @@
tiny-warning "^1.0.3"
tslib "^2.3.0"
-"@reach/visually-hidden@^0.17.0":
+"@reach/visually-hidden@0.17.0", "@reach/visually-hidden@^0.17.0":
version "0.17.0"
resolved "https://registry.yarnpkg.com/@reach/visually-hidden/-/visually-hidden-0.17.0.tgz#033adba10b5ec419649da8d6bd8e46db06d4c3a1"
integrity sha512-T6xF3Nv8vVnjVkGU6cm0+kWtvliLqPAo8PcZ+WxkKacZsaHTjaZb4v1PaCcyQHmuTNT/vtTVNOJLG0SjQOIb7g==
@@ -5574,6 +5609,11 @@
undici "^5.8.0"
web-streams-polyfill "^3.2.0"
+"@xstate/fsm@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@xstate/fsm/-/fsm-1.4.0.tgz#6fd082336fde4d026e9e448576189ee5265fa51a"
+ integrity sha512-uTHDeu2xI5E1IFwf37JFQM31RrH7mY7877RqPBS4ZqSNUwoLDuct8AhBWaXGnVizBAYyimVwgCyGa9z/NiRhXA==
+
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -17846,12 +17886,7 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tslib@^2, tslib@^2.0.0, tslib@^2.3.0, tslib@^2.4.0, tslib@~2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
- integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
-
-tslib@^2.0.0, tslib@^2.1.0:
+tslib@^2, tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@~2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==