diff --git a/.changeset/afraid-dolphins-joke.md b/.changeset/afraid-dolphins-joke.md
new file mode 100644
index 0000000..fd1d7c8
--- /dev/null
+++ b/.changeset/afraid-dolphins-joke.md
@@ -0,0 +1,8 @@
+---
+"wpgraphql-ide": patch
+---
+
+- fix: The IDE no longer waits on `DOMContentLoaded` in order to help client side performance with heavier pages.
+- add: New PHP filters for updating the drawer label:
+ - `wpgraphqlide_drawer_button_label`
+ - `wpgraphqlide_drawer_button_loading_label`
diff --git a/package-lock.json b/package-lock.json
index f6181de..4fc69e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,12 @@
{
"name": "wpgraphql-ide",
+ "version": "1.1.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
+ "name": "wpgraphql-ide",
+ "version": "1.1.8",
"dependencies": {
"@changesets/cli": "^2.27.1",
"@graphiql/plugin-explorer": "^1.0.3",
diff --git a/src/App.jsx b/src/App.jsx
index 4a751be..8ed2ec2 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,5 +1,5 @@
/* global WPGRAPHQL_IDE_DATA */
-import { createRoot, useEffect } from '@wordpress/element';
+import { useEffect } from '@wordpress/element';
import { doAction } from '@wordpress/hooks';
import { useDispatch, useSelect } from '@wordpress/data';
import { parse, print } from 'graphql';
@@ -8,7 +8,10 @@ import LZString from 'lz-string';
import { EditorDrawer } from './components/EditorDrawer';
import { Editor } from './components/Editor';
-const { isDedicatedIdePage } = window.WPGRAPHQL_IDE_DATA;
+const {
+ isDedicatedIdePage,
+ context: { drawerButtonLabel },
+} = window.WPGRAPHQL_IDE_DATA;
const url = new URL( window.location.href );
const params = url.searchParams;
@@ -129,7 +132,7 @@ export function RenderApp() {
return (
-
+
diff --git a/src/components/Editor.jsx b/src/components/Editor.jsx
index 872dee9..e99d9fb 100644
--- a/src/components/Editor.jsx
+++ b/src/components/Editor.jsx
@@ -3,7 +3,7 @@ import { GraphiQL } from 'graphiql';
import { useDispatch, useSelect } from '@wordpress/data';
import { parse, visit } from 'graphql';
import { explorerPlugin } from '@graphiql/plugin-explorer';
-import { helpPlugin } from './help'
+import { helpPlugin } from './help';
import { PrettifyButton } from './toolbarButtons/PrettifyButton';
import { CopyQueryButton } from './toolbarButtons/CopyQueryButton';
@@ -11,7 +11,6 @@ import { MergeFragmentsButton } from './toolbarButtons/MergeFragmentsButton';
import { ShareDocumentButton } from './toolbarButtons/ShareDocumentButton';
import { ToggleAuthButton } from './toolbarButtons/ToggleAuthButton';
-
import 'graphiql/graphiql.min.css';
/**
@@ -129,7 +128,7 @@ export function Editor() {
setSchema( newSchema );
}
} }
- plugins={[ explorer, help ]}
+ plugins={ [ explorer, help ] }
>
{
return select( 'wpgraphql-ide' ).isDrawerOpen();
} );
diff --git a/src/components/help/index.jsx b/src/components/help/index.jsx
index f4e441a..741d0b8 100644
--- a/src/components/help/index.jsx
+++ b/src/components/help/index.jsx
@@ -6,12 +6,12 @@ export const helpPlugin = () => {
title: 'Help',
icon: () => (
),
- content: () => ,
+ content: () => ,
};
};
diff --git a/src/components/toolbarButtons/ToggleAuthButton.jsx b/src/components/toolbarButtons/ToggleAuthButton.jsx
index fd27bca..18eb7ef 100644
--- a/src/components/toolbarButtons/ToggleAuthButton.jsx
+++ b/src/components/toolbarButtons/ToggleAuthButton.jsx
@@ -22,11 +22,15 @@ export const ToggleAuthButton = ( {
return (
diff --git a/src/index.js b/src/index.js
index ddea706..5cc3e79 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,37 +1,49 @@
-// WordPress dependencies for hooks, data handling, and component rendering.
+/**
+ * @file
+ * Initializes the WPGraphQL IDE by setting up the necessary WordPress hooks,
+ * registering the global store, and exposing the GraphQL functionality through a global IDE object.
+ */
+
+// External dependencies
import { createHooks } from '@wordpress/hooks';
import { register } from '@wordpress/data';
import { createRoot } from '@wordpress/element';
-import * as GraphQL from "graphql/index.js";
+import * as GraphQL from 'graphql/index.js';
-// Local imports including the store configuration and the main App component.
+// Internal dependencies
import { store } from './store';
import { App } from './App';
-// Register the store with wp.data to make it available throughout the plugin.
+/**
+ * Registers the application's data store with WordPress to enable global state management.
+ */
register( store );
-// Create a central event hook system for the WPGraphQL IDE.
+/**
+ * Initializes a hooks system to extend the functionality of the WPGraphQL IDE through
+ * external scripts and internal plugin hooks.
+ */
export const hooks = createHooks();
-// Expose a global variable for the IDE, facilitating extension through external scripts.
+/**
+ * Exposes a global `WPGraphQLIDE` variable that includes hooks, store, and GraphQL references,
+ * making them accessible for extensions and external scripts.
+ */
window.WPGraphQLIDE = {
hooks,
store,
- GraphQL
+ GraphQL,
};
/**
- * Initialize and render the application once the DOM is fully loaded.
- * This ensures that the application mounts when all page elements are available.
+ * Attempts to render the React application to a specified mount point in the DOM.
+ * Logs an error to the console if the mount point is missing.
*/
-document.addEventListener( 'DOMContentLoaded', () => {
- const appMountPoint = document.getElementById( 'wpgraphql-ide-root' );
- if ( appMountPoint ) {
- createRoot( appMountPoint ).render( );
- } else {
- console.error(
- 'WPGraphQL IDE mount point not found. Please ensure an element with ID "wpgraphql-ide-root" exists.'
- );
- }
-} );
+const appMountPoint = document.getElementById( 'wpgraphql-ide-root' );
+if ( appMountPoint ) {
+ createRoot( appMountPoint ).render( );
+} else {
+ console.error(
+ 'WPGraphQL IDE mount point not found. Please ensure an element with ID "wpgraphql-ide-root" exists.'
+ );
+}
diff --git a/tests/e2e/specs/drawer.spec.js b/tests/e2e/specs/drawer.spec.js
index 3a0b53f..4d65d04 100644
--- a/tests/e2e/specs/drawer.spec.js
+++ b/tests/e2e/specs/drawer.spec.js
@@ -2,6 +2,7 @@ import {
loginToWordPressAdmin,
openDrawer,
pasteVariables,
+ simulateHeavyJSLoad,
typeQuery,
typeVariables,
visitAdminFacingPage,
@@ -37,6 +38,31 @@ test( 'should open and close successfully', async ( { page } ) => {
).not.toBeVisible();
} );
+test( 'should open on JS-heavy admin page with CPU Throttling', async ( { page } ) => {
+ await visitAdminFacingPage( page );
+
+ // Start a new CDP Session to control the browser
+ const context = page.context();
+ const session = await context.newCDPSession( page );
+
+ // Set CPU Throttling
+ await session.send( 'Emulation.setCPUThrottlingRate', { rate: 4 }); // Throttles CPU to 1/4th its speed
+
+ // Now simulate heavy JavaScript load
+ await simulateHeavyJSLoad( page );
+
+ // Check if the drawer is initially hidden
+ await expect( page.locator( '.graphiql-container' ) ).toBeHidden();
+
+ // Open the drawer and ensure it still functions under throttled conditions
+ await openDrawer( page );
+ await expect( page.locator( '.graphiql-container' ) ).toBeVisible();
+
+ // Optionally, reset CPU throttling rate after test
+ await session.send( 'Emulation.setCPUThrottlingRate', { rate: 1 } );
+});
+
+
test( 'should execute a GraphQL query successfully', async ( { page } ) => {
await page.click( selectors.editorDrawerButton );
await expect( page.locator( selectors.graphiqlContainer ) ).toBeVisible();
diff --git a/tests/e2e/specs/environment.spec.js b/tests/e2e/specs/environment.spec.js
new file mode 100644
index 0000000..699ff3a
--- /dev/null
+++ b/tests/e2e/specs/environment.spec.js
@@ -0,0 +1,20 @@
+import { test, expect } from '@playwright/test';
+import { loginToWordPressAdmin, visitPluginsPage } from '../utils';
+
+test.beforeEach(async ({ page }) => {
+ await loginToWordPressAdmin(page);
+});
+
+test( 'expect the enqueued wpgraphql-ide script to have the defer attribute', async ( { page } ) => {
+ await visitPluginsPage( page );
+
+ // Retrieve the script tags and check for the defer attribute
+ const hasDeferAttribute = await page.evaluate( () => {
+ const scripts = Array.from( document.querySelectorAll( 'script' ) );
+ const targetScript = scripts.find( script => script.src.includes( 'wpgraphql-ide' ) );
+ return targetScript?.hasAttribute( 'defer' );
+ });
+
+ // Assert that the defer attribute is present
+ expect( hasDeferAttribute ).toBe( true );
+});
diff --git a/tests/e2e/utils.js b/tests/e2e/utils.js
index 79dacfa..982fce5 100644
--- a/tests/e2e/utils.js
+++ b/tests/e2e/utils.js
@@ -186,3 +186,30 @@ async function selectAndClearTextUsingKeyboard( page, selector ) {
await page.keyboard.press( selectAllCommand ); // Select all text using OS-specific shortcut
await page.keyboard.press( 'Backspace' ); // Clear selected text
}
+
+export async function simulateHeavyJSLoad(page) {
+ await page.evaluate(() => {
+ // Simulate heavy DOM manipulations
+ for (let i = 0; i < 500; i++) {
+ const div = document.createElement('div');
+ div.textContent = `Heavy content ${i}`;
+ div.style.backgroundColor = "#" + Math.floor(Math.random()*16777215).toString(16);
+ document.body.appendChild(div);
+ }
+
+ // Simulate heavy computations
+ const heavyComputation = Array.from({ length: 50000 }, (_, i) => i ** 2).reduce((a, b) => a + b);
+ console.log('Heavy computation result:', heavyComputation);
+
+ // Simulate asynchronous operations
+ new Promise(resolve => setTimeout(resolve, 5000)).then(() => console.log('Delayed operation completed'));
+
+ // Simulate frequent DOM updates
+ setInterval(() => {
+ const randomDiv = document.querySelector(`div:nth-child(${Math.floor(Math.random() * 500) + 1})`);
+ if (randomDiv) {
+ randomDiv.style.backgroundColor = "#" + Math.floor(Math.random()*16777215).toString(16);
+ }
+ }, 10);
+ });
+}
\ No newline at end of file
diff --git a/wpgraphql-ide.php b/wpgraphql-ide.php
index a29b62c..66ecc38 100644
--- a/wpgraphql-ide.php
+++ b/wpgraphql-ide.php
@@ -20,7 +20,7 @@
exit;
}
-define( 'WPGRAPHQL_IDE_VERSION', '1.0.1' );
+define( 'WPGRAPHQL_IDE_VERSION', '1.1.8' );
define( 'WPGRAPHQL_IDE_ROOT_ELEMENT_ID', 'wpgraphql-ide-root' );
define( 'WPGRAPHQL_IDE_PLUGIN_DIR_PATH', plugin_dir_path( __FILE__ ) );
define( 'WPGRAPHQL_IDE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
@@ -117,6 +117,8 @@ function register_wpadminbar_menus(): void {
global $wp_admin_bar;
+ $app_context = get_app_context();
+
// Link to the new dedicated IDE page.
$wp_admin_bar->add_node(
[
@@ -131,7 +133,7 @@ function register_wpadminbar_menus(): void {
$wp_admin_bar->add_node(
[
'id' => 'wpgraphql-ide-button',
- 'title' => '',
+ 'title' => '' . $app_context['drawerButtonLoadingLabel'] . '
',
'href' => '#',
]
);
@@ -234,7 +236,7 @@ function enqueue_react_app_with_styles(): void {
plugins_url( 'build/index.js', __FILE__ ),
$asset_file['dependencies'],
$asset_file['version'],
- true
+ false
);
wp_localize_script(
@@ -305,10 +307,12 @@ function get_app_context(): array {
return apply_filters(
'wpgraphqlide_context',
[
- 'pluginVersion' => get_plugin_header( 'Version' ),
- 'pluginName' => get_plugin_header( 'Name' ),
- 'externalFragments' => apply_filters( 'wpgraphqlide_external_fragments', [] ),
- 'avatarUrl' => $avatar_url,
+ 'pluginVersion' => get_plugin_header( 'Version' ),
+ 'pluginName' => get_plugin_header( 'Name' ),
+ 'externalFragments' => apply_filters( 'wpgraphqlide_external_fragments', [] ),
+ 'avatarUrl' => $avatar_url,
+ 'drawerButtonLabel' => apply_filters( 'wpgraphqlide_drawer_button_label', '🚀' ),
+ 'drawerButtonLoadingLabel' => apply_filters( 'wpgraphqlide_drawer_button_loading_label', '⏳' ),
]
);
}
@@ -391,3 +395,29 @@ static function ( bool $is_plugin_scoped_page, string $current_page_id, array $a
10,
3
);
+
+/**
+ * Modifies the script tag for specific scripts to add the 'defer' attribute.
+ *
+ * This function checks if the script handle matches 'wpgraphql-ide' and, if so,
+ * adds the 'defer' attribute to the script tag. This allows the script to be executed
+ * after the HTML document is parsed but before the DOMContentLoaded event.
+ *
+ * @param string $tag The HTML `