Agregar una guía de bienvenida personalizada al editor de bloques de WordPress
- Lo que queremos lograr
- Planificación de la implementación
- Paso 1: estructuración del guión
- Paso 2: incluir en la lista negra el panel de la barra lateral personalizado en todos los demás CPT
- Paso 3: Crear la guía personalizada
- Paso 4: Agregar contenido a la guía
- Bonificación: abrir documentos de forma independiente
- ¿Tadaaaaaaaa?
Estoy creando un complemento de WordPress y hay una ligera curva de aprendizaje a la hora de usarlo. Me gustaría brindarles a los usuarios una introducción sobre cómo usar el complemento, pero quiero evitar desviarlos a la documentación en el sitio web del complemento, ya que eso los excluye de la experiencia.
Lo que sería genial es que los usuarios comiencen a usar el complemento inmediatamente una vez que esté instalado, pero tengan acceso a consejos útiles mientras lo usan activamente. No existe una característica nativa para algo como esto en WordPress, pero podemos crear algo porque WordPress es así de súper flexible.
Así que aquí está la idea. Vamos a incorporar la documentación directamente en el complemento y hacerla fácilmente accesible en el editor de bloques. De esta manera, los usuarios pueden utilizar el complemento de inmediato y, al mismo tiempo, obtener respuestas a preguntas comunes directamente donde están trabajando.
Mi complemento funciona a través de varios tipos de publicaciones personalizadas (CPT). Lo que vamos a construir es esencialmente un modal emergente que los usuarios obtienen cuando acceden a estos CPT.
El editor de bloques de WordPress está integrado en React, que utiliza componentes que se pueden personalizar y reutilizar para diferentes situaciones. Ese es el caso de lo que estamos creando (llamémoslo componente Guide
), que se comporta como un modal, pero se compone de varias páginas por las que el usuario puede paginar.
El propio WordPress tiene un Guide
componente que muestra una guía de bienvenida al abrir el editor de bloques por primera vez:
La guía es un contenedor lleno de contenido dividido en páginas individuales. En otras palabras, es más o menos lo que queremos. Eso significa que no tenemos que reinventar la rueda con este proyecto; Podemos reutilizar este mismo concepto para nuestro propio complemento.
Hagamos exactamente eso.
Lo que queremos lograr
Antes de llegar a la solución, hablemos del objetivo final.
El diseño satisface los requisitos del complemento, que es un servidor GraphQL para WordPress. El complemento ofrece una variedad de CPT que se editan mediante bloques personalizados que, a su vez, se definen mediante plantillas. Hay un total de dos bloques: uno llamado "Cliente GraphiQL" para ingresar la consulta GraphQL y otro llamado "Opciones de consulta persistente" para personalizar el comportamiento de la ejecución.
Dado que crear una consulta para GraphQL no es una tarea trivial, decidí agregar el componente de guía a la pantalla del editor para ese CPT. Está disponible en la configuración del Documento como un panel llamado "Guía de bienvenida".
Abra ese panel y el usuario obtendrá un enlace. Ese vínculo es lo que desencadenará el modal.
Para el modal en sí, decidí mostrar un video tutorial sobre el uso del CPT en la primera página y luego describir en detalle todas las opciones disponibles en el CPT en las páginas siguientes.
Creo que este diseño es una forma eficaz de mostrar documentación al usuario. Está apartado, pero aún así está convenientemente cerca de la acción. Claro, podemos usar un diseño diferente o incluso colocar el disparador modal en otro lugar usando un componente diferente en lugar de reutilizarlo Guide
, pero esto está perfectamente bien.
Planificación de la implementación
La implementación comprende los siguientes pasos:
- Creación de un nuevo script para registrar el panel de la barra lateral personalizado
- Mostrar el panel de la barra lateral personalizado en el editor solo para nuestro tipo de publicación personalizada
- Creando la guía
- Agregar contenido a la guía
¡Empecemos!
Paso 1: estructuración del guión
A partir de WordPress 5.4, podemos usar un componente llamado PluginDocumentSettingPanel
para agregar un panel en la configuración del documento del editor como este:
const { registerPlugin } = wp.plugins;const { PluginDocumentSettingPanel } = wp.editPost; const PluginDocumentSettingPanelDemo = () = ( PluginDocumentSettingPanel name="custom-panel" className="custom-panel" Custom Panel Contents /PluginDocumentSettingPanel);registerPlugin( 'plugin-document-setting-panel-demo', { render: PluginDocumentSettingPanelDemo, icon: 'palmtree',} );
Si tiene experiencia con el editor de bloques y ya sabe cómo ejecutar este código, puede seguir adelante. He estado codificando con el editor de bloques durante menos de tres meses y usar React/npm/webpack es un mundo nuevo para mí: ¡este complemento es mi primer proyecto en el que los uso! Descubrí que los documentos en el repositorio de Gutenberg no siempre son adecuados para principiantes como yo y, a veces, falta la documentación por completo, por lo que tuve que profundizar en el código fuente para encontrar respuestas.
Cuando la documentación del componente indica que se debe usar ese fragmento de código anterior, no sé qué hacer a continuación, porque PluginDocumentSettingPanel
no es un bloque y no puedo crear un nuevo bloque ni agregar el código allí. Además, estamos trabajando con JSX, lo que significa que necesitamos tener un paso de compilación de JavaScript para compilar el código.
Sin embargo, encontré el código ES5 equivalente:
var el = wp.element.createElement;var __ = wp.i18n.__;var registerPlugin = wp.plugins.registerPlugin;var PluginDocumentSettingPanel = wp.editPost.PluginDocumentSettingPanel;
function MyDocumentSettingPlugin() { return el( PluginDocumentSettingPanel, { className: 'my-document-setting-plugin', title: 'My Panel', }, __( 'My Document Setting Panel' ) );}
registerPlugin( 'my-document-setting-plugin', { render: MyDocumentSettingPlugin} );
No es necesario compilar el código ES5, por lo que podemos cargarlo como cualquier otro script en WordPress. Pero no quiero usar eso. Quiero la experiencia completa y moderna de ESNext y JSX.
Entonces mi pensamiento es el siguiente: no puedo usar las herramientas de andamiaje de bloques porque no es un bloque y no sé cómo compilar el script (ciertamente no voy a configurar el paquete web yo solo). Eso significa que estoy estancado.
¡Pero espera! La única diferencia entre un bloque y un script normal es cómo se registran en WordPress. Un bloque se registra así:
wp_register_script($blockScriptName, $blockScriptURL, $dependencies, $version);register_block_type('my-namespace/my-block', [ 'editor_script' = $blockScriptName,]);
Y un script normal se registra así:
wp_register_script($scriptName, $scriptURL, $dependencies, $version);wp_enqueue_script($scriptName);
Podemos usar cualquiera de las herramientas de andamiaje de bloques para modificar cosas y luego registrar un script normal en lugar de un bloque, lo que nos da acceso a la configuración del paquete web para compilar el código ESNext. Esas herramientas disponibles son:
- El comando 'andamio' de WP CLI
- Paquete create-guten-block de Ahmad Awais
- El paquete oficial @wordpress/create-block
Elegí usar el paquete @wordpress/create-block porque lo mantiene el equipo que desarrolla Gutenberg.
Para aplicar scaffolding al bloque, ejecutamos esto en la línea de comando:
npm init @wordpress/block
Después de completar todas las solicitudes de información, incluido el nombre, el título y la descripción del bloque, la herramienta generará un complemento de bloque único, con un archivo PHP de entrada que contiene un código similar a este:
/** * Registers all block assets so that they can be enqueued through the block editor * in the corresponding context. * * @see https://developer.wordpress.org/block-editor/tutorials/block-tutorial/applying-styles-with-stylesheets/ */function my_namespace_my_block_block_init() { $dir = dirname( __FILE__ );
$script_asset_path = "$dir/build/index.asset.php"; if ( ! file_exists( $script_asset_path ) ) { throw new Error( 'You need to run `npm start` or `npm run build` for the "my-namespace/my-block" block first.' ); } $index_js = 'build/index.js'; $script_asset = require( $script_asset_path ); wp_register_script( 'my-namespace-my-block-block-editor', plugins_url( $index_js, __FILE__ ), $script_asset['dependencies'], $script_asset['version'] );
$editor_css = 'editor.css'; wp_register_style( 'my-namespace-my-block-block-editor', plugins_url( $editor_css, __FILE__ ), array(), filemtime( "$dir/$editor_css" ) );
$style_css = 'style.css'; wp_register_style( 'my-namespace-my-block-block', plugins_url( $style_css, __FILE__ ), array(), filemtime( "$dir/$style_css" ) );
register_block_type( 'my-namespace/my-block', array( 'editor_script' = 'my-namespace-my-block-block-editor', 'editor_style' = 'my-namespace-my-block-block-editor', 'style' = 'my-namespace-my-block-block', ) );}add_action( 'init', 'my_namespace_my_block_block_init' );
Podemos copiar este código en el complemento y modificarlo adecuadamente, convirtiendo el bloque en un script normal. (Tenga en cuenta que también voy a eliminar los archivos CSS a lo largo del proceso, pero podría conservarlos, si es necesario).
function my_script_init() { $dir = dirname( __FILE__ );
$script_asset_path = "$dir/build/index.asset.php"; if ( ! file_exists( $script_asset_path ) ) { throw new Error( 'You need to run `npm start` or `npm run build` for the "my-script" script first.' ); } $index_js = 'build/index.js'; $script_asset = require( $script_asset_path ); wp_register_script( 'my-script', plugins_url( $index_js, __FILE__ ), $script_asset['dependencies'], $script_asset['version'] ); wp_enqueue_script( 'my-script' );}add_action( 'init', 'my_script_init' );
Copiemos el package.json
archivo:
{ "name": "my-block", "version": "0.1.0", "description": "This is my block", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", "main": "build/index.js", "scripts": { "build": "wp-scripts build", "format:js": "wp-scripts format-js", "lint:css": "wp-scripts lint-style", "lint:js": "wp-scripts lint-js", "start": "wp-scripts start", "packages-update": "wp-scripts packages-update" }, "devDependencies": { "@wordpress/scripts": "^9.1.0" }}
Ahora podemos reemplazar el contenido del archivo src/index.js
con el código ESNext de arriba para registrar el PluginDocumentSettingPanel
componente. Al ejecutarse npm start
(o npm run build
en producción), el código se compilará en build/index.js
.
Hay un último problema que resolver: el PluginDocumentSettingPanel
componente no se importa estáticamente, sino que se obtiene de wp.editPost
, y dado que wp
es una variable global cargada por WordPress en tiempo de ejecución, esta dependencia no está presente en index.asset.php
(que se genera automáticamente durante la compilación). Debemos agregar manualmente una dependencia al wp-edit-post
script al registrarlo para asegurarnos de que se cargue antes que el nuestro:
$dependencies = array_merge( $script_asset['dependencies'], [ 'wp-edit-post', ]);wp_register_script( 'my-script', plugins_url( $index_js, __FILE__ ), $dependencies, $script_asset['version']);
¡Ahora la configuración del script está lista!
El complemento se puede actualizar con los incesantes ciclos de desarrollo de Gutenberg. Ejecute npm run packages-update
para actualizar las dependencias de npm (y, en consecuencia, la configuración del paquete web, que se define en el paquete "@wordpress/scripts"
) a sus últimas versiones compatibles.
En este punto, es posible que se pregunte cómo supe agregar una dependencia al "wp-edit-post"
script antes que nuestro script. Bueno, tuve que profundizar en el código fuente de Gutenberg. La documentación PluginDocumentSettingPanel
está algo incompleta, lo que es un ejemplo perfecto de cómo falta documentación de Gutenberg en ciertos lugares.
Mientras investigaba el código y examinaba la documentación, descubrí algunas cosas esclarecedoras. Por ejemplo, hay dos formas de codificar nuestros scripts: utilizando la sintaxis ES5 o ESNext. ES5 no requiere un proceso de compilación y hace referencia a instancias de código del entorno de ejecución, probablemente a través de la wp
variable global. Por ejemplo, el código para crear un icono es el siguiente:
var moreIcon = wp.element.createElement( 'svg' );
ESNext se basa en webpack para resolver todas las dependencias, lo que nos permite importar componentes estáticos. Por ejemplo, el código para crear un icono sería:
import { more } from '@wordpress/icons';
Esto se aplica prácticamente en todas partes. Sin embargo, ese no es el caso del PluginDocumentSettingPanel
componente, que hace referencia al entorno de ejecución de ESNext:
const { PluginDocumentSettingPanel } = wp.editPost;
Es por eso que tenemos que agregar una dependencia al script "wp-edit-post". Ahí es donde se define la variable wp.editPost.
Si PluginDocumentSettingPanel
se pudiera importar directamente, la dependencia de "wp-edit-post" sería manejada automáticamente por el editor de bloques a través del complemento Webpack de extracción de dependencias. Este complemento construye el puente entre lo estático y el tiempo de ejecución mediante la creación de un index.asset.php
archivo que contiene todas las dependencias para los scripts del entorno de ejecución, que se obtienen reemplazando "@wordpress/"
el nombre del paquete con "wp-"
. Por lo tanto, el "@wordpress/edit-post"
paquete se convierte en el "wp-edit-post"
script de ejecución. Así es como descubrí qué script agregar la dependencia.
Paso 2: incluir en la lista negra el panel de la barra lateral personalizado en todos los demás CPT
El panel mostrará documentación para un CPT específico, por lo que deberá estar registrado únicamente en ese CPT. Eso significa que debemos incluirlo en la lista negra para que no aparezca en ningún otro tipo de publicación.
Ryan Welcher (quien creó el PluginDocumentSettingPanel
componente) describe este proceso al registrar el panel:
const { registerPlugin } = wp.plugins;const { PluginDocumentSettingPanel } = wp.editPostconst { withSelect } = wp.data;
const MyCustomSideBarPanel = ( { postType } ) = {
if ( 'post-type-name' !== postType ) { return null; }
return( PluginDocumentSettingPanel name="my-custom-panel" Hello, World! /PluginDocumentSettingPanel );}
const CustomSideBarPanelwithSelect = withSelect( select = { return { postType: select( 'core/editor' ).getCurrentPostType(), };} )( MyCustomSideBarPanel);
registerPlugin( 'my-custom-panel', { render: CustomSideBarPanelwithSelect } );
También sugiere una solución alternativa, usando useSelect
en lugar de withSelect
.
Dicho esto, esta solución no me convence del todo, porque el archivo JavaScript aún debe cargarse, incluso si no es necesario, lo que obliga al sitio web a sufrir un impacto en el rendimiento. ¿No tiene más sentido no registrar el archivo JavaScript que ejecutar JavaScript solo para deshabilitar JavaScript?
He creado una solución PHP. Admito que se siente un poco complicado, pero funciona bien. Primero, averiguamos qué tipo de publicación está relacionada con el objeto que se está creando o editando:
function get_editing_post_type(): ?string{ if (!is_admin()) { return null; }
global $pagenow; $typenow = ''; if ( 'post-new.php' === $pagenow ) { if ( isset( $_REQUEST['post_type'] ) post_type_exists( $_REQUEST['post_type'] ) ) { $typenow = $_REQUEST['post_type']; }; } elseif ( 'post.php' === $pagenow ) { if ( isset( $_GET['post'] ) isset( $_POST['post_ID'] ) (int) $_GET['post'] !== (int) $_POST['post_ID'] ) { // Do nothing } elseif ( isset( $_GET['post'] ) ) { $post_id = (int) $_GET['post']; } elseif ( isset( $_POST['post_ID'] ) ) { $post_id = (int) $_POST['post_ID']; } if ( $post_id ) { $post = get_post( $post_id ); $typenow = $post-post_type; } } return $typenow;}
Luego, registramos el script solo si coincide con nuestro CPT:
add_action('init', 'maybe_register_script');function maybe_register_script(){ // Check if this is the intended custom post type if (get_editing_post_type() != 'my-custom-post-type') { return; }
// Only then register the block wp_register_script(...); wp_enqueue_script(...);}
Consulte esta publicación para obtener más información sobre cómo funciona esto.
Paso 3: Crear la guía personalizada
Diseñé la funcionalidad para la guía de mi complemento basada en el Guide
componente de WordPress. Al principio no me di cuenta de que estaría haciendo eso, así que así es como pude descubrirlo.
- Busque el código fuente para ver cómo se hizo allí.
- Explore el catálogo de todos los componentes disponibles en Gutenberg's Storybook.
Primero, copié contenido del modal del editor de bloques e hice una búsqueda básica. Los resultados me señalaron este archivo. A partir de ahí descubrí que se llama el componente Guide
y que simplemente podía copiar y pegar su código en mi complemento como base para mi propia guía.
Luego busqué la documentación del componente. Busqué el paquete @wordpress/components (que, como habrás adivinado, es donde se implementan los componentes) y encontré el archivo README del componente. Eso me dio toda la información que necesitaba para implementar mi propio componente de guía personalizado.
También exploré el catálogo de todos los componentes disponibles en el Storybook de Gutenberg (que en realidad muestra que estos componentes se pueden usar fuera del contexto de WordPress). Al hacer clic en todos ellos, finalmente descubrí Guide
. El libro de cuentos proporciona el código fuente de varios ejemplos (o historias). Es un recurso útil para comprender cómo personalizar un componente mediante accesorios.
En ese punto, sabía que Guide
sería una base sólida para mi componente. Sin embargo, falta un elemento: cómo activar la guía al hacer clic. ¡Tuve que devanarme los sesos para este!
Este es un botón con un oyente que abre el modal al hacer clic:
import { useState } from '@wordpress/element';import { Button } from '@wordpress/components';import { __ } from '@wordpress/i18n';import MyGuide from './guide';
const MyGuideWithButton = ( props ) = { const [ isOpen, setOpen ] = useState( false ); return ( Button onClick={ () = setOpen( true ) } { __('Open Guide: “Creating Persisted Queries”') } /Button { isOpen ( MyGuide { ...props } onFinish={ () = setOpen( false ) } / ) } / );};export default MyGuideWithButton;
Aunque el editor de bloques intenta ocultarlo, estamos operando dentro de React. Hasta ahora, nos hemos ocupado de JSX y componentes. Pero ahora necesitamos el useState
gancho, que es específico de React.
Yo diría que es necesario tener un buen conocimiento de React si quieres dominar el editor de bloques de WordPress. No hay manera de evitarlo.
Paso 4: Agregar contenido a la guía
¡Casi estámos allí! Creemos el Guide
componente, que contiene un GuidePage
componente para cada página de contenido.
El contenido puede usar HTML, incluir otros componentes y todo eso. En este caso particular, agregué tres GuidePage
instancias para mi CPT simplemente usando HTML. La primera página incluye un video tutorial y las dos páginas siguientes contienen instrucciones detalladas.
import { Guide, GuidePage } from '@wordpress/components';import { __ } from '@wordpress/i18n';
const MyGuide = ( props ) = { return ( Guide { ...props } GuidePage video controls source src="https://d1c2lqfn9an7pb.cloudfront.net/presentations/graphql-api/videos/graphql-api-creating-persisted-query.mov" type="video/mp4" / { __('Your browser does not support the video tag.') } /video // etc. /GuidePage GuidePage // ... /GuidePage GuidePage // ... /GuidePage /Guide )}export default MyGuide;
¡Nada mal! Sin embargo, existen algunos problemas:
- No pude insertar el video dentro
Guide
porque al hacer clic en el botón de reproducción se cierra la guía. Supongo que eso se debe a queiframe
cae fuera de los límites de la guía. Terminé subiendo el archivo de video a S3 y entregándolo convideo
. - La transición de página en la guía no es muy fluida. El modal del editor de bloques se ve bien porque todas las páginas tienen una altura similar, pero la transición en ésta es bastante abrupta.
- El efecto de desplazamiento sobre los botones podría mejorarse. Con suerte, el equipo de Gutenberg necesita solucionar este problema para sus propios fines, porque mi CSS no está ahí. No es que mis habilidades sean malas; son inexistentes.
Pero puedo vivir con estos problemas. En cuanto a la funcionalidad, he logrado lo que necesito que haga la guía.
Bonificación: abrir documentos de forma independiente
Para nuestro Guide
, creamos el contenido de cada GuidePage
componente directamente usando HTML. Sin embargo, si este código HTML se agrega a través de un componente autónomo, se puede reutilizar para otras interacciones del usuario.
Por ejemplo, el componente CacheControlDescription
muestra una descripción sobre el almacenamiento en caché HTTP:
const CacheControlDescription = () = { return ( pThe Cache-Control header will contain the minimum max-age value from all fields/directives involved in the request, or "no-store" if the max-age is 0/p )}export default CacheControlDescription;
Este componente se puede agregar dentro de a GuidePage
como hicimos antes, pero también dentro de un Modal
componente:
import { useState } from '@wordpress/element';import { Button } from '@wordpress/components';import { __ } from '@wordpress/i18n';import CacheControlDescription from './cache-control-desc';
const CacheControlModalWithButton = ( props ) = { const [ isOpen, setOpen ] = useState( false ); return ( Button icon="editor-help" onClick={ () = setOpen( true ) } / { isOpen ( Modal { ...props } onRequestClose={ () = setOpen( false ) } CacheControlDescription / /Modal ) } / );};export default CacheControlModalWithButton;
Para brindar una buena experiencia de usuario, podemos ofrecer mostrar la documentación solo cuando el usuario interactúa con el bloque. Para eso mostramos u ocultamos el botón dependiendo del valor de isSelected
:
import { __ } from '@wordpress/i18n';import CacheControlModalWithButton from './modal-with-btn';
const CacheControlHeader = ( props ) = { const { isSelected } = props; return ( { __('Cache-Control max-age') } { isSelected ( CacheControlModalWithButton / ) } / );}export default CacheControlHeader;
Finalmente, el CacheControlHeader
componente se agrega al control apropiado.
¿Tadaaaaaaaa?
¡El editor de bloques de WordPress es todo un software! Pude lograr cosas con él que no hubiera podido lograr sin él. Proporcionar documentación al usuario puede no ser el mejor ejemplo o caso de uso, pero es muy práctico y relevante para muchos otros complementos. ¿Quieres usarlo para tu propio complemento? ¡A por ello!
Consulte este tutorial de Cloudways para obtener más información sobre cómo configurar plantillas de páginas personalizadas.
Deja un comentario