From 078319b1645c11884323ad3a2833d1c81f3f2fee Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Sat, 6 May 2023 02:04:47 +0200 Subject: [PATCH 1/3] remove `@radix-ui/react-tooltip` --- packages/graphiql-react/package.json | 2 +- packages/graphiql-react/src/style/root.css | 9 +- packages/graphiql-react/src/ui/tooltip.css | 5 +- packages/graphiql-react/src/ui/tooltip.ts | 3 - packages/graphiql-react/src/ui/tooltip.tsx | 32 + packages/graphiql/src/components/GraphiQL.tsx | 950 +++++++++--------- 6 files changed, 520 insertions(+), 481 deletions(-) delete mode 100644 packages/graphiql-react/src/ui/tooltip.ts create mode 100644 packages/graphiql-react/src/ui/tooltip.tsx diff --git a/packages/graphiql-react/package.json b/packages/graphiql-react/package.json index d9b9ed8a6fd..4412ed4ebc6 100644 --- a/packages/graphiql-react/package.json +++ b/packages/graphiql-react/package.json @@ -38,11 +38,11 @@ "dependencies": { "@radix-ui/react-dialog": "^1.0.3", "@radix-ui/react-visually-hidden": "^1.0.2", + "@radix-ui/react-tooltip": "^1.0.5", "@graphiql/toolkit": "^0.8.4", "@reach/combobox": "^0.17.0", "@reach/listbox": "^0.17.0", "@reach/menu-button": "^0.17.0", - "@reach/tooltip": "^0.17.0", "clsx": "^1.2.1", "codemirror": "^5.65.3", "codemirror-graphql": "^2.0.8", diff --git a/packages/graphiql-react/src/style/root.css b/packages/graphiql-react/src/style/root.css index aed9919204a..bdcc6cad625 100644 --- a/packages/graphiql-react/src/style/root.css +++ b/packages/graphiql-react/src/style/root.css @@ -7,7 +7,8 @@ .CodeMirror-info, .CodeMirror-lint-tooltip, .graphiql-dialog, -.graphiql-dialog-overlay { +.graphiql-dialog-overlay, +.graphiql-tooltip { /* Colors */ --color-primary: 320, 95%, 43%; --color-secondary: 242, 51%, 61%; @@ -73,7 +74,8 @@ body:not(.graphiql-light) .CodeMirror-info, body:not(.graphiql-light) .CodeMirror-lint-tooltip, body:not(.graphiql-light) .graphiql-dialog, - body:not(.graphiql-light) .graphiql-dialog-overlay { + body:not(.graphiql-light) .graphiql-dialog-overlay, + body:not(.graphiql-light) .graphiql-tooltip { --color-primary: 338, 100%, 67%; --color-secondary: 243, 100%, 77%; --color-tertiary: 188, 100%, 44%; @@ -93,7 +95,8 @@ body.graphiql-dark .graphiql-container, body.graphiql-dark .CodeMirror-info, body.graphiql-dark .CodeMirror-lint-tooltip, body.graphiql-dark .graphiql-dialog, -body.graphiql-dark .graphiql-dialog-overlay { +body.graphiql-dark .graphiql-dialog-overlay, +body.graphiql-dark .graphiql-tooltip { --color-primary: 338, 100%, 67%; --color-secondary: 243, 100%, 77%; --color-tertiary: 188, 100%, 44%; diff --git a/packages/graphiql-react/src/ui/tooltip.css b/packages/graphiql-react/src/ui/tooltip.css index 57c340b97dd..4455f9126b8 100644 --- a/packages/graphiql-react/src/ui/tooltip.css +++ b/packages/graphiql-react/src/ui/tooltip.css @@ -1,6 +1,4 @@ -@import url('@reach/tooltip/styles.css'); - -[data-reach-tooltip] { +.graphiql-tooltip { background: hsl(var(--color-base)); border: var(--popover-border); border-radius: var(--border-radius-4); @@ -8,4 +6,5 @@ color: hsl(var(--color-neutral)); font-size: inherit; padding: var(--px-4) var(--px-6); + font-family: var(--font-family); } diff --git a/packages/graphiql-react/src/ui/tooltip.ts b/packages/graphiql-react/src/ui/tooltip.ts deleted file mode 100644 index bc156c6213d..00000000000 --- a/packages/graphiql-react/src/ui/tooltip.ts +++ /dev/null @@ -1,3 +0,0 @@ -import './tooltip.css'; - -export { Tooltip } from '@reach/tooltip'; diff --git a/packages/graphiql-react/src/ui/tooltip.tsx b/packages/graphiql-react/src/ui/tooltip.tsx new file mode 100644 index 00000000000..8bbcaf88ac8 --- /dev/null +++ b/packages/graphiql-react/src/ui/tooltip.tsx @@ -0,0 +1,32 @@ +import { ReactElement } from 'react'; +import * as T from '@radix-ui/react-tooltip'; +import { createComponentGroup } from '../utility/component-group'; +import './tooltip.css'; + +export function TooltipRoot({ + children, + align = 'start', + side = 'bottom', + sideOffset = 5, + label, +}: T.TooltipContentProps & { label: string }): ReactElement { + return ( + + {children} + + + {label} + + + + ); +} + +export const Tooltip = createComponentGroup(TooltipRoot, { + Provider: T.Provider, +}); diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index d8e8ec13334..20ababbb80d 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -334,119 +334,137 @@ export function GraphiQLInterface(props: GraphiQLInterfaceProps) { ); return ( -
-
-
- {pluginContext?.plugins.map(plugin => { - const isVisible = plugin === pluginContext.visiblePlugin; - const label = `${isVisible ? 'Hide' : 'Show'} ${plugin.title}`; - const Icon = plugin.icon; - return ( - - { - if (isVisible) { - pluginContext.setVisiblePlugin(null); - pluginResize.setHiddenElement('first'); - } else { - pluginContext.setVisiblePlugin(plugin); - pluginResize.setHiddenElement(null); - } - }} - aria-label={label} - > - - - ); - })} -
-
- - schemaContext.introspect()} - aria-label="Re-fetch GraphQL schema" - > - - - - setShowDialog('short-keys')} - aria-label="Open short keys dialog" - > - - - - setShowDialog('settings')} - aria-label="Open settings dialog" - > - - -
-
-
-
-
- {PluginContent ? : null} + +
+
+
+ {pluginContext?.plugins.map(plugin => { + const isVisible = plugin === pluginContext.visiblePlugin; + const label = `${isVisible ? 'Hide' : 'Show'} ${plugin.title}`; + const Icon = plugin.icon; + return ( + + { + if (isVisible) { + pluginContext.setVisiblePlugin(null); + pluginResize.setHiddenElement('first'); + } else { + pluginContext.setVisiblePlugin(plugin); + pluginResize.setHiddenElement(null); + } + }} + aria-label={label} + > + + + ); + })} +
+
+ + schemaContext.introspect()} + aria-label="Re-fetch GraphQL schema" + > + + + + setShowDialog('short-keys')} + aria-label="Open short keys dialog" + > + + + + setShowDialog('settings')} + aria-label="Open settings dialog" + > + +
-
- {pluginContext?.visiblePlugin ? ( -
- ) : null} -
-
-
-
- - {editorContext.tabs.length > 1 ? ( - <> - {editorContext.tabs.map((tab, index) => ( - - { - executionContext.stop(); - editorContext.changeTab(index); - }} +
+
+
+ {PluginContent ? : null} +
+
+
+ {pluginContext?.visiblePlugin ? ( +
+ ) : null} +
+
+
+
+ + {editorContext.tabs.length > 1 ? ( + <> + {editorContext.tabs.map((tab, index) => ( + - {tab.title} - - { - if (editorContext.activeTabIndex === index) { + { executionContext.stop(); - } - editorContext.closeTab(index); - }} - /> - - ))} -
+ editorContext.changeTab(index); + }} + > + {tab.title} + + { + if (editorContext.activeTabIndex === index) { + executionContext.stop(); + } + editorContext.closeTab(index); + }} + /> + + ))} +
+ + editorContext.addTab()} + aria-label="Add tab" + > + + +
+ + ) : null} + +
+ {editorContext.tabs.length === 1 ? ( +
- - ) : null} - -
- {editorContext.tabs.length === 1 ? ( -
- - editorContext.addTab()} - aria-label="Add tab" - > - - -
- ) : null} - {logo} + ) : null} + {logo} +
-
-
-
-
-
-
-
- -
-
-
-
-
-
- { - if (editorToolsResize.hiddenElement === 'second') { - editorToolsResize.setHiddenElement(null); - } - setActiveSecondaryEditor('variables'); - }} +
+ +
+
- Variables - - {isHeadersEditorEnabled ? ( + + {toolbar} +
+ +
+
+
+
- Headers + Variables - ) : null} -
- - { - editorToolsResize.setHiddenElement( - editorToolsResize.hiddenElement === 'second' - ? null - : 'second', - ); - }} - aria-label={ + {isHeadersEditorEnabled ? ( + { + if ( + editorToolsResize.hiddenElement === 'second' + ) { + editorToolsResize.setHiddenElement(null); + } + setActiveSecondaryEditor('headers'); + }} + > + Headers + + ) : null} +
+ - {editorToolsResize.hiddenElement === 'second' ? ( - + { + editorToolsResize.setHiddenElement( + editorToolsResize.hiddenElement === 'second' + ? null + : 'second', + ); + }} + aria-label={ + editorToolsResize.hiddenElement === 'second' + ? 'Show editor tools' + : 'Hide editor tools' + } + > + {editorToolsResize.hiddenElement === 'second' ? ( + + +
-
-
-
- - {isHeadersEditorEnabled && ( - +
+ - )} -
+ {isHeadersEditorEnabled && ( + + )} +
+
-
-
-
-
-
-
- {executionContext.isFetching ? : null} - - {footer} +
+
+
+
+
+ {executionContext.isFetching ? : null} + + {footer} +
-
- { - if (!isOpen) { - setShowDialog(null); - } - }} - > -
- - Short Keys - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Short keyFunction
- {modifier} - {' + '} - F - Search in editor
- {modifier} - {' + '} - K - Search in documentation
- {modifier} - {' + '} - Enter - Execute query
- Ctrl - {' + '} - Shift - {' + '} - P - Prettify editors
- Ctrl - {' + '} - Shift - {' + '} - M - Merge fragments definitions into operation definition
- Ctrl - {' + '} - Shift - {' + '} - C - Copy query
- Ctrl - {' + '} - Shift - {' + '} - R - Re-fetch schema using introspection
-

- The editors use{' '} - - CodeMirror Key Maps - {' '} - that add more short keys. This instance of GraphiQL uses{' '} - {props.keyMap || 'sublime'}. -

+ { + if (!isOpen) { + setShowDialog(null); + } + }} + > +
+ + Short Keys + +
-
-
- { - if (!isOpen) { - setShowDialog(null); - setClearStorageStatus(null); - } - }} - > -
- - Settings - - -
- {props.showPersistHeadersSettings ? (
-
- Persist headers -
-
- Save headers upon reloading.{' '} - - Only enable if you trust this device. - -
-
- - - - -
- ) : null} -
-
-
Theme
-
- Adjust how the interface looks like. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Short keyFunction
+ {modifier} + {' + '} + F + Search in editor
+ {modifier} + {' + '} + K + Search in documentation
+ {modifier} + {' + '} + Enter + Execute query
+ Ctrl + {' + '} + Shift + {' + '} + P + Prettify editors
+ Ctrl + {' + '} + Shift + {' + '} + M + + Merge fragments definitions into operation definition +
+ Ctrl + {' + '} + Shift + {' + '} + C + Copy query
+ Ctrl + {' + '} + Shift + {' + '} + R + Re-fetch schema using introspection
+

