Agregar una guía de bienvenida personalizada al editor de bloques de WordPress

Índice
  1. Lo que queremos lograr
  2. Planificación de la implementación
  3. Paso 1: estructuración del guión
  4. Paso 2: incluir en la lista negra el panel de la barra lateral personalizado en todos los demás CPT
  5. Paso 3: Crear la guía personalizada
  6. Paso 4: Agregar contenido a la guía
  7. Bonificación: abrir documentos de forma independiente
  8. ¿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 Guidecomponente 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:

  1. Creación de un nuevo script para registrar el panel de la barra lateral personalizado
  2. Mostrar el panel de la barra lateral personalizado en el editor solo para nuestro tipo de publicación personalizada
  3. Creando la guía
  4. 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 PluginDocumentSettingPanelpara 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 PluginDocumentSettingPanelno 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.jsonarchivo:

{  "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.jscon el código ESNext de arriba para registrar el PluginDocumentSettingPanelcomponente. Al ejecutarse npm start(o npm run builden producción), el código se compilará en build/index.js.

Hay un último problema que resolver: el PluginDocumentSettingPanelcomponente no se importa estáticamente, sino que se obtiene de wp.editPost, y dado que wpes 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-postscript 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-updatepara 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 PluginDocumentSettingPanelestá 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 wpvariable 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 PluginDocumentSettingPanelcomponente, 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 PluginDocumentSettingPanelse 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.phparchivo 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 PluginDocumentSettingPanelcomponente) 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 useSelecten 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 Guidecomponente de WordPress. Al principio no me di cuenta de que estaría haciendo eso, así que así es como pude descubrirlo.

  1. Busque el código fuente para ver cómo se hizo allí.
  2. 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 Guidey 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 Guideserí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 useStategancho, 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 Guidecomponente, que contiene un GuidePagecomponente para cada página de contenido.

El contenido puede usar HTML, incluir otros componentes y todo eso. En este caso particular, agregué tres GuidePageinstancias 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 Guideporque al hacer clic en el botón de reproducción se cierra la guía. Supongo que eso se debe a que iframecae fuera de los límites de la guía. Terminé subiendo el archivo de video a S3 y entregándolo con video.
  • 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 GuidePagecomponente 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 CacheControlDescriptionmuestra 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 GuidePagecomo hicimos antes, pero también dentro de un Modalcomponente:

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 CacheControlHeadercomponente 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.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion