diff --git a/ACTIONS_AND_FILTERS.md b/ACTIONS_AND_FILTERS.md
new file mode 100644
index 0000000..8f3c720
--- /dev/null
+++ b/ACTIONS_AND_FILTERS.md
@@ -0,0 +1,24 @@
+# Actions & Filters
+
+-> [Original documentation](https://www.wpgraphql.com/docs/customizing-wpgraphiql)
+
+_legend_ 🎉 = new
+
+## PHP Actions
+
+- `wpgraphqlide_enqueue_script` ([enqueue_graphiql_extension](https://www.wpgraphql.com/docs/customizing-wpgraphiql#enqueue_graphiql_extension))
+
+## PHP Filters
+
+- `wpgraphqlide_capability_required` 🎉
+- `wpgraphqlide_context` 🎉
+- `wpgraphqlide_external_fragments` ([graphiql_external_fragments](https://www.wpgraphql.com/docs/customizing-wpgraphiql#graphiql_external_fragments))
+
+## JavaScript Actions
+
+- `wpgraphqlide_destroyed` 🎉
+- `wpgraphqlide_rendered` ([graphiql_rendered](https://www.wpgraphql.com/docs/customizing-wpgraphiql#graphiql_rendered))
+
+## JavaScript Filters
+
+...
diff --git a/README.md b/README.md
index e5ef51b..8fab46a 100644
--- a/README.md
+++ b/README.md
@@ -31,3 +31,7 @@ GraphiQL IDE has intentionally been updated to read "GraphQL IDE", which feels m
```
Builds are only required for JS updates as the plugin's CSS is directly enqueued.
+
+## Custom Hooks
+
+See [ACTIONS_AND_FILTERS.md].
diff --git a/package-lock.json b/package-lock.json
index da71d03..4851495 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,6 +6,7 @@
"": {
"dependencies": {
"@wordpress/element": "^5.23.0",
+ "@wordpress/hooks": "^3.49.0",
"graphiql": "^3.0.10",
"graphql-ws": "^5.14.2",
"vaul": "^0.7.9"
@@ -5546,10 +5547,9 @@
}
},
"node_modules/@wordpress/hooks": {
- "version": "3.46.0",
- "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.46.0.tgz",
- "integrity": "sha512-TTYNZwMZeATpkWmvAoShP43UONd/WPNTtsy1czMSyiqPzFhzGJbKD75CdJtPp5DqIAiuWQEuDmcxRAPcZ/1Qgw==",
- "dev": true,
+ "version": "3.49.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.49.0.tgz",
+ "integrity": "sha512-GH546Jg8u/rw9I3fsvAhidwt8rUFNmkdXGByIPGsN3R6y+QwWMXPzsnoYdFmFOmDK9gOGCRDe5bXHikoWnaiKA==",
"dependencies": {
"@babel/runtime": "^7.16.0"
},
diff --git a/package.json b/package.json
index c73f6ca..972fb26 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
},
"dependencies": {
"@wordpress/element": "^5.23.0",
+ "@wordpress/hooks": "^3.49.0",
"graphiql": "^3.0.10",
"graphql-ws": "^5.14.2",
"vaul": "^0.7.9"
diff --git a/src/App.jsx b/src/App.jsx
index 1e34dda..fa02afb 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,15 +1,45 @@
-import { useState } from '@wordpress/element';
+import { useState, useEffect } from '@wordpress/element';
import { EditorDrawer } from './components/EditorDrawer';
import { Editor } from './components/Editor';
+// Assuming wp.hooks is globally available through WordPress's script enqueueing mechanism.
+const { doAction } = wp.hooks;
+
+/**
+ * The main application component.
+ *
+ * @returns {JSX.Element} The application component.
+ */
export function App() {
- const [ drawerOpen, setDrawerOpen ] = useState( false );
-
- return (
-
-
-
-
-
- );
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ useEffect(() => {
+ /**
+ * Perform actions on component mount.
+ *
+ * Triggers a custom action 'wpgraphqlide_rendered' when the App component mounts,
+ * allowing plugins or themes to hook into this event. The action passes
+ * the current state of `drawerOpen` to any listeners, providing context
+ * about the application's UI state.
+ */
+ doAction('wpgraphqlide_rendered', drawerOpen);
+
+ /**
+ * Cleanup action on component unmount.
+ *
+ * Returns a cleanup function that triggers the 'wpgraphqlide_destroyed' action,
+ * signaling that the App component is about to unmount. This allows for
+ * any necessary cleanup or teardown operations in response to the App
+ * component's lifecycle.
+ */
+ return () => doAction('wpgraphqlide_destroyed');
+ }, [drawerOpen]);
+
+ return (
+
+
+
+
+
+ );
}
diff --git a/src/index.js b/src/index.js
index 4732918..59db144 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,7 @@
/* global WPGRAPHQL_IDE_DATA */
import { createRoot } from '@wordpress/element';
+import { createHooks } from '@wordpress/hooks';
+
import { App } from './App';
/**
@@ -18,5 +20,8 @@ if ( rootElement ) {
root.render( );
}
+// Initialize hook system.
+App.hooks = createHooks();
+
// Expose app as a global variable to utilize in gutenberg.
window.WPGraphQLIDE = App;
diff --git a/wpgraphql-ide.php b/wpgraphql-ide.php
index d66d1d3..f2f1d58 100644
--- a/wpgraphql-ide.php
+++ b/wpgraphql-ide.php
@@ -121,7 +121,9 @@ function enqueue_react_app_with_styles(): void {
'wp-i18n',
];
- $version = plugin_version();
+ $app_context = get_app_context();
+
+ $version = get_plugin_header( 'Version' );
wp_enqueue_script(
'wpgraphql-ide-app',
@@ -138,28 +140,52 @@ function enqueue_react_app_with_styles(): void {
'nonce' => wp_create_nonce( 'wp_rest' ),
'graphqlEndpoint' => trailingslashit( site_url() ) . 'index.php?' . \WPGraphQL\Router::$route,
'rootElementId' => WPGRAPHQL_IDE_ROOT_ELEMENT_ID,
+ 'context' => $app_context,
]
);
wp_enqueue_style( 'wpgraphql-ide-app', plugins_url( 'build/index.css', __FILE__ ), [], $version );
// Avoid running custom styles through a build process for an improved developer experience.
wp_enqueue_style( 'wpgraphql-ide', plugins_url( 'styles/wpgraphql-ide.css', __FILE__ ), [], $version );
+
+ // Extensions looking to extend GraphiQL can hook in here,
+ // after the window object is established, but before the App renders
+ do_action( 'wpgraphqlide_enqueue_script', $app_context );
}
add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\\enqueue_react_app_with_styles' );
add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\\enqueue_react_app_with_styles' );
/**
- * Retrieves the version of the current plugin.
+ * Retrieves the specific header of this plugin.
*
- * @return string The version number of the plugin. Returns an empty string if the version is not found.
+ * @param string The plugin data key.
+ * @return string|null The version number of the plugin. Returns an empty string if the version is not found.
*/
-function plugin_version(): string {
+function get_plugin_header( $key = '' ): ?string {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
}
+ if ( empty( $key ) ) {
+ return null;
+ }
+
$plugin_data = get_plugin_data( __FILE__ );
- $version = $plugin_data['Version'];
- return $version;
+ return $plugin_data[ $key ] ?? null;
+}
+
+/**
+ * Retrieves app context.
+ *
+ * @return array The possibly filtered app context array.
+ */
+function get_app_context() {
+ $context = apply_filters( 'wpgraphqlide_context', [
+ 'pluginVersion' => get_plugin_header( 'Version' ),
+ 'pluginName' => get_plugin_header( 'Name' ),
+ 'externalFragments' => apply_filters( 'wpgraphqlide_external_fragments', [] )
+ ]);
+
+ return $context;
}