+ The editors use{' '} + + CodeMirror Key Maps + {' '} + that add more short keys. This instance of GraphiQL + uses {props.keyMap || 'sublime'}. +

-
- - - - - +
+ { + if (!isOpen) { + setShowDialog(null); + setClearStorageStatus(null); + } + }} + > +
+ + Settings + +
-
- {storageContext ? ( + {props.showPersistHeadersSettings ? ( +
+
+
+ Persist headers +
+
+ Save headers upon reloading.{' '} + + Only enable if you trust this device. + +
+
+ + + + +
+ ) : null}
-
Clear storage
+
Theme
- Remove all locally stored data and start fresh. + Adjust how the interface looks like.
- + + + + +
- ) : null} - -
+ {storageContext ? ( +
+
+
+ Clear storage +
+
+ Remove all locally stored data and start fresh. +
+
+
+ +
+
+ ) : null} + +
+ ); } From 7ad125c7af8ef30b486b20b3f109021c86275c01 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Thu, 1 Jun 2023 23:13:37 +0200 Subject: [PATCH 2/3] add changeset --- .changeset/poor-lizards-jam.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/poor-lizards-jam.md diff --git a/.changeset/poor-lizards-jam.md b/.changeset/poor-lizards-jam.md new file mode 100644 index 00000000000..2307ea421ca --- /dev/null +++ b/.changeset/poor-lizards-jam.md @@ -0,0 +1,5 @@ +--- +'@graphiql/react': major +--- + +replace `@reach/tooltip` by `@radix-ui/react-tooltip` From cc88655fe7e838a0001e765f6f531dbe5f71d0ff Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Thu, 1 Jun 2023 23:14:25 +0200 Subject: [PATCH 3/3] =?UTF-8?q?3=EF=B8=8F=E2=83=A3=20replace=20`@reach/lis?= =?UTF-8?q?tbox`=20and=20=20`@reach/menu-button`=20by=20`@radix-ui/react-d?= =?UTF-8?q?ropdown-menu`=20(#3183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/gold-lizards-march.md | 7 + .changeset/shiny-carpets-rescue.md | 5 + .../package.json | 4 +- .../graphiql-plugin-explorer/package.json | 4 +- packages/graphiql-react/package.json | 13 +- .../src/explorer/components/doc-explorer.css | 5 +- .../src/explorer/components/doc-explorer.tsx | 4 +- .../src/explorer/components/search.css | 26 +- .../src/explorer/components/search.tsx | 169 +++-- packages/graphiql-react/src/style/root.css | 9 +- packages/graphiql-react/src/toolbar/index.ts | 1 - .../graphiql-react/src/toolbar/listbox.css | 5 - .../graphiql-react/src/toolbar/listbox.tsx | 36 -- packages/graphiql-react/src/toolbar/menu.tsx | 21 +- packages/graphiql-react/src/ui/dropdown.css | 20 +- packages/graphiql-react/src/ui/dropdown.tsx | 75 +-- packages/graphiql/cypress/e2e/docs.cy.ts | 31 +- packages/graphiql/cypress/support/commands.ts | 6 +- packages/graphiql/package.json | 8 +- packages/graphiql/resources/index.html.ejs | 12 +- packages/graphiql/resources/renderExample.js | 5 +- yarn.lock | 594 +++++++++++------- 22 files changed, 567 insertions(+), 493 deletions(-) create mode 100644 .changeset/gold-lizards-march.md create mode 100644 .changeset/shiny-carpets-rescue.md delete mode 100644 packages/graphiql-react/src/toolbar/listbox.css delete mode 100644 packages/graphiql-react/src/toolbar/listbox.tsx diff --git a/.changeset/gold-lizards-march.md b/.changeset/gold-lizards-march.md new file mode 100644 index 00000000000..d4219c759f0 --- /dev/null +++ b/.changeset/gold-lizards-march.md @@ -0,0 +1,7 @@ +--- +'@graphiql/react': major +--- + +replace `@reach/menu-button` by `@radix-ui/react-dropdown-menu` +remove `@reach/listbox` +remove `` and `` components (use `` instead) diff --git a/.changeset/shiny-carpets-rescue.md b/.changeset/shiny-carpets-rescue.md new file mode 100644 index 00000000000..3c6927b053b --- /dev/null +++ b/.changeset/shiny-carpets-rescue.md @@ -0,0 +1,5 @@ +--- +'@graphiql/react': major +--- + +replace `@reach/combobox` with `Combobox` from `@headlessui/react` diff --git a/packages/graphiql-plugin-code-exporter/package.json b/packages/graphiql-plugin-code-exporter/package.json index a7d9e2f4973..39fa292ea27 100644 --- a/packages/graphiql-plugin-code-exporter/package.json +++ b/packages/graphiql-plugin-code-exporter/package.json @@ -33,8 +33,8 @@ }, "peerDependencies": { "graphql": "^15.5.0 || ^16.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" }, "devDependencies": { "@graphiql/react": "^0.17.4", diff --git a/packages/graphiql-plugin-explorer/package.json b/packages/graphiql-plugin-explorer/package.json index 93bc9203369..2e87ebc5dc2 100644 --- a/packages/graphiql-plugin-explorer/package.json +++ b/packages/graphiql-plugin-explorer/package.json @@ -33,8 +33,8 @@ }, "peerDependencies": { "graphql": "^15.5.0 || ^16.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" }, "devDependencies": { "@vitejs/plugin-react": "^1.3.0", diff --git a/packages/graphiql-react/package.json b/packages/graphiql-react/package.json index 4412ed4ebc6..0e6941b3ce3 100644 --- a/packages/graphiql-react/package.json +++ b/packages/graphiql-react/package.json @@ -32,17 +32,16 @@ }, "peerDependencies": { "graphql": "^15.5.0 || ^16.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" }, "dependencies": { + "@headlessui/react": "^1.7.14", "@radix-ui/react-dialog": "^1.0.3", "@radix-ui/react-visually-hidden": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.4", "@graphiql/toolkit": "^0.8.4", - "@reach/combobox": "^0.17.0", - "@reach/listbox": "^0.17.0", - "@reach/menu-button": "^0.17.0", "clsx": "^1.2.1", "codemirror": "^5.65.3", "codemirror-graphql": "^2.0.8", @@ -59,8 +58,8 @@ "@vitejs/plugin-react": "^1.3.0", "graphql": "^16.4.0", "postcss-nesting": "^10.1.7", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", "typescript": "^4.6.3", "vite": "^2.9.13", "vite-plugin-react-svg": "^0.2.0" diff --git a/packages/graphiql-react/src/explorer/components/doc-explorer.css b/packages/graphiql-react/src/explorer/components/doc-explorer.css index b5b081b6b39..87c81282175 100644 --- a/packages/graphiql-react/src/explorer/components/doc-explorer.css +++ b/packages/graphiql-react/src/explorer/components/doc-explorer.css @@ -28,7 +28,6 @@ /* The search input in the header of the doc explorer */ .graphiql-doc-explorer-search { - height: 100%; position: absolute; right: 0; top: 0; @@ -37,12 +36,12 @@ left: 0; } - & [data-reach-combobox-input] { + & [role='combobox'] { height: 24px; width: 4ch; } - & [data-reach-combobox-input]:focus { + & [role='combobox']:focus { width: 100%; } } diff --git a/packages/graphiql-react/src/explorer/components/doc-explorer.tsx b/packages/graphiql-react/src/explorer/components/doc-explorer.tsx index 43a1b3c803a..63385469c58 100644 --- a/packages/graphiql-react/src/explorer/components/doc-explorer.tsx +++ b/packages/graphiql-react/src/explorer/components/doc-explorer.tsx @@ -81,9 +81,7 @@ export function DocExplorer() { )}
{navItem.name}
-
- -
+
{content}
diff --git a/packages/graphiql-react/src/explorer/components/search.css b/packages/graphiql-react/src/explorer/components/search.css index 624b2b6f094..e2ca4e42ebe 100644 --- a/packages/graphiql-react/src/explorer/components/search.css +++ b/packages/graphiql-react/src/explorer/components/search.css @@ -1,6 +1,4 @@ -@import url('@reach/combobox/styles.css'); - -[data-reach-combobox] { +.graphiql-doc-explorer-search { color: hsla(var(--color-neutral), var(--alpha-secondary)); &:not([data-state='idle']) { @@ -11,8 +9,6 @@ & .graphiql-doc-explorer-search-input { background: hsl(var(--color-base)); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; } } } @@ -25,7 +21,7 @@ padding: var(--px-8) var(--px-12); } -[data-reach-combobox-input] { +.graphiql-doc-explorer-search [role='combobox'] { border: none; background-color: transparent; margin-left: var(--px-4); @@ -36,7 +32,7 @@ } } -[data-reach-combobox-popover] { +.graphiql-doc-explorer-search [role='listbox'] { background-color: hsl(var(--color-base)); border: none; border-bottom-left-radius: var(--border-radius-4); @@ -45,7 +41,9 @@ hsla(var(--color-neutral), var(--alpha-background-heavy)); max-height: 400px; overflow-y: auto; - + margin: 0; + font-size: var(--font-size-body); + padding: var(--px-4); /** * This makes sure that the logic for auto-scrolling the search results when * using keyboard navigation works properly (we use `offsetTop` there). @@ -53,20 +51,16 @@ position: relative; } -[data-reach-combobox-list] { - font-size: var(--font-size-body); - padding: var(--px-4); -} - -[data-reach-combobox-option] { +.graphiql-doc-explorer-search [role='option'] { border-radius: var(--border-radius-4); color: hsla(var(--color-neutral), var(--alpha-secondary)); overflow-x: hidden; padding: var(--px-8) var(--px-12); text-overflow: ellipsis; white-space: nowrap; + cursor: pointer; - &[data-highlighted] { + &[data-headlessui-state="active"] { background-color: hsla(var(--color-neutral), var(--alpha-background-light)); } @@ -77,7 +71,7 @@ ); } - &[data-highlighted]:hover { + &[data-headlessui-state="active"]:hover { background-color: hsla(var(--color-neutral), var(--alpha-background-heavy)); } diff --git a/packages/graphiql-react/src/explorer/components/search.tsx b/packages/graphiql-react/src/explorer/components/search.tsx index 886899d7b89..3c4e01ff8ea 100644 --- a/packages/graphiql-react/src/explorer/components/search.tsx +++ b/packages/graphiql-react/src/explorer/components/search.tsx @@ -1,10 +1,3 @@ -import { - Combobox, - ComboboxInput, - ComboboxPopover, - ComboboxList, - ComboboxOption, -} from '@reach/combobox'; import { GraphQLArgument, GraphQLField, @@ -15,6 +8,7 @@ import { isObjectType, } from 'graphql'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Combobox } from '@headlessui/react'; import { MagnifyingGlassIcon } from '../../icons'; import { useSchemaContext } from '../../schema'; import debounce from '../../utility/debounce'; @@ -31,11 +25,8 @@ export function Search() { }); const inputRef = useRef(null); - const popoverRef = useRef(null); - const getSearchResults = useSearchResults(); const [searchValue, setSearchValue] = useState(''); - const [results, setResults] = useState(getSearchResults(searchValue)); const debouncedGetSearchResults = useMemo( () => @@ -61,87 +52,78 @@ export function Search() { const navItem = explorerNavStack.at(-1)!; + const onSelect = useCallback( + (def: TypeMatch | FieldMatch) => { + push( + 'field' in def + ? { name: def.field.name, def: def.field } + : { name: def.type.name, def: def.type }, + ); + }, + [push], + ); + const [isFocused, setIsFocused] = useState(false); + const handleFocus = useCallback(e => { + setIsFocused(e.type === 'focus'); + }, []); + const shouldSearchBoxAppear = explorerNavStack.length === 1 || isObjectType(navItem.def) || isInterfaceType(navItem.def) || isInputObjectType(navItem.def); + if (!shouldSearchBoxAppear) { + return null; + } - return shouldSearchBoxAppear ? ( + return ( { - const def = value as unknown as TypeMatch | FieldMatch; - push( - 'field' in def - ? { name: def.field.name, def: def.field } - : { name: def.type.name, def: def.type }, - ); - }} >
{ - if (inputRef.current) { - inputRef.current.focus(); - } + inputRef.current?.focus(); }} > - { - setSearchValue(event.target.value); - }} - onKeyDown={event => { - if (!event.isDefaultPrevented()) { - const container = popoverRef.current; - if (!container) { - return; - } - - window.requestAnimationFrame(() => { - const element = container.querySelector('[aria-selected=true]'); - if (!(element instanceof HTMLElement)) { - return; - } - const top = element.offsetTop - container.scrollTop; - const bottom = - container.scrollTop + - container.clientHeight - - (element.offsetTop + element.clientHeight); - if (bottom < 0) { - container.scrollTop -= bottom; - } - if (top < 0) { - container.scrollTop += top; - } - }); - } - - // We don't want for example "Escape" key presses to bubble up - // further. This could have other effects like closing a dialog - // that contains this component. - event.stopPropagation(); - }} + setSearchValue(event.target.value)} placeholder="⌘ K" ref={inputRef} value={searchValue} + data-cy="doc-explorer-input" />
- - - {/** - * Setting the `index` prop explicitly on the `ComboboxOption` solves - * buggy behavior of the internal ordering of the combobox items. - * (Sometimes this results in weird jumps when using the keyboard to - * navigate search results.) - */} - {results.within.map((result, i) => ( - - - - ))} + + {/* hide on blur */} + {isFocused && ( + + {results.within.length + + results.types.length + + results.fields.length === + 0 ? ( +
  • + No results found +
  • + ) : ( + results.within.map((result, i) => ( + + + + )) + )} {results.within.length > 0 && results.types.length + results.fields.length > 0 ? (
    @@ -149,36 +131,28 @@ export function Search() {
    ) : null} {results.types.map((result, i) => ( - - + ))} {results.fields.map((result, i) => ( - . - + ))} - {results.within.length + - results.types.length + - results.fields.length === - 0 ? ( -
    - No results found -
    - ) : null} -
    -
    + + )}
    - ) : null; + ); } type TypeMatch = { type: GraphQLNamedType }; @@ -227,7 +201,6 @@ export function useSearchResults(caller?: Function) { typeNames = typeNames.filter(n => n !== withinType.name); typeNames.unshift(withinType.name); } - for (const typeName of typeNames) { if ( matches.within.length + @@ -283,7 +256,7 @@ export function useSearchResults(caller?: Function) { ); } -function isMatch(sourceText: string, searchValue: string) { +function isMatch(sourceText: string, searchValue: string): boolean { try { const escaped = searchValue.replaceAll(/[^_0-9A-Za-z]/g, ch => '\\' + ch); return sourceText.search(new RegExp(escaped, 'i')) !== -1; @@ -305,20 +278,18 @@ type FieldProps = { argument?: GraphQLArgument; }; -function Field(props: FieldProps) { +function Field({ field, argument }: FieldProps) { return ( <> - - {props.field.name} - - {props.argument ? ( + {field.name} + {argument ? ( <> ( - {props.argument.name} + {argument.name} :{' '} - {renderType(props.argument.type, namedType => ( + {renderType(argument.type, namedType => ( ))} ) diff --git a/packages/graphiql-react/src/style/root.css b/packages/graphiql-react/src/style/root.css index bdcc6cad625..f34058d566d 100644 --- a/packages/graphiql-react/src/style/root.css +++ b/packages/graphiql-react/src/style/root.css @@ -8,7 +8,8 @@ .CodeMirror-lint-tooltip, .graphiql-dialog, .graphiql-dialog-overlay, -.graphiql-tooltip { +.graphiql-tooltip, +[data-radix-popper-content-wrapper] { /* Colors */ --color-primary: 320, 95%, 43%; --color-secondary: 242, 51%, 61%; @@ -75,7 +76,8 @@ body:not(.graphiql-light) .CodeMirror-lint-tooltip, body:not(.graphiql-light) .graphiql-dialog, body:not(.graphiql-light) .graphiql-dialog-overlay, - body:not(.graphiql-light) .graphiql-tooltip { + body:not(.graphiql-light) .graphiql-tooltip, + body:not(.graphiql-light) [data-radix-popper-content-wrapper] { --color-primary: 338, 100%, 67%; --color-secondary: 243, 100%, 77%; --color-tertiary: 188, 100%, 44%; @@ -96,7 +98,8 @@ body.graphiql-dark .CodeMirror-info, body.graphiql-dark .CodeMirror-lint-tooltip, body.graphiql-dark .graphiql-dialog, body.graphiql-dark .graphiql-dialog-overlay, -body.graphiql-dark .graphiql-tooltip { +body.graphiql-dark .graphiql-tooltip, +body.graphiql-dark [data-radix-popper-content-wrapper] { --color-primary: 338, 100%, 67%; --color-secondary: 243, 100%, 77%; --color-tertiary: 188, 100%, 44%; diff --git a/packages/graphiql-react/src/toolbar/index.ts b/packages/graphiql-react/src/toolbar/index.ts index 6f344628eea..9a180077030 100644 --- a/packages/graphiql-react/src/toolbar/index.ts +++ b/packages/graphiql-react/src/toolbar/index.ts @@ -1,4 +1,3 @@ export * from './button'; export * from './execute'; -export * from './listbox'; export * from './menu'; diff --git a/packages/graphiql-react/src/toolbar/listbox.css b/packages/graphiql-react/src/toolbar/listbox.css deleted file mode 100644 index 380bff194d8..00000000000 --- a/packages/graphiql-react/src/toolbar/listbox.css +++ /dev/null @@ -1,5 +0,0 @@ -.graphiql-toolbar-listbox { - display: block; - height: var(--toolbar-width); - width: var(--toolbar-width); -} diff --git a/packages/graphiql-react/src/toolbar/listbox.tsx b/packages/graphiql-react/src/toolbar/listbox.tsx deleted file mode 100644 index d5107c88226..00000000000 --- a/packages/graphiql-react/src/toolbar/listbox.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ComponentProps, forwardRef, ReactNode } from 'react'; -import { clsx } from 'clsx'; -import { Listbox, Tooltip } from '../ui'; -import { createComponentGroup } from '../utility/component-group'; - -import './listbox.css'; - -type ToolbarListboxProps = { - button: ReactNode; - label: string; -}; - -const ToolbarListboxRoot = forwardRef< - HTMLDivElement, - ToolbarListboxProps & ComponentProps ->(({ button, children, label, ...props }, ref) => { - const labelWithValue = `${label}${props.value ? `: ${props.value}` : ''}`; - return ( - - - {button} - - {children} - - ); -}); -ToolbarListboxRoot.displayName = 'ToolbarListbox'; - -export const ToolbarListbox = createComponentGroup(ToolbarListboxRoot, { - Option: Listbox.Option, -}); diff --git a/packages/graphiql-react/src/toolbar/menu.tsx b/packages/graphiql-react/src/toolbar/menu.tsx index 83d45976c5e..9a259216fb5 100644 --- a/packages/graphiql-react/src/toolbar/menu.tsx +++ b/packages/graphiql-react/src/toolbar/menu.tsx @@ -1,20 +1,26 @@ -import { forwardRef, ReactNode } from 'react'; +import { ReactNode } from 'react'; import { clsx } from 'clsx'; import { Menu, Tooltip } from '../ui'; import { createComponentGroup } from '../utility/component-group'; import './menu.css'; +import { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; type ToolbarMenuProps = { button: ReactNode; label: string; }; -const ToolbarMenuRoot = forwardRef< - HTMLDivElement, - ToolbarMenuProps & JSX.IntrinsicElements['div'] ->(({ button, children, label, ...props }, ref) => ( - +const ToolbarMenuRoot = ({ + button, + children, + label, + ...props +}: ToolbarMenuProps & { + children: ReactNode; + className?: string; +} & DropdownMenuProps) => ( + {children} -)); -ToolbarMenuRoot.displayName = 'ToolbarMenu'; +); export const ToolbarMenu = createComponentGroup(ToolbarMenuRoot, { Item: Menu.Item, diff --git a/packages/graphiql-react/src/ui/dropdown.css b/packages/graphiql-react/src/ui/dropdown.css index 3daf6c14f16..c62dec672e5 100644 --- a/packages/graphiql-react/src/ui/dropdown.css +++ b/packages/graphiql-react/src/ui/dropdown.css @@ -1,8 +1,4 @@ -@import url('@reach/listbox/styles.css'); -@import url('@reach/menu-button/styles.css'); - -[data-reach-listbox-popover], -[data-reach-menu-list] { +[data-radix-menu-content] { background-color: hsl(var(--color-base)); border: var(--popover-border); border-radius: var(--border-radius-8); @@ -10,10 +6,11 @@ font-size: inherit; max-width: 250px; padding: var(--px-4); + font-family: var(--font-family); + color: hsl(var(--color-neutral)); } -[data-reach-listbox-option], -[data-reach-menu-item] { +[data-radix-collection-item] { border-radius: var(--border-radius-4); font-size: inherit; margin: var(--px-4); @@ -21,6 +18,9 @@ padding: var(--px-6) var(--px-8); text-overflow: ellipsis; white-space: nowrap; + outline: none; + cursor: pointer; + line-height: var(--line-height); &[data-selected], &[data-current-nav], @@ -33,9 +33,3 @@ margin-top: 0; } } - -[data-reach-listbox-button] { - border: none; - cursor: pointer; - padding: 0; -} diff --git a/packages/graphiql-react/src/ui/dropdown.tsx b/packages/graphiql-react/src/ui/dropdown.tsx index 9cdc5564544..90a1b744062 100644 --- a/packages/graphiql-react/src/ui/dropdown.tsx +++ b/packages/graphiql-react/src/ui/dropdown.tsx @@ -1,55 +1,40 @@ -import { - Listbox as ListboxRoot, - ListboxButton as ReachListboxButton, - ListboxInput, - ListboxOption, - ListboxPopover, -} from '@reach/listbox'; -import { - Menu as MenuRoot, - MenuButton as ReachMenuButton, - MenuItem, - MenuList, -} from '@reach/menu-button'; -import { ComponentProps, forwardRef } from 'react'; +import { ComponentProps, forwardRef, ReactElement } from 'react'; import { clsx } from 'clsx'; import { createComponentGroup } from '../utility/component-group'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import './dropdown.css'; -const MenuButton = forwardRef< - HTMLButtonElement, - ComponentProps ->((props, ref) => ( - -)); +const MenuButton = forwardRef>( + (props, ref) => ( + +