Skip to content

GraphiQL v6#4228

Draft
trevor-scheer wants to merge 39 commits into
mainfrom
graphiql-6
Draft

GraphiQL v6#4228
trevor-scheer wants to merge 39 commits into
mainfrom
graphiql-6

Conversation

@trevor-scheer
Copy link
Copy Markdown
Contributor

Tracking PR for the GraphiQL v6 redesign effort. This is the long-running integration branch that hosts work-in-progress against main.

Individual PRs target graphiql-6 and produce alpha releases via changesets pre-mode. When v6 is ready to ship, this branch will be merged into main.

See discussion #4219 for background and progress updates.

## Summary

- Swap the vestigial `graphiql-5` reference in
`.github/workflows/release.yml` for `graphiql-6` so the
changesets-action runs on pushes to the integration branch.
- Enter changesets pre-mode with the `alpha` tag so merges aggregate
into `6.0.0-alpha.N` prereleases.
- Add a changeset that seeds the alpha release line by bumping
`graphiql` to v6. No functional change — subsequent alphas accumulate
the redesign work.

## Test plan

- [ ] On merge: changesets-action opens a "Version Packages (alpha)" PR
bumping `graphiql` to `6.0.0-alpha.0`.
- [ ] Merging the version PR publishes `graphiql@6.0.0-alpha.0` to npm
with the `alpha` dist-tag.

Refs: #4219
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 7, 2026

🦋 Changeset detected

Latest commit: eb46e2f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@graphiql/react Minor
graphiql Major
@graphiql/plugin-doc-explorer Major
@graphiql/plugin-history Major
@graphiql/toolkit Patch
@graphiql/plugin-code-exporter Major
@graphiql/plugin-explorer Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

The latest changes of this PR are not available as canary, since there are no linked changesets for this PR.

## Summary

- Introduce a new `packages/graphiql-react/src/style/tokens.css` with
the v6 OKLCH-based design token system. Both dark and light palettes
ship together.
- Light theme activates explicitly via `data-theme="light"` or
automatically via `prefers-color-scheme: light` when no theme is pinned.
Dark remains the default.
- Existing v5 HSL variables are unchanged; nothing in `@graphiql/react`
references the new tokens yet.
- Future PRs will restyle components to consume the new tokens and shim
the v5 variables.

Refs: #4219
## Summary

Storybook gives us a fast feedback loop for iterating on the look of the
app and individual components — flipping themes/density/font-size
without spinning up the full GraphiQL shell.

- Bootstrap Storybook 10 in `@graphiql/react`. Stories colocated as
`<component>.stories.tsx`; ships one starter (`Spinner`) to validate the
pipeline.
- A global decorator wraps every story in `.graphiql-container` with
`data-theme` / `data-density` / `data-font-size` attributes, toggleable
from the Storybook toolbar.
- Move `Uri`, `KeyMod`, `KeyCode`, and `Range` out of the `utility`
barrel into direct imports from `utility/monaco-ssr`. The barrel was
bundling two unrelated concerns — lightweight UI helpers (`cn`, `pick`,
etc.) and heavy Monaco re-exports — so any story reaching for `cn`
transitively pulled Monaco's ESM bundle, which doesn't initialize
cleanly inside Storybook's preview iframe. Splitting them keeps UI
primitives lightweight.

## Run locally

From the repo root:

```
yarn storybook         # dev server on http://localhost:6006
yarn build-storybook   # static build under packages/graphiql-react/storybook-static
```

Refs: #4219
## Summary

Component a11y is covered by Storybook + axe; this is the full-app
counterpart. `cypress-axe` runs axe at four checkpoints during a normal
session (initial render, after running a query, with the docs panel
open, with the history panel open) and gates PRs against a committed
baseline.

`cypress/.a11y-baseline.json` pins today's accepted violations —
color-contrast in several spots, a couple of nested-interactive cases,
link-in-text-block in the docs panel. CI fails on net-new only.

The spec lives alongside the existing Cypress suite, so it runs as part
of the normal `yarn e2e` flow. `cypress.config.ts` gets a small
`writeBaseline` Node task so the spec can persist baseline updates from
inside the browser.

## Refresh baseline

```
yarn workspace graphiql test:a11y:update
```

Refs: #4219
)

## Summary

