-
Notifications
You must be signed in to change notification settings - Fork 576
fix(deps): update dependency @graphiql/plugin-explorer to v5 #4033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
fc37113
3b9c751
ebd952e
428467c
e56e59c
bf63c14
63aa19a
84f3c1b
58e57ea
91888e7
35fe363
dcdf60e
2e6981d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
"@graphql-yoga/graphiql": patch | ||
--- | ||
dependencies updates: | ||
- Updated dependency [`@graphiql/plugin-explorer@5.1.1` ↗︎](https://www.npmjs.com/package/@graphiql/plugin-explorer/v/5.1.1) (from `^3.0.0`, in `dependencies`) | ||
- Updated dependency [`@graphql-tools/url-loader@8.0.33` ↗︎](https://www.npmjs.com/package/@graphql-tools/url-loader/v/8.0.33) (from `^8.0.15`, in `dependencies`) | ||
- Updated dependency [`graphiql@5.2.0` ↗︎](https://www.npmjs.com/package/graphiql/v/5.2.0) (from `3.1.1`, in `dependencies`) | ||
- Removed dependency [`use-url-search-params@2.5.1` ↗︎](https://www.npmjs.com/package/use-url-search-params/v/2.5.1) (from `dependencies`) |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,13 +1,12 @@ | ||||||
import 'graphiql/style.css'; | ||||||
import '@graphiql/plugin-explorer/style.css'; | ||||||
import { GraphiQL, GraphiQLProps } from 'graphiql'; | ||||||
import { DocumentNode, Kind, parse } from 'graphql'; | ||||||
import { explorerPlugin } from '@graphiql/plugin-explorer'; | ||||||
import '@graphiql/plugin-explorer/dist/style.css'; | ||||||
import { GraphiQL, GraphiQLInterface, GraphiQLProps, GraphiQLProvider } from 'graphiql'; | ||||||
import { Fetcher, FetcherOpts, FetcherParams } from '@graphiql/toolkit'; | ||||||
import { LoadFromUrlOptions, SubscriptionProtocol, UrlLoader } from '@graphql-tools/url-loader'; | ||||||
import 'graphiql/graphiql.css'; | ||||||
import { DocumentNode, Kind, parse } from 'graphql'; | ||||||
import 'json-bigint-patch'; | ||||||
import React, { useMemo, useState } from 'react'; | ||||||
import { useUrlSearchParams } from 'use-url-search-params'; | ||||||
import React, { useMemo } from 'react'; | ||||||
import { YogaLogo } from './YogaLogo'; | ||||||
import './styles.css'; | ||||||
|
||||||
|
@@ -43,6 +42,19 @@ export type YogaGraphiQLProps = Partial<GraphiQLProps> & | |||||
* Extra headers you always want to pass with users' headers input | ||||||
*/ | ||||||
additionalHeaders?: LoadFromUrlOptions['headers']; | ||||||
|
||||||
/** | ||||||
* @deprecated Use `initialQuery` instead. | ||||||
*/ | ||||||
query?: GraphiQLProps['initialQuery']; | ||||||
/** | ||||||
* @deprecated Use `initialHeaders` instead. | ||||||
*/ | ||||||
headers?: GraphiQLProps['initialHeaders']; | ||||||
/** | ||||||
* @deprecated Use `initialVariables` instead. | ||||||
*/ | ||||||
variables?: GraphiQLProps['initialVariables']; | ||||||
}; | ||||||
|
||||||
export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement { | ||||||
|
@@ -81,10 +93,6 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement { | |||||
|
||||||
const endpoint = new URL(props.endpoint ?? location.pathname, location.href).toString(); | ||||||
|
||||||
const type = { | ||||||
query: String, | ||||||
}; | ||||||
|
||||||
const urlLoader = useMemo(() => new UrlLoader(), []); | ||||||
|
||||||
const fetcher = useMemo(() => { | ||||||
|
@@ -126,81 +134,71 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement { | |||||
}; | ||||||
}, [urlLoader, endpoint, props.fetcher]) as Fetcher; | ||||||
|
||||||
const [params, setParams] = useUrlSearchParams( | ||||||
{ | ||||||
query: props.defaultQuery || initialQuery, | ||||||
}, | ||||||
type, | ||||||
false, | ||||||
); | ||||||
|
||||||
const [query, setQuery] = useState(params['query']?.toString()); | ||||||
const explorer = explorerPlugin({ | ||||||
showAttribution: true, | ||||||
}); | ||||||
|
||||||
if (props.query && !props.onEditQuery) { | ||||||
// eslint-disable-next-line no-console | ||||||
console.warn( | ||||||
'If you provide `query` prop, you should also provide `onEditQuery` prop to handle query changes.', | ||||||
); | ||||||
} | ||||||
const currentUrl = new URL(location.href); | ||||||
const initialQueryFromUrl = currentUrl.searchParams.get('query') || props.query || initialQuery; | ||||||
|
||||||
const { | ||||||
query: deprecatedInitialQuery = initialQueryFromUrl, | ||||||
headers: deprecatedInitialHeaders, | ||||||
variables: deprecatedInitialVariables, | ||||||
...otherProps | ||||||
} = props; | ||||||
|
||||||
return ( | ||||||
<div className="graphiql-container"> | ||||||
<GraphiQLProvider | ||||||
<GraphiQL | ||||||
// default values that can be override by props | ||||||
shouldPersistHeaders | ||||||
plugins={[explorer]} | ||||||
schemaDescription={true} | ||||||
inputValueDeprecation={true} | ||||||
query={query} | ||||||
{...props} | ||||||
isHeadersEditorEnabled | ||||||
defaultEditorToolsVisibility | ||||||
initialQuery={deprecatedInitialQuery} | ||||||
defaultHeaders={deprecatedInitialHeaders} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The prop name
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
initialVariables={deprecatedInitialVariables} | ||||||
onEditQuery={(query, ast) => { | ||||||
currentUrl.searchParams.set('query', query); | ||||||
history.replaceState({}, '', currentUrl); | ||||||
props.onEditQuery?.(query, ast); | ||||||
}} | ||||||
{...otherProps} | ||||||
fetcher={fetcher} | ||||||
> | ||||||
<GraphiQLInterface | ||||||
isHeadersEditorEnabled | ||||||
defaultEditorToolsVisibility | ||||||
{...props} | ||||||
onEditQuery={(query, ast) => { | ||||||
setParams({ | ||||||
query, | ||||||
}); | ||||||
setQuery(query); | ||||||
props.onEditQuery?.(query, ast); | ||||||
}} | ||||||
> | ||||||
<GraphiQL.Logo> | ||||||
<div style={{ display: 'flex', alignItems: 'center' }}> | ||||||
{typeof props?.logo === 'string' ? ( | ||||||
// if the logo is a string, then it's coming when rendering graphiql as a static page (see render-graphiql) | ||||||
<div | ||||||
style={{ width: 40, display: 'flex' }} | ||||||
dangerouslySetInnerHTML={{ __html: props.logo }} | ||||||
/> | ||||||
) : ( | ||||||
// otherwise, it's used inside react and we can render it as a component | ||||||
<div style={{ width: 40, display: 'flex' }}>{props?.logo || <YogaLogo />}</div> | ||||||
)} | ||||||
{typeof props?.title === 'string' ? ( | ||||||
// if the title is a string, then it's coming when rendering graphiql as a static page (see render-graphiql) | ||||||
<span dangerouslySetInnerHTML={{ __html: props.title }} /> | ||||||
) : ( | ||||||
// otherwise, it's used inside react and we can render it as a component | ||||||
<span> | ||||||
{props?.title || ( | ||||||
<> | ||||||
Yoga Graph | ||||||
<em>i</em> | ||||||
QL | ||||||
</> | ||||||
)} | ||||||
</span> | ||||||
)} | ||||||
</div> | ||||||
</GraphiQL.Logo> | ||||||
</GraphiQLInterface> | ||||||
</GraphiQLProvider> | ||||||
<GraphiQL.Logo> | ||||||
<div style={{ display: 'flex', alignItems: 'center' }}> | ||||||
{typeof props?.logo === 'string' ? ( | ||||||
// if the logo is a string, then it's coming when rendering graphiql as a static page (see render-graphiql) | ||||||
<div | ||||||
style={{ width: 40, display: 'flex' }} | ||||||
dangerouslySetInnerHTML={{ __html: props.logo }} | ||||||
/> | ||||||
) : ( | ||||||
// otherwise, it's used inside react and we can render it as a component | ||||||
<div style={{ width: 40, display: 'flex' }}>{props?.logo || <YogaLogo />}</div> | ||||||
)} | ||||||
{typeof props?.title === 'string' ? ( | ||||||
// if the title is a string, then it's coming when rendering graphiql as a static page (see render-graphiql) | ||||||
<span dangerouslySetInnerHTML={{ __html: props.title }} /> | ||||||
) : ( | ||||||
// otherwise, it's used inside react and we can render it as a component | ||||||
<span> | ||||||
{props?.title || ( | ||||||
<> | ||||||
Yoga Graph | ||||||
<em>i</em> | ||||||
QL | ||||||
</> | ||||||
)} | ||||||
</span> | ||||||
)} | ||||||
</div> | ||||||
</GraphiQL.Logo> | ||||||
</GraphiQL> | ||||||
</div> | ||||||
); | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,14 @@ import { setTimeout as setTimeout$ } from 'node:timers/promises'; | |
// eslint-disable-next-line | ||
import { Browser, chromium, ElementHandle, Page } from 'playwright'; | ||
import { fakePromise } from '@whatwg-node/server'; | ||
import { CORSOptions, createSchema, createYoga, GraphiQLOptions, Repeater } from '../src/index.js'; | ||
import { | ||
ansiCodes, | ||
CORSOptions, | ||
createSchema, | ||
createYoga, | ||
GraphiQLOptions, | ||
Repeater, | ||
} from '../src/index.js'; | ||
|
||
let resolveOnReturn: VoidFunction; | ||
const timeoutsSignal = new AbortController(); | ||
|
@@ -251,12 +258,36 @@ describe('browser', () => { | |
let port: number; | ||
const server = createServer(yogaApp); | ||
|
||
function wrapColor(msg: string, color?: string) { | ||
if (color != null && color in ansiCodes) { | ||
return `${ansiCodes[color as keyof typeof ansiCodes]}${msg}${ansiCodes.reset}`; | ||
} | ||
return msg; | ||
} | ||
|
||
beforeAll(async () => { | ||
await new Promise<void>(resolve => server.listen(0, resolve)); | ||
port = (server.address() as AddressInfo).port; | ||
browser = await chromium.launch({ | ||
headless: process.env['PLAYWRIGHT_HEADLESS'] !== 'false', | ||
args: ['--incognito', '--no-sandbox', '--disable-setuid-sandbox'], | ||
// eslint-disable-next-line unicorn/no-negated-condition, no-extra-boolean-cast | ||
logger: !!process.env['DEBUG'] | ||
? { | ||
isEnabled(_name: string) { | ||
return true; | ||
}, | ||
log(name, severity, message, args, hints) { | ||
if (severity === 'error') { | ||
// eslint-disable-next-line no-console | ||
console.error(wrapColor(`[${name}] ${message}`, hints.color), ...args); | ||
} else { | ||
// eslint-disable-next-line no-console | ||
console.log(wrapColor(`[${name}] ${message}`), ...args); | ||
} | ||
}, | ||
} | ||
: undefined, | ||
}); | ||
}); | ||
beforeEach(async () => { | ||
|
@@ -281,38 +312,41 @@ describe('browser', () => { | |
}); | ||
|
||
const typeOperationText = async (text: string) => { | ||
await page.type('.graphiql-query-editor .CodeMirror textarea', text, { delay: 300 }); | ||
await page.type('[data-uri*="operation"] textarea', text, { delay: 300 }); | ||
// TODO: figure out how we can avoid this wait | ||
// it is very likely that there is a delay from textarea -> react state update | ||
await setTimeout$(300); | ||
}; | ||
|
||
const typeVariablesText = async (text: string) => { | ||
await page.type('[aria-label="Variables"] .CodeMirror textarea', text, { delay: 100 }); | ||
await page.type('[data-uri*="variables"] textarea', text, { delay: 100 }); | ||
// TODO: figure out how we can avoid this wait | ||
// it is very likely that there is a delay from textarea -> react state update | ||
await setTimeout$(100); | ||
}; | ||
|
||
const waitForResult = async (): Promise<object> => { | ||
await page.waitForSelector('.graphiql-response .CodeMirror-code'); | ||
await page.waitForSelector('[data-uri*="response"] textarea'); | ||
await page.waitForFunction( | ||
() => | ||
!!window.document.querySelector('.graphiql-response .CodeMirror-code')?.textContent?.trim(), | ||
// @ts-expect-error - value is not null | ||
() => !!window.document.querySelector('[data-uri*="response"] textarea')?.value?.trim(), | ||
); | ||
const resultContents = await page.evaluate(() => { | ||
return window.document | ||
.querySelector('.graphiql-response .CodeMirror-code') | ||
?.textContent?.trim() | ||
.replaceAll('\u00A0', ' '); | ||
return ( | ||
window.document | ||
.querySelector('[data-uri*="response"] textarea') | ||
// @ts-expect-error - value is not null | ||
?.value?.trim() | ||
Comment on lines
+331
to
+339
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nitpick] Instead of using @ts-expect-error, consider using optional chaining or proper type assertions. The comment suggests confidence that value is not null, so a non-null assertion (!) would be more appropriate. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
.replaceAll('\u00A0', ' ') | ||
); | ||
}); | ||
|
||
return JSON.parse(resultContents!); | ||
}; | ||
|
||
const showGraphiQLSidebar = async () => { | ||
// Click to show sidebar | ||
await page.click('.graphiql-sidebar [aria-label="Show Documentation Explorer"]'); | ||
await page.click('[aria-label="Show Documentation Explorer"]'); | ||
}; | ||
|
||
const getElementText = async (element: ElementHandle<Element>) => | ||
|
@@ -472,8 +506,9 @@ describe('browser', () => { | |
|
||
await page.waitForFunction(() => { | ||
const value = window.document | ||
.querySelector('.graphiql-response .CodeMirror-code') | ||
?.textContent?.trim() | ||
.querySelector('[data-uri*="response"] textarea') | ||
// @ts-expect-error - value is not null | ||
?.value?.trim() | ||
.replaceAll('\u00A0', ' '); | ||
|
||
return value?.includes('2'); | ||
|
@@ -540,27 +575,14 @@ describe('browser', () => { | |
|
||
it('should include default header', async () => { | ||
await page.goto(customGraphQLEndpoint); | ||
|
||
await page.evaluate(() => { | ||
const tabs = Array.from( | ||
document.querySelectorAll('.graphiql-editor-tools button'), | ||
) as HTMLButtonElement[]; | ||
tabs.find(tab => tab.textContent === 'Headers')!.click(); | ||
}); | ||
|
||
const headerContentEl$ = page.waitForSelector( | ||
'section.graphiql-editor-tool .graphiql-editor:not(.hidden) pre.CodeMirror-line', | ||
); | ||
|
||
await expect(headerContentEl$).resolves.not.toBeNull(); | ||
|
||
await expect( | ||
headerContentEl$.then(headerContentEl => getElementText(headerContentEl!)), | ||
).resolves.toBe(defaultHeader); | ||
await page.click('[data-name="headers"]'); | ||
await page.click('[data-uri*="headers"]'); | ||
const headerValue = await page.inputValue('[data-uri*="headers"] textarea'); | ||
expect(headerValue).toBe(defaultHeader); | ||
}); | ||
|
||
it('supports input value deprecations', async () => { | ||
await page.goto(`http://localhost:${port}${endpoint}`); | ||
await page.goto(customGraphQLEndpoint); | ||
await page.click('.graphiql-un-styled[data-index="0"]'); | ||
await page.click('a.graphiql-doc-explorer-type-name'); | ||
await page.getByText('Show Deprecated Fields').click(); | ||
|
@@ -764,7 +786,6 @@ describe('browser', () => { | |
endpoint: anotherEndpoint, | ||
}; | ||
await page.goto(`http://localhost:${port}${endpoint}`); | ||
await page.waitForSelector('.graphiql-query-editor .CodeMirror textarea'); | ||
await typeOperationText('{ greetings }'); | ||
await page.click(playButtonSelector); | ||
await expect(waitForResult()).resolves.toEqual({ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
Kind
import appears to be unused in this file. Consider removing it to keep imports clean.Copilot uses AI. Check for mistakes.