Skip to content
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

fix: graphiql enable schema description, default headers #2172

Merged
5 changes: 5 additions & 0 deletions .changeset/nice-shirts-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-yoga/graphiql': patch
---

- fix: enable graphiql schema description, default headers prop
3 changes: 2 additions & 1 deletion packages/graphiql/src/YogaGraphiQL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement {
credentials: 'same-origin',
specifiedByUrl: true,
directiveIsRepeatable: true,
schemaDescription: true,
...props,
headers: props.additionalHeaders || {},
})
Expand Down Expand Up @@ -152,6 +151,8 @@ export function YogaGraphiQL(props: YogaGraphiQLProps): React.ReactElement {
<GraphiQLProvider
plugins={[explorerPlugin]}
query={query}
headers={props.headers}
schemaDescription={true}
fetcher={fetcher}
>
<GraphiQLInterface
Expand Down
130 changes: 129 additions & 1 deletion packages/graphql-yoga/__integration-tests__/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { InMemoryLiveQueryStore } from '@n1ru4l/in-memory-live-query-store'
import { GraphQLLiveDirective, useLiveQuery } from '@envelop/live-query'
import { CORSOptions, createYoga, Repeater } from '../src/index.js'
import { renderGraphiQL } from '@graphql-yoga/render-graphiql'
import puppeteer, { Browser, Page } from 'puppeteer'
import puppeteer, { Browser, ElementHandle, Page } from 'puppeteer'
import { createServer, Server } from 'http'
import {
GraphQLObjectType,
Expand Down Expand Up @@ -231,7 +231,62 @@ describe('browser', () => {
return resultContents
}

const showGraphiQLSidebar = async () => {
// Click to show sidebar
await page.click(
'.graphiql-sidebar [aria-label="Show Documentation Explorer"]',
)
}

const getElementText = async (element: ElementHandle<Element>) =>
element.evaluate((el) => el.textContent?.trim())

describe('GraphiQL', () => {
it('should show default title', async () => {
await page.goto(`http://localhost:${port}${endpoint}`)

const title = await page.evaluate(() => document.title)

expect(title).toBe('Yoga GraphiQL')
})

it('should show default schema docs', async () => {
await page.goto(`http://localhost:${port}${endpoint}`)

// Click to show sidebar
await showGraphiQLSidebar()

const docsElement = await page.waitForSelector(
'.graphiql-markdown-description',
)

expect(docsElement).not.toBeNull()
const docs = await getElementText(docsElement!)

expect(docs).toBe(
'A GraphQL schema provides a root type for each kind of operation.',
)
})

it('should show editor tools by default', async () => {
await page.goto(`http://localhost:${port}${endpoint}`)

// If this button is visible, that mean editor tools is showing
const buttonHideEditor = await page.$(
'button[aria-label="Hide editor tools"]',
)

const editorTabs = await page.evaluate(() =>
Array.from(
document.querySelectorAll('.graphiql-editor-tools-tabs button'),
(e) => e.textContent,
),
)

expect(buttonHideEditor).not.toBeNull()
expect(editorTabs).toEqual(['Variables', 'Headers'])
})

it('execute simple query operation', async () => {
await page.goto(`http://localhost:${port}${endpoint}`)
await typeOperationText('{ alwaysTrue }')
Expand Down Expand Up @@ -437,6 +492,79 @@ describe('browser', () => {
})
})

describe('GraphiQL with custom options', () => {
let customGraphQLEndpoint: string

const schemaWithDescription = createTestSchema()
schemaWithDescription.description = 'Here is the custom docs for schema'

const defaultHeader = '{"Authorization":"Bearer test-auth-header"}'
const customServer = createServer(
createYoga({
schema: schemaWithDescription,
logging: false,
graphqlEndpoint: endpoint,
graphiql: {
title: 'GraphiQL Custom title here',
headers: defaultHeader,
},
renderGraphiQL,
}),
)

beforeAll(async () => {
await new Promise<void>((resolve) => customServer.listen(0, resolve))
const port = (customServer.address() as AddressInfo).port
customGraphQLEndpoint = `http://localhost:${port}${endpoint}`
})

afterAll(async () => {
await new Promise((resolve) => customServer.close(resolve))
})

it('should show custom title', async () => {
await page.goto(customGraphQLEndpoint)

const title = await page.evaluate(() => document.title)

expect(title).toBe('GraphiQL Custom title here')
})

it('should show custom schema docs', async () => {
await page.goto(customGraphQLEndpoint)

await showGraphiQLSidebar()
const docsElement = await page.waitForSelector(
'.graphiql-markdown-description',
)

expect(docsElement).not.toBeNull()
const docs = await getElementText(docsElement!)

expect(docs).toBe(schemaWithDescription.description)
})

it('should include default header', async () => {
await page.goto(customGraphQLEndpoint)

await page.evaluate(() => {
const tabs = Array.from(
document.querySelectorAll('.graphiql-editor-tools-tabs button'),
) as HTMLButtonElement[]
tabs.find((tab) => tab.textContent === 'Headers')!.click()
})

const headerContentEl = await page.waitForSelector(
'section.graphiql-editor-tool .graphiql-editor:not(.hidden) pre.CodeMirror-line',
)

expect(headerContentEl).not.toBeNull()
const headerContent = await getElementText(headerContentEl!)

expect(headerContent).toBe(defaultHeader)
})
})

describe('CORS', () => {
let anotherServer: Server
let anotherOriginPort: number
Expand Down
12 changes: 3 additions & 9 deletions packages/graphql-yoga/src/plugins/useGraphiQL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,18 @@ export type GraphiQLOptions = {
* will use its own default query.
*/
defaultQuery?: string
/**
* Whether to open the variable editor by default. Defaults to `true`.
*/
defaultVariableEditorOpen?: boolean
/**
tuanpt-0634 marked this conversation as resolved.
Show resolved Hide resolved
* The initial headers to render inside the header editor. Defaults to `"{}"`.
* The value should be a JSON encoded string, for example:
* `headers: JSON.stringify({Authorization: "Bearer your-auth-key"})`
*/
headers?: string
/**
* More info there: https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
*/
credentials?: RequestCredentials
/**
* Whether the header editor is enabled. Defaults to `true`.
*/
headerEditorEnabled?: boolean
/**
* The title to display at the top of the page. Defaults to `"YogaGraphiQL"`.
* The title to display at the top of the page. Defaults to `"Yoga GraphiQL"`.
*/
title?: string
/**
Expand Down