feat(ide)!: rebuild IDE on Smart Cache primitives with WordPress components and CodeMirror 6#3784
Conversation
|
@josephfusco is attempting to deploy a commit to the WPGraphql Team on Vercel. A member of the Team first needs to authorize it. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3784 +/- ##
=========================================
+ Coverage 83.5% 84.6% +1.1%
+ Complexity 5284 4362 -922
=========================================
Files 286 221 -65
Lines 22753 19446 -3307
=========================================
- Hits 18995 16446 -2549
+ Misses 3758 3000 -758
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR begins a ground-up rebuild of the WPGraphQL IDE UI: moving off GraphiQL’s UI components, adopting WordPress admin components, and replacing the editors with CodeMirror 6 while extending the app store for execution-related state.
Changes:
- Replace GraphiQL-based UI/layout with a new
IDELayoutbuilt on@wordpress/componentsand CodeMirror 6 editors. - Add new app-store state for variables/headers/response/isFetching plus hooks for schema introspection and execution.
- Update docs, build/enqueue assets, and add/adjust unit tests + Jest transforms for CM6 dependencies.
Reviewed changes
Copilot reviewed 38 out of 39 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/wp-graphql-ide/wpgraphql-ide.php | Adjusts enqueued render CSS asset name/version; removes GraphiQL-specific admin notice padding. |
| plugins/wp-graphql-ide/tests/unit/specs/hooks/useSchema.test.js | Adds unit coverage for the new useSchema hook. |
| plugins/wp-graphql-ide/tests/unit/specs/hooks/useExecution.test.js | Adds unit coverage for the new useExecution hook. |
| plugins/wp-graphql-ide/tests/unit/specs/editors/ResponseViewer.test.js | Adds tests for the new CM6-based response viewer. |
| plugins/wp-graphql-ide/tests/unit/specs/editors/JSONEditor.test.js | Adds tests for the new CM6-based JSON editor. |
| plugins/wp-graphql-ide/tests/unit/specs/editors/GraphQLEditor.test.js | Adds tests for the new CM6-based GraphQL editor. |
| plugins/wp-graphql-ide/tests/unit/jest.config.js | Updates Jest transforms to compile CM6/GraphQL LS dependencies in node_modules. |
| plugins/wp-graphql-ide/styles/wpgraphql-ide.css | Removes GraphiQL/CM5-specific styling. |
| plugins/wp-graphql-ide/src/stores/app/app-store-selectors.js | Adds selectors for variables/headers/response/isFetching. |
| plugins/wp-graphql-ide/src/stores/app/app-store-reducer.js | Extends initial state + reducer cases for new execution-related fields. |
| plugins/wp-graphql-ide/src/stores/app/app-store-actions.js | Adds actions for variables/headers/response/isFetching updates. |
| plugins/wp-graphql-ide/src/stores/activity-bar/activity-bar-selectors.js | Adds visiblePanel selector for the new activity panel rendering flow. |
| plugins/wp-graphql-ide/src/registry/editor-toolbar-buttons/prettify-button.js | Replaces GraphiQL icon usage with WordPress icons. |
| plugins/wp-graphql-ide/src/registry/editor-toolbar-buttons/merge-fragments-button.js | Replaces GraphiQL icon usage with WordPress icons. |
| plugins/wp-graphql-ide/src/registry/editor-toolbar-buttons/copy-query-button.js | Replaces GraphiQL icon usage with WordPress icons and renames clipboard import. |
| plugins/wp-graphql-ide/src/hooks/useSchema.js | Introduces schema introspection hook wired to the app store. |
| plugins/wp-graphql-ide/src/hooks/useExecution.js | Introduces execution hook (variables/headers parsing, abort support, response formatting). |
| plugins/wp-graphql-ide/src/components/ide-layout.css | Adds WordPress-admin-styled layout and CodeMirror sizing styles. |
| plugins/wp-graphql-ide/src/components/editors/index.js | Exports new editor components. |
| plugins/wp-graphql-ide/src/components/editors/ResponseViewer.jsx | Adds CM6 read-only JSON response viewer. |
| plugins/wp-graphql-ide/src/components/editors/JSONEditor.jsx | Adds CM6 JSON editor for variables/headers. |
| plugins/wp-graphql-ide/src/components/editors/GraphQLEditor.jsx | Adds CM6 GraphQL editor with cm6-graphql schema integration. |
| plugins/wp-graphql-ide/src/components/ShortKeysDialog.jsx | Migrates from GraphiQL Dialog to WP Modal and updates copy/markup. |
| plugins/wp-graphql-ide/src/components/SettingsDialog.jsx | Migrates from GraphiQL Dialog to WP Modal/controls (Theme + settings UI). |
| plugins/wp-graphql-ide/src/components/IDELayout.jsx | New main IDE composition using WP components, editors, and new hooks. |
| plugins/wp-graphql-ide/src/components/GraphiQL.jsx | Removes the prior GraphiQL provider/interface implementation. |
| plugins/wp-graphql-ide/src/components/EditorToolbar.jsx | Replaces GraphiQL toolbar button UI with WP Button + Tooltip. |
| plugins/wp-graphql-ide/src/components/EditorGroup.jsx | Removes the prior GraphiQL editor group layout. |
| plugins/wp-graphql-ide/src/components/AppDrawer.jsx | Updates focus targeting from GraphiQL/CM5 elements to CM6. |
| plugins/wp-graphql-ide/src/components/App.jsx | Switches from GraphiQL wrapper to IDELayout; extends fetcher to accept headers + abort signal. |
| plugins/wp-graphql-ide/src/components/ActivityPanel.jsx | Refactors activity panel rendering to use store-driven visible panel and WP ResizableBox. |
| plugins/wp-graphql-ide/src/components/ActivityBarUtilities.jsx | Migrates activity bar utilities UI to WP Buttons/Tooltips/icons. |
| plugins/wp-graphql-ide/src/components/ActivityBarPanels.jsx | Migrates activity bar panel buttons to WP components and store-driven toggle action. |
| plugins/wp-graphql-ide/src/components/ActivityBar.jsx | Updates activity bar composition to match new panel/utilities components. |
| plugins/wp-graphql-ide/package.json | Renames package to @wpgraphql/ide, updates deps (CM6, cm6-graphql), adjusts scripts. |
| plugins/wp-graphql-ide/CONTRIBUTING.md | Updates workspace commands to the new package name. |
| plugins/wp-graphql-ide/ACTIONS_AND_FILTERS.md | Updates docs to reflect renamed/non-GraphiQL hooks and removes legacy section. |
| package-lock.json | Updates lockfile for new dependencies and package rename. |
| CLAUDE.md | Updates workspace commands to the new package name. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 59 out of 62 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This comment was marked as outdated.
This comment was marked as outdated.
ff8a28f to
e84b452
Compare
eb3844c to
84bc463
Compare
CI lint failure on PR wp-graphql#3784: PHPStan crashed with `PathNotFoundException: ../wp-graphql/vendor/webonyx/graphql-php/src does not exist`. The path I added earlier resolved locally because the WPGraphQL monorepo has both plugins side by side with their vendor dirs populated, but each plugin's lint job only installs its own composer dependencies — wp-graphql's vendor isn't there. Switch to the stubs package this plugin already requires for static analysis (axepress/wp-graphql-stubs). It contains class definitions for GraphQL\Language\Parser, GraphQL\Language\Printer, and GraphQL\Error\SyntaxError, so PHPStan resolves them without touching the sibling plugin. The stubs aren't auto-loaded (the package ships no extension.neon), so the path is registered explicitly via `scanFiles`. Verified locally: composer phpstan exits 0 errors.
…n the dep PR wp-graphql#3784 CI flagged the @graphiql/react import in GraphiQLToolbar.js as unresolved. The package resolves at runtime via graphiql@1.x's nested node_modules, just not at a path the lint resolver walks. We considered adding @graphiql/react as an explicit top-level dep but backed off — that file lives in the legacy `wpgraphiql/` package that wp-graphql-ide is replacing. Adding a permanent dep extends the legacy IDE's life with a new contract; an eslint-disable on the one import keeps lint quiet without committing to maintain the package. If/when the legacy `wpgraphiql/` package is removed in a follow-up, this comment goes with it.
PR wp-graphql#3784 CI flagged the @graphiql/react import in GraphiQLToolbar.js as unresolved. The package resolves at runtime through graphiql@1.x's nested node_modules, just not at a path the lint resolver walks. Both imported hooks (usePrettifyEditors, useHistoryContext) drive real button behavior on this toolbar, so the import has to stay. Adding @graphiql/react as an explicit top-level dep would silence the lint, but it'd also commit the repo to maintaining a version of an upstream package we don't otherwise consume. The `eslint-disable-next-line` is the smaller change.
493f112 to
4394203
Compare
…le in SPL autoloader CI's PHPCS run treats warnings as errors, so the variable-path require_once inside the autoloader fallback (added in bb946a5) failed Schema Linter and any check-cs report-mode workflow that runs with --report-checkstyle. The VIP sniff is a false positive here — the autoloader's whole job is to resolve a class name to a path at runtime, and the value is already constrained by: - prefix check (must start with WPGraphQLIDE\) - file_exists() guard before include Same justification AssetEnqueue.php uses for its build-asset includes.
Three e2e specs were locked to the pre-refactor behavior of temp drafts: - A temp tab "shows the unsaved-dirty indicator" - After Escape from SaveDialog, asserted the dirty bullet was visible - "Save draft button is disabled when the doc has no unsaved changes" pointed at a fresh temp tab The current contract (autopersisted temps + Save promotes temp → draft): - Temp tabs are marked with `is-temp` (italic title) — not the dirty bullet, which would always be on for them and become useless. - Save stays enabled on temp tabs even when activeDocDirty is false, because clicking it is the user's path to promoting a temp. Updated each spec to assert the new contract. The third test's intent — "Save reflects the underlying contract" — survives, just inverted: temp tabs keep the button enabled.
The execute button was 32×32 with a 16px icon — tight enough that hitting it took deliberate aim, even with a mouse. Bump to 44×44 (WCAG 2.5.5 AAA minimum) with a 20px icon. The button reads as primary at the new size; the rest of the execution pill keeps its existing scale so the play button visually anchors the row.
Sync main (split PRs wp-graphql#3877/wp-graphql#3878/wp-graphql#3879 + asset pipeline wp-graphql#3880/wp-graphql#3882/wp-graphql#3883) onto the IDE rebuild branch, on top of Joe's floor/CI commits (floor 6.1, WP 7.0 CI coverage + tested-up-to). Conflicts (all IDE release metadata) resolved as: - Version / Stable tag / package.json: 4.5.0 (release-please manifest value; release-please bumps to 5.0.0 at release — in-repo version tracks last release per the plugin's CLAUDE.md). - CHANGELOG.md / readme.txt changelog: union — keep the 5.0.0 notes AND main's released 4.5.0 entry. Dropped the now-inaccurate "raised floor to 7.0" breaking-change note (Joe set the floor to 6.1) and the 7.0-floor framing in the i18n docs. - Requires at least: 6.1 / Tested up to: 7.0 — kept Joe's values.
… longer blocks)" This reverts commit 08d0977.
The IDE's `codeception.dist.yml` boots Smart Cache via WPLoader's
`plugins:` list. Smart Cache's entry file only loads its classes when
its own `vendor/autoload.php` is present:
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require __DIR__ . '/vendor/autoload.php';
}
The reusable workflow ran `composer install` for the plugin under test
and (conditionally) for `plugins/wp-graphql`, but never for
`plugins/wp-graphql-smart-cache`. So inside the test container:
1. Smart Cache's `Document` class never autoloaded.
2. `can_load_plugin()` returned false (Document::class missing).
3. Smart Cache's `init` callback bailed -> `graphql_document` post
type was never registered.
4. `SmartCacheBridge::register()` also bailed on the same
`class_exists` guard, so `show_in_rest` was never filtered on.
5. Every `/wp/v2/graphql_document/*` REST test 404'd; every
`graphqlDocument` GraphQL query returned `{errors: ...}` with no
`data` key (17 failures + 9 errors per matrix row).
The CI band-aids on the branch (re-activating the IDE plugin against
`tests-cli` before the WPUnit suite) were not the fix — WPLoader
bootstraps its own in-process WordPress and doesn't read the
`tests-cli` container's `active_plugins` option.
Mirror the existing `install_wp_graphql_core_deps` pattern: add an
`install_wp_graphql_smart_cache_deps` input to the reusable workflow,
add a conditional composer-install step for Smart Cache, and pass
`true` from the IDE caller.
Same root cause as 877b5c7, but in the JS E2E reusable workflow. `.wp-env.json` mounts wp-graphql-smart-cache for every plugin's test container, and Smart Cache's entry file only loads its classes when its own vendor/autoload.php is present. The E2E reusable workflow already follows this pattern for wp-graphql core (auto-install for any plugin that isn't wp-graphql itself) — extend it to Smart Cache. Without this, Smart Cache's post types + GraphQL types (`graphql_document`, `graphqlDocumentGroups`, …) never register and the IDE's e2e suite fails with `Cannot query field "graphqlDocumentGroups" on type "RootQuery"` on first render.
The endpoint-mode e2e suite toggled the public-endpoint setting with: wp option patch update graphql_ide_settings graphql_ide_public_endpoint $value || wp option patch insert graphql_ide_settings graphql_ide_public_endpoint $value `wp option patch` requires the option to exist as a serialized array. On a fresh tests-cli container the option is missing (or coerced to an empty string by WordPress internals when no settings have been saved), so `patch insert` errors with: Cannot create key "graphql_ide_public_endpoint" on data type string `wp option patch update` then errors with "No data exists for key" because the array doesn't have the subkey yet, and the fallback `||` hits the same string-coercion wall. Just overwrite the whole `graphql_ide_settings` option with the desired state via `wp option update --format=json`. The other IDE settings are read through `get_graphql_setting()` which defaults a non-array section to `[]` and falls back to each field's declared default, so absence == declared default for any field not in the payload. `afterAll` resets `graphql_ide_public_endpoint` to "off", which is also its declared default.
The Playground Preview workflow was failing on every fork PR at the
sticky-comment step with "Resource not accessible by integration"
because `pull_request` events from forks always get GITHUB_TOKEN
downgraded to read-only, regardless of the `permissions:` block.
Splitting the workflow is the standard fix:
playground-preview.yml (pull_request, no write perms)
Builds the plugin ZIP and uploads it.
Uploads a small pr-meta artifact with the PR number + head SHA.
playground-preview-comment.yml (workflow_run, pull-requests: write)
Triggered when Playground Preview finishes successfully.
Downloads the pr-meta artifact via the cross-run download mode of
actions/download-artifact@v4, builds the Playground URL using the
triggering workflow's run_id (so the existing nightly.link route
still resolves), and posts/updates the sticky comment.
The comment workflow runs in the base repo context with the base repo's
GITHUB_TOKEN, so it can comment on PRs from forks. The build workflow
no longer requests pull-requests:write — fork PRs would have it
silently dropped anyway, and decoupling makes the intent explicit.
Standard workflow_run caveat: GitHub always executes the copy of the
trigger file on the default branch, so the comment workflow won't
take effect on this PR. It'll start working for fork PRs once this
merges. The `preview` check goes green here regardless because the
failing step is gone.
Two changes: 1. Write the assembled Playground URL to `$GITHUB_STEP_SUMMARY` from the build workflow. This makes the link reachable on the workflow run page for every PR — including fork PRs and including this PR itself — without depending on the workflow_run-triggered sticky comment landing first. Step summaries don't need any special token permissions, which is the whole point. Follows the WordPress Playground "Open in Playground" badge convention via a shields.io badge → playground.wordpress.net#<blueprint> URL, so reviewers see a button that matches the rest of the ecosystem (Gutenberg PRs, core Playground demos). 2. Compute the Playground URL once in the build workflow and stash it in the pr-meta artifact alongside PR_NUMBER and HEAD_SHA. The comment workflow now reads the URL instead of rebuilding the same base64 blueprint with the same heredoc. Single source of truth, and a blueprint change touches one file.
`wp-scripts plugin-zip` derives the output filename from package.json's
`name` field and adds discovered files flat at the archive root. The
IDE's workspace name `@wpgraphql/wp-graphql-ide` makes that produce:
- filename `@wpgraphql/wp-graphql-ide.zip` (with `@wpgraphql/` as a
literal subdirectory of the build cwd)
- contents flat at root (no top-level wrapping directory)
Both of those are wrong for any consumer that follows the WP plugin
convention. WordPress, wp.org's installer, and WordPress Playground's
`installPlugin` step all expect a ZIP containing a single top-level
directory whose name equals the plugin slug. Without it, Playground's
extraction lands files in a derived location that doesn't match
`wp-content/plugins/wp-graphql-ide`, so the subsequent `activatePlugin`
step fails with "wasn't able to find the plugin
/wordpress/wp-content/plugins/wp-graphql-ide".
Replace `build:zip` with `bin/build-plugin-zip.js`, which reuses the
same file discovery (`npm-packlist`, honoring the `files` field) but
writes everything under a `wp-graphql-ide/` prefix to a flat
`wp-graphql-ide.zip` at the workspace root. `adm-zip` and
`npm-packlist` are already transitively available via
`@wordpress/scripts`, so no new dependencies.
The Playground preview workflow can now skip its "Normalize ZIP
filename" find/mv hack — the build script writes the right artifact
in the right place directly.
Also: add `wp-graphql-ide.zip` (with the dash matching the plugin
slug) to the IDE's .gitignore; the existing entry was for the old
slug-less filename `wpgraphql-ide.zip`.
`actions/upload-artifact@v4` always wraps whatever it's given in a ZIP — that wrapper is the file the user (or Playground, via nightly.link) downloads. Uploading our already-built `wp-graphql-ide.zip` as a single file therefore produced a double-zipped download: extracting one level revealed the inner ZIP file at the root, not the plugin directory. Playground's `installPlugin` step extracts exactly one level, found a `.zip` sitting where a plugin folder should be, and the subsequent `activatePlugin` failed with "wasn't able to find the plugin /wordpress/wp-content/plugins/wp-graphql-ide". Extract the properly-shaped ZIP into a staging dir and upload the extracted contents instead. GitHub's artifact wrapper becomes the one and only ZIP layer, the download contains `wp-graphql-ide/<files>` at its root, and Playground's installer is happy. `build:zip` itself still produces `wp-graphql-ide.zip` for wp.org / manual distribution — only the Playground workflow's artifact path changes.
…it up `actions/upload-artifact@v4` defaults `include-hidden-files` to false and skips paths whose names start with `.`, so the previous staging dir `.playground-artifact/` produced "No files were found with the provided path" and uploaded nothing. Drop the dot from both the workflow path and the .gitignore entry — staging is meant to be visible to the upload action.
The new bin script written in 7f2b6d9 used spaces inside parens / braces (the @wordpress/scripts-style I'd written from muscle memory), which the IDE's Prettier config rejects. 16 auto-fixable formatting errors, no behavior change. Ran `lint:js:fix` to align with the rest of the workspace.
The `files` field in `package.json` listed `build`, `styles`, `plugins/*/*.php`, etc. but omitted `includes` — so every PSR-4 class file and every modular helper that `wpgraphql-ide.php` require_once's was missing from the published archive. The very first line of plugin bootstrap is require_once __DIR__ . '/includes/access-functions.php'; which fataled with no output, surfacing in WordPress Playground as PHP.run() failed with exit code 255 === Stdout === (empty) === Stderr === (empty) (Empty streams because PHP died before any output buffering ran.) Add `includes` to the files list. Verified locally: rebuilt ZIP now contains 19 PHP files from `includes/` (access-functions.php, Access.php, AdminUI.php, AssetEnqueue.php, SmartCacheBridge.php, the document-settings/ subtree, etc.) where it previously had zero. The same gap would have broken wp.org installs the moment users tried to download the published ZIP. Bug pre-dates this branch — the files list never had it.
Regression guard for the bug fixed in ad9a664. The IDE's \`package.json\` \`files\` list and the entry file's \`require_once __DIR__ . '/...'\` graph have to stay in sync — when they don't, the published ZIP is missing files the plugin needs at load time and the install fatals before any output buffer flushes (which is why Playground saw \`PHP.run() failed with exit code 255\` with empty stdout AND empty stderr). The test: - Rebuilds wp-graphql-ide.zip via bin/build-plugin-zip.js so the assertion is over what would actually ship, not a stale artifact. - Asserts the archive has a single \`wp-graphql-ide/\` top-level directory (Playground / WP / wp.org all require it). - Parses every \`require_once __DIR__ . '/path.php'\` literal out of wpgraphql-ide.php and asserts each target is in the archive. Adding a new require automatically extends coverage — no test edits needed. - Filters the one conditional require (\`vendor/autoload.php\`, intentionally optional; the SPL fallback covers it). - Asserts the four webpack entry points the IDE enqueues at runtime (.js + .asset.php for both main and render bundles). Wired into \`.github/workflows/playground-preview.yml\` as a step after build so every fork PR runs it — Jest isn't otherwise in CI yet, and running this single spec adds <1s. Lives at \`tests/unit/specs/distribution/\` rather than \`.../build/\` because the plugin's \`.gitignore\` has a top-level \`build/\` entry (for the webpack output dir) that would otherwise swallow this file too.
|
Here is the WordPress Playground for this pull request, as of this run. This is a one-off manual comment because the auto-comment workflow ships in this PR, as it cannot post here yet. Once merged, every future PR will get its own auto-updating sticky comment with a fresh Playground link. |
Two issues from CI's Lint job on 2d52f88: 1. `import/no-extraneous-dependencies` on \`require('adm-zip')\`. Both the new test and bin/build-plugin-zip.js were resolving adm-zip transitively through @wordpress/scripts. Declare it as a direct devDep so the dependency is explicit and ESLint stops complaining. Same applies to npm-packlist transitively, but that one's only used by bin/* which the lint config already exempts. 2. \`prettier/prettier\` on the multi-line template literal in the webpack-entrypoint assertion. Autofixed. Test still passes locally (5/5). Net: package.json gains one line, package-lock.json gets the matching adm-zip entry, test file gets the single-line template fixup.


Summary
Rebuilds the WPGraphQL IDE as a React + CodeMirror 6 client on @wordpress/data + @wordpress/components, and moves saved-document storage onto Smart Cache's
graphql_documentpost type.Architecture
includes/:PostTypes,UserMeta,Access,GraphQLSchema,Telemetry,ImportExport,Rest,AssetEnqueue,SettingsPage,AdminUI,SmartCacheBridge.app,activity-bar,document-editor,response-extensions,editor-bottom-tabs,status-bar-items,response-view-modes,response-actions,editor-actions,document-tab-actions.graphql_ide_querypost type andgraphql_ide_collectiontaxonomy are removed;SmartCacheBridgefilters Smart Cache'sgraphql_document+ 4 doc taxonomies into REST and adds_graphql_ide_variables/_graphql_ide_headersmeta + matching GraphQL fields for execution context.hasSmartCachebootstrap flag. Without Smart Cache the IDE works standalone with local-only unsaved tabs.graphql_ide_history.Three render modes
/wp-admin/admin.php?page=graphql-ideAccept: text/html(opt-in setting)All three are e2e covered, with an autocomplete-above-drawer regression guard.
Data layer
Document, collection, and history CRUD run through WPGraphQL via
src/api/graphql-client.js. REST remains for user-preference writes, the aggregateddocumentSettingsreadback field, and import/export/reorder.Extension API
JS (
window.WPGraphQLIDE):registerPreference,registerActivityBarPanel,registerWorkspaceTabType+openWorkspaceTab,registerTopbarAction,registerDocumentEditorToolbarButton,registerResponseExtensionTab,registerEditorBottomTab,registerStatusBarItem,registerResponseViewMode,registerResponseAction,registerEditorAction,registerDocumentTabAction.Execution hooks:
executeRequest/executeResponsefilters;wpgraphql-ide.afterExecuteaction.Canonical reference:
plugins/wp-graphql-ide/API_SURFACE.md.Features
+NoverflowInternationalization
Every UI string goes through
@wordpress/i18nwith thewpgraphql-idetext domain — chrome, tabs, dialogs, panels, settings, notices, registry labels.Security
manage_graphql_ide; GraphQL connections + node lookups scoped to the current userwpgraphql_ide_capability_requiredfilter is now consulted at every IDE permission check, not just admin-menu renderwp_kses_post()Accessibility (WCAG AA)
Issues addressed
reorder_graphql_submenu_items()strips out other 3rd-party submenus. #3757 —reorder_graphql_submenu_items()strips 3rd-party submenusTest plan