Component-level a11y for v6. `@storybook/addon-a11y` surfaces axe
results next to each story while you're working on it;
`@storybook/addon-vitest` folds those same checks into the existing
Vitest suite so they run as part of `yarn test` in CI.

The model is per-story `parameters.a11y.test`:

- `'error'` (default) — axe violations fail the test
- `'todo'` — warn only, for stories with known issues we plan to fix
- `'off'` — skip a11y for the story

`vitest.config.mts` is split into two projects:

- `unit` — existing jsdom suite, unchanged behavior
- `storybook` — Vitest browser mode (Playwright Chromium), picks up
`.stories.*` files

The PR CI workflow gets one new step: `yarn playwright install
--with-deps chromium` ahead of `yarn test`.

## Run locally

```
yarn workspace @graphiql/react test                       # both projects
yarn workspace @graphiql/react vitest run --project=unit  # unit only
yarn workspace @graphiql/react vitest run --project=storybook
```

The Storybook a11y panel surfaces the same axe results live during `yarn
workspace @graphiql/react storybook`.

Refs: #4219
## Summary

- Migrate `Button`, `UnStyledButton`, `ToolbarButton`, and
`ExecuteButton` CSS to v6 OKLCH tokens.
- Add `variant?: 'default' | 'primary'` to `Button`; `primary` renders
the Run-button style.
- Switch `:focus` to `:focus-visible` on interactive states so the focus
ring no longer fires on mouse click. Aligns with [MDN
`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible)
and WCAG 2.4.7 (Focus Visible).
- Import `clsx` directly in `button` and `toolbar-button`, following the
leaf-module pattern from #4272. A follow-up PR will convert remaining
callers and remove the `cn` re-export.
- Add Storybook stories: `Primitives/Button` (Default, Primary, Success,
Error, Disabled) and `Primitives/ToolbarButton` (Default).

## Test plan

- [x] Open Storybook `Primitives/Button` and verify each variant matches
the design.
- [x] Tab into the buttons, then mouse-click them. Focus ring appears on
Tab only, not on click.
- [x] Open `Primitives/ToolbarButton`. Hover the icon button; a v6
tooltip appears.
- [x] Run `yarn dev:graphiql`. The Run button and toolbar buttons match
the new design.

Refs: #4219
## Summary

`tokens.css` scopes the v6 OKLCH cascade to `[data-theme='light']` /
`[data-theme='dark']` selectors, but `setTheme` was only writing
`graphiql-light` / `graphiql-dark` classes onto `<body>`. With no
`data-theme` attribute set, v6 components fell through to the OS
`prefers-color-scheme` media-query fallback regardless of the in-app
toggle. This PR mirrors the active theme onto `document.documentElement`
as `data-theme` so the selectors actually match; the existing body
classes are preserved for any consumer CSS that depends on them.

## Test plan

- [ ] Open the [deploy
preview](https://deploy-preview-4293--graphiql-test.netlify.app). With
the GraphiQL theme dropdown set to System, confirm `<html>` has no
`data-theme` attribute and v6 chrome follows your OS preference.
- [ ] Toggle the theme to Light; confirm `<html>` gains
`data-theme="light"` and v6 components (`TopBar`, `StatusBar`,
`ActivityRail`, etc., once those PRs land) all switch to light tokens.
- [ ] Toggle the theme to Dark; confirm `<html>` gains
`data-theme="dark"` and v6 components switch to dark tokens.
- [ ] Toggle back to System; confirm `data-theme` is removed and the v6
components revert to following the OS preference.

Refs: #4219
## Summary

This PR introduces the top bar that anchors the new GraphiQL v6 layout.
It renders the brand, an endpoint URL display, a command palette button,
and a primary Run button across the top of the app. The endpoint URL and
command palette are intentional placeholders; their behavior will fill
in once the transport and command palette work land.

## Test plan

- [x] Open the [deploy
preview](https://deploy-preview-4288--graphiql-test.netlify.app).
Confirm the top bar spans the full width above the editor/sidebar split,
showing the brand, endpoint URL placeholder, command palette button, and
Run button.
- [x] In the deploy preview, type a query and click Run. Confirm the
button disables for the duration of the fetch and re-enables once
results arrive.
- [x] In the deploy preview, open the settings dialog and toggle
dark/light theme. Confirm the top bar reads from the active tokens in
both modes.
- [x] Tab through the bar with the keyboard. Confirm the Run button is
reachable and activates with Space or Enter.

Refs: #4219
## Summary

This PR adds a 24px footer pinned to the bottom of the GraphiQL app. It
shows the connection status (with a small dot indicator), schema type
count, plugin count, the document encoding, and the language label. The
outer container becomes a flex column with a new `graphiql-body` wrapper
for the sidebar-plus-main row, which keeps the status bar fixed beneath
a scrolling content area.

## Test plan

- [x] Open the [deploy
preview](https://deploy-preview-4289--graphiql-test.netlify.app).
Confirm the status bar appears at the bottom of the app and reads
"Disconnected" with a red dot before any schema loads.
- [x] In the deploy preview, the default endpoint should load a schema;
confirm the status flips to "Connected" with a green dot, and a schema
type count appears.
- [x] Resize the browser window narrow; confirm the status bar stays
pinned at the bottom and the content area above scrolls independently.
- [x] Open the settings dialog and toggle dark/light theme; confirm the
bar's chrome follows the active theme.

Refs: #4219
## Summary

This PR replaces the v5 sidebar icon strip with a new `ActivityRail`
that renders registered plugins as a flat list with the settings gear
pinned at the bottom. An `ActivityBar` wrapper in the `graphiql` package
owns the resize sync and the settings/theme dialogs that previously
lived inside the old `Sidebar`. There are no plugin API changes;
registered plugins continue to render through the existing `plugins`
array.

## Test plan

- [x] Open the [deploy
preview](https://deploy-preview-4290--graphiql-test.netlify.app).
Confirm the activity rail renders along the left edge with each plugin's
icon and the settings gear at the bottom.
- [x] Click between plugin icons; confirm the active plugin shows the
accent indicator and the panel content swaps.
- [x] Click the settings gear; confirm the settings dialog opens.
- [x] Drag the divider between the panel and the editor area; confirm
the rail stays put and the side panel resizes.
- [x] Toggle dark/light theme via the settings dialog; confirm the rail
reads from the active tokens in both modes.

Refs: #4219
## Summary

This PR adds a `SidePanel` that hosts the active plugin's content next
to the new activity rail. The panel renders nothing when no plugin is
active, and remains user-resizable via the existing drag handle.
Together with the activity rail change, it completes the left-side
navigation chrome for the v6 layout.

## Test plan

- [x] Open the [deploy
preview](https://deploy-preview-4291--graphiql-test.netlify.app). With
no plugin active, confirm the side panel area is collapsed and the
editor takes the full width.
- [x] Click a plugin icon in the activity rail; confirm the side panel
opens with that plugin's content.
- [x] Drag the divider between the panel and the editor; confirm the
panel resizes smoothly and the width persists when you switch between
plugins.
- [x] Click the active plugin's icon a second time; confirm the panel
closes.
- [x] Toggle dark/light theme via the settings dialog; confirm the panel
chrome reads from the active tokens.

Refs: #4219
## Summary

This PR restyles the editor tab strip for v6 and introduces dirty-state
and save affordances. Each tab now shows an accent dot when its query
differs from the last saved snapshot, and hovering a tab reveals a close
button. A new save action, bound to `Cmd-S` / `Ctrl-S`, snapshots the
active tab's query, and the right side of the strip gains prettify,
copy, and save buttons that surface the existing actions inline.

A note on save: today `saveQuery()` just updates the `lastSavedQuery`
snapshot to clear the dirty dot. Tab content already auto-persists to
localStorage, so save isn't writing anything new. The hook is in place
so collections (planned as a follow-up package) can give save real
semantics, snapshotting the active tab into the current collection
entry. Until that lands, the indicator is a cosmetic baseline.

## Test plan

- [x] Open the [deploy
preview](https://deploy-preview-4292--graphiql-test.netlify.app) with at
least two tabs. Edit the query in one tab and confirm the accent dot
appears next to its label.
- [x] Press `Cmd-S` (macOS) or `Ctrl-S` (Windows/Linux); confirm the dot
disappears.
- [x] Click the Save button in the tab strip's right-side action area;
confirm the dot also clears.
- [x] Hover over a dirty tab; confirm the close button appears and the
dirty dot shifts left so it doesn't overlap the close icon.
- [x] Close an inactive tab via the close button; confirm the active tab
stays active. Close the active tab; confirm focus moves to an adjacent
tab.
- [x] Drag tabs to reorder them; confirm the dirty state follows the
correct tab.
- [x] Tab through the strip with the keyboard; confirm both tab labels
and the right-side action buttons are reachable in a logical order.

Refs: #4219
## Summary

- Migrate `@graphiql/plugin-doc-explorer` CSS from v5 HSL tokens to v6
OKLCH tokens.
- Use `PanelHeader` for the panel header.
- Type names: `--accent-orange`. Field and enum names:
`--accent-green-light`. Argument names: `--accent-purple`.

## Test plan

- [x] Open the Docs panel in the example app; confirm type/field colors
match the v6 palette and navigation (back button, search) still works.
- [x] Check the `DocExplorer` Storybook stories: schema overview, type
detail variants (object, enum, input), field detail, and token colors
story.
- [x] Verify the search overlay still expands over the header title
(focus the search icon while on a nested type page).

Refs: #4219
## Summary

- Rewrite `MONACO_THEME_DATA` in `constants.ts`: both `graphiql-DARK`
and `graphiql-LIGHT` now cover all GraphQL token types (keywords, types,
fields, `$variables`, `@directives`, strings, numbers, operators,
comments) using the v6 accent palette.
- Editor surface colors (suggest widget, hover widget, quick input) move
from the old pink `--color-primary` to `--accent-green`.
- `editor.css`: swaps `hsl(var(--color-primary))` for
`oklch(var(--accent-green))` in the `.highlight` and
`input:focus-visible` rules.
- Adds `monaco-themes.test.ts` to pin token rule shapes for both themes.

## Test plan

- [x] Open the editor, write a query with keywords, type names,
variables, directives, strings, and a comment; confirm syntax colors
match the v6 palette (see the changeset for the token reference).
- [x] Toggle the theme via the settings dropdown; confirm Monaco
switches from dark to light and back, with distinct and correct syntax
colors in each.
- [x] Verify the autocomplete suggestion widget, hover widget, and
quick-input palette use the green accent instead of pink.
- [x] Open the editor with the system set to light mode and no pinned
theme; confirm Monaco uses the light palette.

Refs: #4219
## Summary

- Replaces the `examples/monaco-graphql-react-vite` build target with a
minimal fixture under
`packages/monaco-graphql/__fixtures__/bundle-test/` that imports only
`monaco-graphql`'s public API and wires graphql + json workers. No
React, no example-app baggage, no TypeScript language support.
- Replaces the previous `execa`-driven `yarn workspace ... build`
shell-out with Vite's programmatic `build()` API. Drops `write: false`,
`minify: false`, `target: 'esnext'`, `reportCompressedSize: false`, and
`treeshake: false` since the assertion only cares about the chunk file
list, not the bundle contents or size.
- Adds an explicit negative regex assertion against
`/typescript|tsMode|tsWorker|ts\.worker|cssMode|css\.worker|htmlMode|html\.worker/i`,
so the test's intent ("don't bundle unwanted Monaco language modules")
is readable from the assertion, not just the snapshot.
- Removes the now-unused `execa` devDependency.
- Local test runtime drops from ~8.5s to ~2.3s. CI runtime is ~32s,
dominated by Rollup traversing monaco-editor's module graph; the new
minimal-fixture surface and dropped transforms don't change that floor.
Vitest timeout set to 60s (~28s headroom).

## Test plan

- [x] Run `yarn workspace monaco-graphql test` and confirm both tests
pass in under 5s locally.
- [x] Confirm the snapshot lists only editor core, graphql, and json
chunks (no TypeScript, CSS, or HTML language service modules).
- [x] Sanity-check the negative assertion by manually adding a
TypeScript worker import to the fixture, rerunning the test, and
confirming it fails with the explicit message before reverting.
## Summary

`KeycapHint` callers now use semantic names (`MODIFIER.Meta`, etc.) and
the component renders the right glyph or text per OS. `MODIFIER.Meta`
becomes `⌘` on macOS and `Ctrl` on other platforms; `Ctrl`/`Alt`/`Shift`
become Mac glyphs (`⌃`/`⌥`/`⇧`) on macOS and plain text elsewhere.
`Enter` always renders as `⏎`.

## Test plan

- [ ] Open Storybook `Primitives/KeycapHint` on macOS: chips render as
⌘/⏎.
- [ ] In Chrome devtools, override the user agent to a Windows or Linux
UA and reload Storybook: same stories render as Ctrl/⏎.
- [ ] Confirm the run-shortcut chip and command-palette chip in the top
bar follow the same OS detection.
## Summary

- `PanelHeader` for the docs panel becomes a fixed eyebrow label,
`Schema Explorer`. The type name moves into a new breadcrumb row below
the header.
- New `SchemaDocumentation` root view: two eyebrow sections (`ROOT
TYPES` and `ALL SCHEMA TYPES · N`) replace the old accordion. Root
operation rows show a `MethodPill` and target type name. All-types rows
show a kind badge (`TYPE`, `SCALAR`, `ENUM`, `INPUT`, `INTERFACE`,
`UNION`) and type name in mono.
- New `Breadcrumb` row: mono font, depth segments color-coded as
`--accent-blue` / `--accent-green-light` / `--fg-strong`, chevron
separators; clicking a segment navigates back to that depth.
- New `SearchRow` content-area component: inline `Search schema` input
with keycap hint; listbox renders as a floating popover in the content
area, replacing the old absolutely-positioned search overlay.
- New `TypeCard`: `TYPE` badge (mono, uppercase, accent-blue tinted),
type name in mono, description, and an `implements X · Y` row with
`--accent-orange` links.
- New `FieldsList`: collapsible `FIELDS · N` header, mono rows with
field name in `--accent-green-light`, colon in `--fg-muted`, return type
in `--accent-orange`, optional description in `--fg-subtle`, and a 2px
`--accent-blue` left border on the active row. Horizontal padding bumped
to 16px across all content sections for breathing room.
- `TypeDocumentation` gains a `hideHeader` prop so it can suppress its
own description / implements / fields block when `TypeCard` +
`FieldsList` render those above.

## Test plan

- [ ] Open the Docs panel. Confirm the header shows `SCHEMA EXPLORER`
with no extra buttons, a search input, and two eyebrow sections: `ROOT
TYPES` and `ALL SCHEMA TYPES · N`.
- [ ] Confirm each root operation row shows its `MethodPill`
(`QRY`/`MUT`/`SUB`), operation label, and target type name. Click a row
and confirm it navigates into the type detail.
- [ ] In `ALL SCHEMA TYPES`, confirm each row shows a kind badge and
mono type name. Click a row and confirm navigation.
- [ ] Confirm the breadcrumb row appears below the header when inside a
type, with color-coded segments. Click an intermediate segment and
confirm it navigates back.
- [ ] Confirm `TypeCard` shows the kind badge, name, description, and
`implements` list for an object type that implements interfaces.
- [ ] Confirm `FieldsList` shows mono rows with green field names and
orange return types; click a field row and confirm it navigates in.
- [ ] Type in the inline search row and confirm the listbox popover
appears with results.
- [ ] Toggle light / dark theme; confirm root view and type-detail both
render correctly.
- [ ] Navigate to an enum type and confirm enum values still render
below.

Refs: #4219
## Summary

- Migrate `@graphiql/plugin-history` CSS to OKLCH tokens.
- New row layout: status dot, mono query name, variables snippet below,
action buttons on the right.
- Replace the bespoke header div with `PanelHeader`; subtitle carries
the ⌥-click hint.
- Storybook stories: `Empty`, `FewRows`, `ManyRows`, `Mixed`, and
individual item variants.

## Test plan

- [x] Open the History panel; confirm the header reads "History" with
the subtitle.
- [x] Run a query; verify the row appears with a green status dot, mono
query name, and a variable snippet if variables were provided.
- [x] Click a history row; confirm the query loads into the editor.
- [ ] ⌥-click a row (diff behavior) and confirm it still works.
- [x] Click "Clear"; confirm rows are removed and the button shows
"Cleared" briefly.
- [x] Open Storybook under "Plugins/History"; verify all stories render
correctly.

Refs: #4219
## Summary

- Show a `MethodPill` (`QRY`/`MUT`/`SUB`) per History row in place of
the green status dot.
- `QueryStoreItem` gains an optional `operation` field, populated at
write time from the parsed query.
- The Clear button drops the green-on-success state; rows disappearing
is the feedback.

## Test plan

- Open the History panel, run a query/mutation/subscription, and confirm
each row shows the right pill.
- Click "Clear"; rows are removed without any color flash on the button.
- Open Storybook under "Plugins/History"; check the updated `Mixed`
story.

Refs: #4219
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant