Skip to content

Commit

Permalink
[redesign] implement toolbar menu and select in new design (#2631)
Browse files Browse the repository at this point in the history
* implement toolbar menu in new design

* implement `ToolbarListbox` in new design

* better tooltips
  • Loading branch information
thomasheyenbrock committed Aug 24, 2022
1 parent 0526693 commit db6e291
Show file tree
Hide file tree
Showing 36 changed files with 527 additions and 548 deletions.
4 changes: 2 additions & 2 deletions .changeset/five-pillows-fail.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
---

Add new components:
- UI components (`Button`, `ButtonGroup`, `Dialog`, `Menu`, `Spinner`, `Tab`, `Tabs`, `UnStyledButton` and lots of icon components)
- UI components (`Button`, `ButtonGroup`, `Dialog`, `Menu`, `Spinner`, `Tab`, `Tabs`, `Tooltip`, `UnStyledButton` and lots of icon components)
- Editor components (`QueryEditor`, `VariableEditor`, `HeaderEditor` and `ResponseEditor`)
- Toolbar components (`ExecuteButton` and `ToolbarButton`)
- Toolbar components (`ExecuteButton`, `ToolbarButton`, `ToolbarMenu` and `ToolbarSelect`)
- Docs components (`Argument`, `DefaultValue`, `DeprecationReason`, `Directive`, `DocExplorer`, `ExplorerSection`, `FieldDocumentation`, `FieldLink`, `SchemaDocumentation`, `Search`, `TypeDocumentation` and `TypeLink`)
- `History` component
2 changes: 2 additions & 0 deletions .changeset/real-waves-enjoy.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
BREAKING: The following static properties of the `GraphiQL` component have been removed:
- `GraphiQL.Button`: You can use the `ToolbarButton` component from `@graphiql/react` instead.
- `GraphiQL.ToolbarButton`: This exposed the same component as `GraphiQL.Button`.
- `GraphiQL.Menu`: You can use the `ToolbarMenu` component from `@graphiql/react` instead.
- `GraphiQL.MenuItem`: You can use the `ToolbarMenu.Item` component from `@graphiql/react` instead.
- `GraphiQL.Group`: Grouping multiple buttons side-by-side is not provided out-of-the box anymore in the new GraphiQL UI. If you want to implement a similar feature in the new vertical toolbar you can do so by adding your own styles for your custom toolbar elements. Example:
```jsx
import { GraphiQL } from "graphiql";
Expand Down
4 changes: 4 additions & 0 deletions .changeset/red-zoos-divide.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
BREAKING: The following exports of the `graphiql` package have been removed:
- `DocExplorer`: Now exported from `@graphiql/react` as `DocExplorer`
- The `schema` prop has been removed, the component now uses the schema provided by the `ExplorerContext`
- `ToolbarMenu`: Now exported from `@graphiql/react` as `ToolbarMenu`
- `ToolbarMenuItem`: Now exported from `@graphiql/react` as `ToolbarMenu.Item`
- `ToolbarSelect`: Now exported from `@graphiql/react` as `ToolbarListbox`
- `ToolbarSelectOption`: Now exported from `@graphiql/react` as `ToolbarListbox.Option`
1 change: 1 addition & 0 deletions custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ languageservice
linenumber
linenumbers
linkify
listbox
listvalues
matchingbracket
modulemap
Expand Down
2 changes: 2 additions & 0 deletions packages/graphiql-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
"@graphiql/toolkit": "^0.6.1",
"@reach/combobox": "^0.17.0",
"@reach/dialog": "^0.17.0",
"@reach/listbox": "^0.17.0",
"@reach/menu-button": "^0.17.0",
"@reach/tooltip": "^0.17.0",
"@reach/visually-hidden": "^0.17.0",
"codemirror": "^5.65.3",
"codemirror-graphql": "^1.3.3",
Expand Down
8 changes: 4 additions & 4 deletions packages/graphiql-react/src/editor/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,13 +319,13 @@ const DEFAULT_QUERY = `# Welcome to GraphiQL
#
# Keyboard shortcuts:
#
# Prettify Query: Shift-Ctrl-P (or press the prettify button above)
# Prettify query: Shift-Ctrl-P (or press the prettify button)
#
# Merge Query: Shift-Ctrl-M (or press the merge button above)
# Merge fragments: Shift-Ctrl-M (or press the merge button)
#
# Run Query: Ctrl-Enter (or press the play button above)
# Run Query: Ctrl-Enter (or press the play button)
#
# Auto Complete: Ctrl-Space (or just start typing)
# Auto Complete: Ctrl-Space (or just start typing)
#
`;
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,10 @@ describe('QueryHistoryItem', () => {
});

it('renders label input if the edit label button is clicked', () => {
const { container, getByTitle } = render(
const { container, getByLabelText } = render(
<QueryHistoryItemWithContext {...getMockProps()} />,
);
fireEvent.click(getByTitle('Edit label'));
fireEvent.click(getByLabelText('Edit label'));
expect(container.querySelectorAll('li.editable').length).toBe(1);
expect(container.querySelectorAll('input').length).toBe(1);
expect(
Expand Down
56 changes: 34 additions & 22 deletions packages/graphiql-react/src/history/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Fragment, useEffect, useRef, useState } from 'react';

import { useEditorContext } from '../editor';
import { CloseIcon, PenIcon, StarFilledIcon, StarIcon } from '../icons';
import { UnStyledButton } from '../ui';
import { Tooltip, UnStyledButton } from '../ui';
import { useHistoryContext } from './context';

import './style.css';
Expand Down Expand Up @@ -118,28 +118,40 @@ export function HistoryItem(props: QueryHistoryItemProps) {
>
{displayName}
</UnStyledButton>
<UnStyledButton
type="button"
className="graphiql-history-item-action"
title="Edit label"
onClick={e => {
e.stopPropagation();
setIsEditable(true);
}}
>
<PenIcon />
</UnStyledButton>
<UnStyledButton
type="button"
className="graphiql-history-item-action"
onClick={e => {
e.stopPropagation();
toggleFavorite(props.item);
}}
title={props.item.favorite ? 'Remove favorite' : 'Add favorite'}
<Tooltip label="Edit label">
<UnStyledButton
type="button"
className="graphiql-history-item-action"
onClick={e => {
e.stopPropagation();
setIsEditable(true);
}}
aria-label="Edit label"
>
<PenIcon aria-hidden="true" />
</UnStyledButton>
</Tooltip>
<Tooltip
label={props.item.favorite ? 'Remove favorite' : 'Add favorite'}
>
{props.item.favorite ? <StarFilledIcon /> : <StarIcon />}
</UnStyledButton>
<UnStyledButton
type="button"
className="graphiql-history-item-action"
onClick={e => {
e.stopPropagation();
toggleFavorite(props.item);
}}
aria-label={
props.item.favorite ? 'Remove favorite' : 'Add favorite'
}
>
{props.item.favorite ? (
<StarFilledIcon aria-hidden="true" />
) : (
<StarIcon aria-hidden="true" />
)}
</UnStyledButton>
</Tooltip>
</>
)}
</li>
Expand Down
1 change: 0 additions & 1 deletion packages/graphiql-react/src/history/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ button.graphiql-history-item-action {

& > svg {
height: 14px;
pointer-events: none;
width: 14px;
}
}
Expand Down
7 changes: 4 additions & 3 deletions packages/graphiql-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export {
HistoryContextProvider,
useHistoryContext,
} from './history';
export * from './icons';
export {
SchemaContext,
SchemaContextProvider,
Expand All @@ -59,10 +58,12 @@ export {
useStorageContext,
} from './storage';
export { useTheme } from './theme';
export * from './toolbar';
export * from './ui';
export { useDragResize } from './utility/resize';

export * from './icons';
export * from './ui';
export * from './toolbar';

export type {
EditorContextType,
ResponseTooltipType,
Expand Down
4 changes: 0 additions & 4 deletions packages/graphiql-react/src/toolbar/button.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,4 @@ button.graphiql-toolbar-button {
&.error {
background: hsla(var(--color-error), 0.15);
}

& > svg {
pointer-events: none;
}
}
62 changes: 34 additions & 28 deletions packages/graphiql-react/src/toolbar/button.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,45 @@
import { forwardRef, useState } from 'react';

import { UnStyledButton } from '../ui';
import { Tooltip, UnStyledButton } from '../ui';
import { compose } from '../utility/compose';

import './button.css';

type ToolbarButtonProps = {
label: string;
};

export const ToolbarButton = forwardRef<
HTMLButtonElement,
JSX.IntrinsicElements['button']
>((props, ref) => {
ToolbarButtonProps & JSX.IntrinsicElements['button']
>(({ label, ...props }, ref) => {
const [error, setError] = useState<Error | null>(null);
return (
<UnStyledButton
{...props}
ref={ref}
type="button"
className={
'graphiql-toolbar-button' +
(error ? ' error' : '') +
(props.className ? ' ' + props.className : '')
}
onClick={event => {
try {
props.onClick?.(event);
setError(null);
} catch (err) {
setError(
err instanceof Error
? err
: new Error(`Toolbar button click failed: ${err}`),
);
}
}}
title={error ? error.message : props.title}
aria-invalid={error ? 'true' : props['aria-invalid']}
/>
<Tooltip label={label}>
<UnStyledButton
{...props}
ref={ref}
type="button"
className={compose(
'graphiql-toolbar-button',
error ? 'error' : '',
props.className,
)}
onClick={event => {
try {
props.onClick?.(event);
setError(null);
} catch (err) {
setError(
err instanceof Error
? err
: new Error(`Toolbar button click failed: ${err}`),
);
}
}}
aria-label={error ? error.message : label}
aria-invalid={error ? 'true' : props['aria-invalid']}
/>
</Tooltip>
);
});
ToolbarButton.displayName = 'ToolbarButton';
3 changes: 1 addition & 2 deletions packages/graphiql-react/src/toolbar/execute.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
position: relative;
}

.graphiql-execute-button {
button.graphiql-execute-button {
background-color: hsl(var(--color-primary));
border: none;
border-radius: var(--border-radius-8);
Expand All @@ -24,7 +24,6 @@
display: block;
height: var(--px-16);
margin: auto;
pointer-events: none;
width: var(--px-16);
}
}
32 changes: 19 additions & 13 deletions packages/graphiql-react/src/toolbar/execute.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEditorContext } from '../editor';
import { useExecutionContext } from '../execution';
import { PlayIcon, StopIcon } from '../icons';
import { Menu } from '../ui';
import { Menu, Tooltip } from '../ui';

import './execute.css';

Expand All @@ -18,16 +18,20 @@ export function ExecuteButton() {
const operations = queryEditor?.operations || [];
const hasOptions = operations.length > 1 && typeof operationName !== 'string';

const label = `${isFetching ? 'Stop' : 'Execute'} query (Ctrl-Enter)`;
const buttonProps = {
type: 'button' as const,
className: 'graphiql-execute-button',
title: 'Execute Query (Ctrl-Enter)',
children: isFetching ? <StopIcon /> : <PlayIcon />,
'aria-label': label,
};

return hasOptions ? (
<Menu>
<Menu.Button {...buttonProps} />
<Tooltip label={label}>
<Menu.Button {...buttonProps} />
</Tooltip>

<Menu.List>
{operations.map((operation, i) => {
const opName = operation.name
Expand Down Expand Up @@ -55,15 +59,17 @@ export function ExecuteButton() {
</Menu.List>
</Menu>
) : (
<button
{...buttonProps}
onClick={() => {
if (isFetching) {
stop();
} else {
run();
}
}}
/>
<Tooltip label={label}>
<button
{...buttonProps}
onClick={() => {
if (isFetching) {
stop();
} else {
run();
}
}}
/>
</Tooltip>
);
}
2 changes: 2 additions & 0 deletions packages/graphiql-react/src/toolbar/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './button';
export * from './execute';
export * from './listbox';
export * from './menu';
5 changes: 5 additions & 0 deletions packages/graphiql-react/src/toolbar/listbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.graphiql-toolbar-listbox {
display: block;
height: var(--toolbar-width);
width: var(--toolbar-width);
}
36 changes: 36 additions & 0 deletions packages/graphiql-react/src/toolbar/listbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { ComponentProps, forwardRef, ReactNode } from 'react';
import { Listbox, Tooltip } from '../ui';
import { createComponentGroup } from '../utility/component-group';
import { compose } from '../utility/compose';

import './listbox.css';

type ToolbarListboxProps = {
button: ReactNode;
label: string;
};

const ToolbarListboxRoot = forwardRef<
HTMLDivElement,
ToolbarListboxProps & ComponentProps<typeof Listbox.Input>
>(({ button, children, label, ...props }, ref) => {
const labelWithValue = `${label}${props.value ? `: ${props.value}` : ''}`;
return (
<Listbox.Input
{...props}
ref={ref}
className={compose('graphiql-toolbar-listbox', props.className)}
aria-label={labelWithValue}
>
<Tooltip label={labelWithValue}>
<Listbox.Button>{button}</Listbox.Button>
</Tooltip>
<Listbox.Popover>{children}</Listbox.Popover>
</Listbox.Input>
);
});
ToolbarListboxRoot.displayName = 'ToolbarListbox';

export const ToolbarListbox = createComponentGroup(ToolbarListboxRoot, {
Option: Listbox.Option,
});
5 changes: 5 additions & 0 deletions packages/graphiql-react/src/toolbar/menu.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
button.graphiql-toolbar-menu {
display: block;
height: var(--toolbar-width);
width: var(--toolbar-width);
}
Loading

0 comments on commit db6e291

Please sign in to comment.