Skip to content

First pass at creating a more extensible plugin #10

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

Merged
merged 14 commits into from
Feb 23, 2024
24 changes: 24 additions & 0 deletions ACTIONS_AND_FILTERS.md
Original file line number Diff line number Diff line change
@@ -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

...
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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].
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
50 changes: 40 additions & 10 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="AppRoot">
<EditorDrawer open={ drawerOpen } setDrawerOpen={ setDrawerOpen }>
<Editor setDrawerOpen={ setDrawerOpen } />
</EditorDrawer>
</div>
);
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 (
<div className="AppRoot">
<EditorDrawer open={ drawerOpen } setDrawerOpen={ setDrawerOpen }>
<Editor setDrawerOpen={setDrawerOpen} />
</EditorDrawer>
</div>
);
}
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* global WPGRAPHQL_IDE_DATA */
import { createRoot } from '@wordpress/element';
import { createHooks } from '@wordpress/hooks';

import { App } from './App';

/**
Expand All @@ -18,5 +20,8 @@ if ( rootElement ) {
root.render( <App /> );
}

// Initialize hook system.
App.hooks = createHooks();

// Expose app as a global variable to utilize in gutenberg.
window.WPGraphQLIDE = App;
38 changes: 32 additions & 6 deletions wpgraphql-ide.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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;
}