Zum Inhalt springen Zur Fußzeile springen

WordPress Block entwickeln

Wie du einen eigenen WordPress Block für den neuen Gutenberg-Editor entwickelst

„In diesem Artikel werden wir einen eigenen WordPress Block entwickeln.

Wir starten komplett bei null und verzichten bewusst auf Drittanbieter-Plugins, wie z.B. Advanced Custom Fields, um langfristig die volle Kontrolle zu haben.

Das bedeutet auf den ersten Blick mehr Arbeit, sichert uns jedoch nachhaltig eine gewisse Unabhängigkeit, die ich besonders bei Kundenprojekten anstrebe.“

Robert Rosanke,
Webdesigner

Die Zielstellung

Ziel dieses Tutorials ist ein eigener Block, mit dem der Nutzer eine auffällige Infobox ohne HTML und CSS-Kenntnisse erstellen kann.

Folgende Funktionen soll der Block bieten:

Das könnte dann am Ende wie folgt aussehen:

Foto des eigenen Blocks im Praxiseinsatz
So soll der Block später sowohl im Backend als auch im Frontend aussehen

Entwicklungsumgebung für Gutenberg einrichten

Die Einrichtung einer Entwicklungsumgebung ist nicht zwingend für jedes Vorhaben notwendig.

Der große Vorteil einer gleichbleibenden Entwicklungsumgebung: Sie bringt eine gewisse Konstanz in die Arbeitsabläufe, sodass wir effizienter arbeiten können (und werden).

Um die Umgebung für unseren ersten WordPress Block einzurichten, bin ich Florian Brinkmanns Anleitung zur Webpack-Einrichtung für die Gutenberg-Entwicklung Schritt für Schritt durchgegangen.

Diese Anleitung verzichtet auf den Einsatz von weiteren Plugins und ist ohne großes Drumherum auf den Punkt gebracht

Drei Tipps für das Tutorial von Florian:

  1. Geduldig sein – Besonders die Installation von node.js kann eine Weile dauern. Der Rest geht innerhalb weniger Minuten.
  2. Erstelle ein eigenes Plugin für all deine Blöcke, um das Theme in Zukunft einfach wechseln und die Blöcke problemlos in anderen Projekten nutzen zu können.
  3. Tipp für den Abschnitt „package.json erstellen und Abhängigkeiten installieren“: Pfad des neuen Plugins in der Kommandozeile öffnen geht bei Windows mit Shift + Rechtsklick auf den Ordner und Eingabeaufforderung hier öffnen. Der Pfad ist dann direkt anvisiert.
  4. Verwende einen Code Editor, um die wichtigen Dateien des Projekts auf einen Blick zu haben und schnell zwischen den Dokumenten wechseln zu können. Ich nutze den kostenfreien Editor Atom.

Ebenfalls lesenswert in diesem Zusammenhang ist Thomas Weichselbaumers Anleitung zur Erstellung einer Entwicklungsumgebung mit create-guten-block.

Thomas hat mit create-guten-block eine Lösung gefunden, mit der er schnell und unkompliziert eine funktionsfähige Entwicklungsumgebung aufbauen kann.

Während der node.js Installation findest du mit Sicherheit ein paar Minuten, um mal reinzuschauen.

Sobald du die Entwicklungsumgebung eingerichtet hast, legen wir mit der Erstellung des eigenen WordPress Blocks innerhalb eines eigenen Plugins los.

Main Plugin File erstellen und Block registrieren

Sobald die Entwicklungsumbeung nach Florians Artikel aufgebaut ist, geht es mit der eigentlichen Plugin- und Block-Erstellung los.

Falls noch nicht geschehen, führen wir npm run start in der Kommandozeile aus, damit die zukünftigen Äderungen in den .scss-Dateien zwischen Backend und Frontend automatisch aktualisiert und übernommen werden.

Main Plugin File erstellen

Als erstes geben wir unserem Plugin die entsprechenden Header Informationen mit auf den Weg, um beispielsweise einen eigenständigen Namen inklusive aussagekräftiger Beschreibung für das Plugin-Verzeichnis festzulegen.

Falls das dein erstes Plugin ist:
Wir befinden uns jetzt auf Ebene der package.json und in der Datei [dein-plugin-name].php.

Meine Datei heißt wf-gutenberg-blocks.php.

<?php
/**
 *
 * Plugin Name: wf info block
 * Description: Ein eigener WordPress Block für Gutenberg.
 * Version:     Testversion
 * Author:      Robert Rosanke
 * Author URI:  https://webfluence.de/
 * License:     GPL-2.0+
 * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Text Domain: wf-block
 * Domain Path: /languages
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

Weitere Informationen zum Plugin Header auf developer.wordpress.org

Styles mit register_block_type() angeben

Damit die CSS-Dateien unseres Blocks in Backend und Frontend geladen werden, registrieren wir die Styles und übergeben sie an die Funktion register_block_type().

Wir befinden uns noch immer im Main Plugin File und schreiben den folgenden PHP-Code direkt unter den Plugin Header.

/**
 * Register block script and backend stylesheet
 */
	wp_register_script(
		'wf-gutenberg-blocks-editor-script',
		plugins_url( 'assets/js/editor.blocks.js', __FILE__ ),
		[ 'wp-blocks', 'wp-element', 'wp-edit-post', 'lodash' ]
	);
	wp_register_style(
		'wf-gutenberg-blocks-editor-style',
		plugins_url( 'assets/css/editor.blocks.css', __FILE__ )
	);

/**
 * Register block styles for frontend
 */
	wp_register_style(
		'wf-gutenberg-blocks-frontend-style',
		plugins_url( 'assets/css/frontend.blocks.css', __FILE__ )
	);

/**
 * Enqeue scripts and styles for info-box block
 */
register_block_type(
	'webfluence-block/info-box', array(
		'style'         => 'wf-gutenberg-blocks-frontend-style',
		'editor_script' => 'wf-gutenberg-blocks-editor-script',
		'editor_style'  => 'wf-gutenberg-blocks-editor-style',
	)
);

Block in der index.js mit registerBlockType() registrieren

Um nun einen kleinen Platzhalter-Block zu erstellen gehen wir in das Verzeichnis blocks und öffnen die darin enthaltene index.js.

In dieser Datei findet die ganze Magie statt.

Mit der index.js können wir:

Für einige Modifikationen gibt es eine kleine aber offizielle Übersicht über die Block-Filter mit Code-Beispielen.

/**
 * WordPress dependencies
 */
const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;

/**
 * Register block
 */
registerBlockType( 'webfluence-block/info-box',
	{
		title: __( 'Info Box', 'wf-block' ),

		category: 'layout',

		edit() {
			return (
				<div>Das wird im Editor angezeigt</div>
			);
		},

		save() {
			return (
				<div>Das wird im Frontend angezeigt</div>
			);
		},
	}
);

Kurze Erklärung des Codes:

Mit const importieren wir zu Beginn und im weiteren Verlauf der Block-Erstellung Komponenten, die wir für unseren Block brauchen.

i18n samt __ werden dir von der Theme- und Plugin-Übersetzung mit Sicherheit schon bekannt sein.

registerBlockType() beinhaltet den Namen, in unserem Fall webfluence-block/info-box, und die weiteren Block-Settings in Form von Properties.

Die wichtigsten Properties, also die, die den Block zum laufen bringen, sind folgende:

Sobald der Block interaktiv und dynamisch wird, werden wir zudem eigene Variablen mit dem attributes-Objekt festlegen.

Dazu kommen wir später im Artikel.

Die edit– und save-Funktion beinhalten hauptsächlich das Markup und die Platzhalter für dynamische Eingaben. Die optische Darstellung wird weiterhin hauptsächlich per CSS festgelegt.

Plugin aktivieren und kurze Funktionskontrolle

Nun wechseln wir kurz ins WordPress Backend, aktivieren das Plugin und schauen, ob der Block-Platzhalter im Editor erscheint.

Das neue Plugin wird in der Plugin-Übersicht aktiviert.
Das frisch erstellte Plugin wird unter Design » Plugins aktiviert.
Die Infobox erscheint in der Block-Auswahl
Die Info Box ist nun unter der festgelegten Kategorie „Layout“ in der Block-Auswahl verfügbar.
Der neue Infobox-Block wird im Gutenberg Editor dargestellt
Der Platzhalter-Text wird ebenso erfolgreich im Editor angezeigt.

Statischen Basis-Block erstellen (das Grundgerüst)

Nun geht es ans eingemachte.

Der Block ist im System angekommen und es ist an der Zeit das Grundgerüst aufzubauen, damit sich der Block sowohl im Editor als auch im Frontend vom normalen Absatz-Block unterscheidet.

Wir werden jetzt das Markup festlegen und einfache Styles zuweisen, bevor es an die Integration von Eingabefeldern für den Nutzer geht.

Frontend Markup festlegen

Wichtig: Für die Gutenberg-Entwicklung schreiben wir in JSX.

JSX bringt teilweise eine eigene Syntax mit, die sich von klassischem HTML oder der Plugin-Entwicklung mit PHP abhebt.

Dadurch wird beispielsweise für das HTML-Attribut class der Bezeichner className verwendet und aus tabindex wird tabIndex, wie in der JSX-Einleitung nachzulesen ist.

save() {
			return (
        <div className="wf-info-block-wrapper">
          <div className="wf-info-block-header">
            <i className="far fa-lightbulb" aria-hidden="true"></i>
            Kopfzeilen-Text
          </div>
          <p className="wf-info-block-content">
          Content
          </p>
        </div>
			);
		},

Damit Markup-Änderungen wirksam werden, muss der Block im Backend nach den Änderungen noch einmal neu zur Seite hinzugefügt werden. Andererseits bleibt einfach der alte Block, also unser Platzhalter-Block ohne das neue Markup, zu sehen.

Schauen wir einmal, was im Frontend zu sehen ist.

Frontend Markup des Blocks in den Chrome DevTools
Das Frontend Markup in den Chrome DevTools.

Beim genaueren Hinschauen fällt auf, dass WordPress automatisch eine weitere CSS-Klasse in das erste div eingestzt hat. Diese besteht aus dem Prefix wp-blocks und der Bezeichnung, mit der die Block Styles zu Beginn registriert wurden.

Die Code-Ansicht unseres neuen Blocks im Gutenberg-Editor

In meinem kleinen Test wurde auch das Individuelles CSS bereits automatisch an den Wrapper-Block übergeben, das heiß: soweit klappt alles.

Backend Markup festlegen

Da wir ein einheitliches Erscheinungsbild anstreben, passen wir nun das Backend-Markup an.

Dafür können wir den return-Teil des Frontend Markups, bis auf eine kleine Anpassung, einfach übernehmen.

edit( props ) {
  	const { className } = props;

  	return (
  		<div className={ className + " wf-info-block-wrapper" }>
          <div className="wf-info-block-header">
            <i className="far fa-lightbulb" aria-hidden="true"></i>
            Kopfzeilen-Text
          </div>
          <p className="wf-info-block-content">
          Content
          </p>
  		</div>
  	);
  },

Die Klasse wp-block-webfluence-block-info-block, die automatisch zum ersten div des Frontend Markups hinzugefügt wird, müssen wir nun manuell in das Backend Markup integrieren, um die Einheitlichkeit zu erreichen.

Dafür wird lediglich das erste div ein wenig angepasst.

Im ersten Teil der Klassenzuweisung wird mit className={ className die automatisch generierte Klasse wp-block-webfluence-block-info-block übergeben.

Anschließend fügen wir mit + " wf-info-block-wrapper" } unsere eigene Wrapper-Klasse hinzu.

Die geschweiften Klammern werden in JSX vereinfacht gesagt genutzt, um inline JavaScript auszuführen. Und um unsere CSS-Klasse (ein string) abzugrenzen, werden die hochgestellten Anführungszeichen genutzt.

Ich gehe davon aus, dass diese Vorgehensweise mit dem + und einem „string“ nicht best practise sind, um in JSX mehrere CSS-Klassen zuzuweisen. Doch für den Anfang funktioniert die Schreibweise und das ist das wichtigste für den ersten Block.

Die Styles laden

Wenn du das Setup nach der Anleitung von Florian Brinkmann aufgebaut hast, dann steht in der Datei editor.scss bereits @import ‚./frontend‘;.

Dadurch werden die Styles aus dem Frontend-Dokument automatisch ins Backend-Dokument übernommen, sodass das Design nur einmal erstellt werden muss und auf Backend und Frontend angewendet wird.

Meine frontend.scss sieht nun so aus:

.wf-info-block-wrapper {
 background-color: #f4f4f4;
}

.wf-info-block-header {
  background-color: #e6e6e6;
  font-weight: 600;
  padding: 5px;
}

.wf-info-block-header i {
  color: yellow;
  margin-right: 8px;
}

.wf-info-block-content {
  margin: 0;
  padding: 10px 5px;
}

Nachdem alles gespeichert ist, sind die Styles soweit auch übernommen.

Die Backend-Ansicht des Blocks nach der Erstellung der CSS-Regeln

Lediglich FontAwesome muss per Plugin Main File noch in die Webseite integriert werden. Sowohl Frontend als auch Backend.

Viele Menschen empfehlen FontAwesome, jQuery und weitere bekannte Frameworks seit der Datenschutz-Grundverordnung lokal zu hosten und einzubinden.
Da ich hier ‚nur‘ eine lokale Testumgebung auf dem eigenen PC habe, habe ich an dieser Stelle den schnelleren Weg gewählt und ziehe FontAwesome von externen Servern.

/**
 *  Enqueue FontAwesome for backend.
 */
add_action( 'admin_enqueue_scripts', function() {
  wp_enqueue_style('admin-styles', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css');
} );
/**
 *  Enqueue FontAwesome for frontend.
 */
add_action( 'wp_enqueue_scripts', function() {
  wp_register_style( 'wf-fontawesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css', $deps, $ver = null, $media = 'all' );
  wp_enqueue_style('wf-fontawesome');
} );

Kurzer Check – Sind die Icons angekommen?

Die Icons sind angekommen und der Block sieht im Backend und Frontend einheitlich aus
Die Info Box wird nun sowohl im Backend als auch im Frontend mit Icon angezeigt, sodass eine einheitliche Darstellung gewährleistet ist.

Den WordPress Block mit Komponenten erweitern

Damit der Block nun auch verwendet werden kann, fehlen lediglich die Eingabefelder, mit denen der Nutzer zukünftig eine Überschrift sowie den Inhalt für die Infobox festlegen kann.

Für den angedachten Verwendungszweck sind 3 Funktionen notwendig.

  1. Text soll in die Kopfzeile eingegeben werden können
  2. Der Inhalt soll aus weiteren Blöcken bestehen. Der Nutzer soll selbständig entscheiden, ob er Fotos, Embeds, Listen etc. oder auch mehrere verschiedene Blöcke in den Inhaltsbereich der Info-Box integriert.
  3. Stil der Box soll in der Sidebar ausgewählt werden können
    • Tipp = Grüne Lampe als Icon
    • Achtung = Rotes Schild als Icon
    • Anmerkung = Blaues i-Symbol

Eingabefeld erstellen mit RichText

Schritt 1: RichText-Komponente integrieren

Um editierbare Eingabefelder für unseren Block zu ermöglichen brauchen wir eine RichText-Komponente.

Füge dazu const { RichText } = wp.editor; an den Anfang der index.js hinzu.

Schritt 2: Eingabefeld erstellen

Derzeit sieht unser Platzhalter für das Kopfzeilen-Eingabefeld in der edit-Funktion wie folgt aus:

<div className="wf-info-block-header">
            <i className="far fa-lightbulb" aria-hidden="true"></i>
            Kopfzeilen-Text
</div>

Um den Platzhalter gegen ein Eingabefeld auszutauschen, nutzen wir die eben integrierte RichText-Komponente.

Im folgenden Beispiel erstellen wir ein Eingabefeld, dass nun unseren statischen Platzhalter ersetzt.

<RichText
						tagName="p"
						value={ attributes.boxTitle }
						className="wf-info-block-header"
						onChange={ changeBoxTitle }
						placeholder={ __( 'Add a title…', 'wf-block' ) }
						keepPlaceholderOnFocus
					/>

Das FontAwesome-Icon hat den Umstieg auf die RichText-Komponente nicht überlebt und wird später wieder hinzugefügt…

Das wichtigste erklärt:

tagName legt fest, welchen HTML-Tag das Eingabefeld im Backend-Editor erhalten soll

value bestimmt den Wert, sprich den Inhalt des Eingabefelds

className legt die CSS-Klasse fest

onChange ruft eine Funktion auf, die bei Änderungen durch den Nutzer im Backend aufgerufen wird

placeholder stellt einen Platzhalter bereit, den der Nutzer im Backend sieht, wenn er noch kein individueller Inhalt eingefügt hat

keepPlaceholderOnFocus legt fest, dass der Platzhalter weiterhin angezeigt werden soll, wenn der Nutzer den leeren Block fokussiert

Eine umfassende Liste mit weiteren RichText-Properties gibt es auf GitHub.

Damit das Eingabefeld funktionstüchtig wird, müssen wir die value und onChange Properties der RichText-Komponente nun mit Inhalten füttern.

Schritt 2.1 Attribut für die value-Property erstellen

Damit der Gutenberg-Editor den Wert des Eingabefeldes auch nach dem Abspeichern und neu laden des Beitrags wiederfindet, durchsucht er die Code-Ansicht und baut sich daraus die visuelle Ansicht, die der Anwender genießen darf.

Mit unserer Angabe value={ attributes.boxTitle } sucht der Editor nun nach dem Attribut boxTitle.

Damit die Suche erfolgreich endet, fügen wir folgenden Code hinter den Basis Infos zum Block, also hinter title, category, etc., hinzu und erstellen das Attribut:

attributes: {
			boxTitle: {
				type: 'string',
				source: 'html',
				selector: '.wf-info-block-header',
			}
		},

Das nun erstellte boxTitle-Attribut sagt dem Editor, wonach er suchen muss, um das Eingabefeld zu befüllen.

Der Editor durchsucht die Code-Ansicht nach Elementen mit der Klasse .wf-info-block-header und hat durch source: 'html', Zugriff auf den gesamten HTML-Inhalt dieser Elemente.

Somit können wir damit den bestehenden Inhalt aus dem Eingabefeld auslesen.

/* Ein Beispiel zur Veranschaulichung */

<p class="wf-info-block-header"><strong>Geänderte Öffnungszeiten<strong> im Herbst</p>

/* Wert von boxTitle: <strong>Geänderte Öffnungszeiten<strong> im Herbst */

Hätten wir stattdessen source: 'text', gewählt, dann würde lediglich der Text ohne die <strong>-Tags übermittelt.

Weitere Informationen zu Attributen

Schritt 2.2 Funktion für die onChange-Property erstellen

Damit der Inhalt des Eingabefelds mit dem eben festgelegten Element synchronisiert wird, sobald der Nutzer dort etwas anderes eingibt, müssen wir die Funktion erstellen, die wir an die onChange-Property übermittelt haben.

Hier noch einmal die entsprechende Zeile:

onChange={ changeBoxTitle }

Sobald der Nutzer den Wert des Eingabefelds ändert, wird die Funktion changeBoxTitle() aufgerufen.

In unserem Beispiel soll diese Funktion den neu eingegeben Wert dem boxTitle zuweisen, weil dieser Zugriff auf unser Element mit der CSS-Klasse .wf-info-block-header hat.

Und das Element mit dieser CSS-Klasse ist schließlich das, worin sich die Überschrift unserer Infobox befindet.

Deshalb fügen wir folgenden Schnipsel vor das return-Statement innerhalb der edit-Funktion ein.

function changeBoxTitle( newBoxTitle ) {
	setAttributes( { boxTitle: newBoxTitle } );
}

Die onChange-Property übermittelt den neuen Eingabefeld-Wert mit der Variable newBoxTitle an die Funktion changeBoxTitle().

Das einzige was die Funktion tut ist es das Attribut boxTitle zu nehmen und diesem den neuen Wert zuzuschreiben.

Die Bezeichnung newBoxTitle ist an dieser Stelle frei gewählt und muss nirgendwo außerhalb der Funktion deklariert werden. Du kannst natürlich einen anderen Bezeichner wählen, der besser zu deinem Vorhaben passt.

Schritt 2.3 Properties der edit-Funktion aktualisieren

Um das Attribut boxTitle an die edit-Funktion zu übergeben, tauschen wir die bestehende Zeile Code

const { className } = props;

gegen folgenden Absatz aus:

const {
  		attributes,
  		className,
  		setAttributes,
  	} = props;

Die edit-Funktion sieht nun wie folgt aus:

edit( props ) {
  	const {
  		attributes,
  		className,
  		setAttributes,
  	} = props;

  	function changeBoxTitle( newBoxTitle ) {
  		setAttributes( { boxTitle: newBoxTitle } );
  	}

  	return (
  		<div className={ className }>
        <div className="wf-info-block-wrapper">
          <RichText
						tagName="p"
						value={ attributes.boxTitle }
						className="wf-info-block-header"
						onChange={ changeBoxTitle }
						placeholder={ __( 'Add a title…', 'wf-block' ) }
						keepPlaceholderOnFocus
					/>

          <p className="wf-info-block-content">
          Content
          </p>
        </div>
  		</div>
  	);
  },

Kurzer Zwischenstand

So sieht unser Block im Backend aus

Der Block hat eine beschreibbares Eingabefeld erhalten
Unser Block verfügt über ein Eingabefeld in der Kopfzeile und einen Platzhalter-Text
Schritt 3: Eingabefeld-Wert mit RichText.Content an den Post Content übermitteln

Jetzt wo der Gutenberg-Editor im Backend den Post Content nach unserem Wert für das Eingabefeld durchsuchen und diesen unserem Block zuordnen kann, ist es an der Zeit die Frontend Ausgabe anzupassen.

Dafür ersetzen wir in der save-Funktion die statische Platzhalter Überschrift

<div className="wf-info-block-header">
      <i className="far fa-lightbulb" aria-hidden="true"></i>
      Kopfzeilen-Text
</div>

mit dem folgendem Part:

<RichText.Content
            tagName="p"
            className="wf-info-block-header"
            value={ boxTitle }
          />

Mit RichText.Content und der obigen Konfiguration erhalten wir den Eingabefeld-Wert über die Property value und setzen ihn das eigens kreierte p-Element mit der CSS-Klasse wf-info-block-header.

Somit haben wir den Wert, der bis dato lediglich im Gutenberg-Editor gespeichert war, aufgegriffen und in der Code-Ansicht abgelegt.

Ebenso übermittelten wir der save-Funktion die Attribute, um RichText.Content den Zugriff auf den boxTitle zu ermöglichen.

  save( { attributes } ) {
    const {
      boxTitle,
    } = attributes;
			return (
....

Die save-Funktion sieht nun wie folgt aus:

  save( { attributes } ) {
    const {
      boxTitle,
    } = attributes;
			return (
        <div className="wf-info-block-wrapper">
          <RichText.Content
            tagName="p"
            className="wf-info-block-header"
            value={ boxTitle }
          />

          <p className="wf-info-block-content">
          Content
          </p>
        </div>

Dank RichText und RichText.Content kann der Titel manuell vom Nutzer editiert und ausgegeben werden.

Das FontAwesome-Icon habe ich mit dem einfügen der RichText-Komponente entfernt, weil es später als Pseudo-Element mit einer CSS-Klasse hinzugefügt werden soll.

Dadurch wird die Infobox flexibler und fortgeschrittene User mit CSS-Kenntnissen können die Styles der Box ändern und bei Bedarf einen individuellen, weiteren Boxen-Stil erstellen.

Blöcke innerhalb eines Blocks erlauben mit InnerBlocks

So.

Das Eingabefeld für den Header ist erstellt und vollständig funktionsfähig.

Ich will uns an dieser Stelle kurz die Zielstellung zurück ins Gedächtnis rufen.

Zwei der vier Zielstellungen sind bereits abgehakt.

Fahren wir zur Erholung kurz mit einem einfachen und entspannten Thema fort.

InnerBlocks.

Mit <InnerBlocks /> können wir dem Nutzer erlauben selbständig Blöcke innerhalb eines Containers zu verwenden.

Schritt 1: InnerBlocks-Komponente integrieren

Wir fügen InnerBlocks zu den Konstanten am Beginn der index.js hinzu:

const { InnerBlocks } = wp.editor;

Schritt 2: Bereich für freie Block-Auswahl festlegen

An der Stelle, an der der Nutzer selbständig Blöcke hinzufügen darf, fügen wir <InnerBlocks /> ein.

Da jeder Block sein eigenes Markup mitbringt, tauschen wir den p-tag gegen ein div aus, sodass folgendes Ergebnis in der edit-Funktion anstelle der Platzhalters herauskommt:

<div className="wf-info-block-content">
          <InnerBlocks />
          </div>

In diesem Fall soll der Nutzer frei entscheiden dürfen, welche Blöcke er in den Content-Bereich der Infobox packt.

Falls das bei dir nicht erwünscht ist, dann kannst du ein Array mit erlaubten Blöcken festlegen.

<InnerBlocks allowedBlocks={ [ 'core/heading', 'core/paragraph' ] } />

Bei wpdc gibt es eine Auflistung der Core-Block-Slugs, mit denen du schnell und einfach herausfindest, welche Bezeichnungen die Blocks intern nutzen.

Eine umfassende Liste mit weiteren InnerBlocks-Properties gibt es auf GitHub.

Schritt 3: Eingefügte Blöcke mit InnerBlocks.Content im Frontend ausgeben

Um den Content der inneren Blöcke an das Frontend zu übermitteln, fügen wir <InnerBlocks.Content /> in die save-Funktion ein.

<div className="wf-info-block-content">
          <InnerBlocks.Content />
          </div>
Der Nutzer kann endlich selbständig Blöcke frei hinzufügen und mit Inhalt befüllen
Der aktuelle Stand. Unser Block akzeptiert über ein individuelles Eingabefeld eine Überschrift und gibt dem Nutzer die Möglichkeit den Content-Bereich eigenständig und flexibel mit Inhalten zu befüllen.

Ergebnis: Nun kann der Nutzer weitere Blöcke im Content-Bereich der Infobox verwenden.

Die Ausgabe funktioniert soweit auch. Lediglich am CSS sollten wir vor der Integration des Blockes noch einmal schrauben.

Quelle: https://www.ibenic.com/enable-inner-blocks-gutenberg/

Sidebar Einstellungen hinzufügen mit InspectorControls

Kommen wir zur letzten, wichtigen Aufgabe.

Wir erteilen dem Block nun drei Design-Stile, die der Nutzer bequem aus einem Dropdown in den Block-Einstellungen der Sidebar auswählen kann.

Ähnlich wie Text im Paragraph-Block die Stile „klein“, „normal“ und „groß“ erhalten kann, soll unser Block folgende Stile bzw. Auswahlmöglichkeiten erhalten:

  1. Anmerkung
  2. Info
  3. Warnung

Jeder Stil soll dem Block dann eine CSS-Klasse hinzufügen.

Mit dieser CSS-Klasse können wir nachhaltig und ohne großen Programmieraufwand individuelle Erscheinungsbilder für die verschiedenen Stile festlegen und über das CSS-Pseudo-Element :before ein FontAwesome-Icon hinzufügen.

Als erstes fügen wir neue Konstanten an den Anfang der index.js ein:

const { InspectorControls } = wp.editor;
const { PanelBody } = wp.components;

Jetzt wird es ein wenig tricky.

Deshalb erstellen wir einen Platzhalter, den wir zuerst Testweise in der Sidebar positionieren.

<InspectorControls>
						<PanelBody title={ __( 'Choose Style', 'wf-block' ) }>
							<p>Lorem ipsum</p>
						</PanelBody>
					</InspectorControls>

Damit die Einstellungen in die Sidebar wandern, muss die edit-Funktion angepasst werden.

Aus der einfachen Ausgabe des Inhalts wird nun ein Array, das neben dem bisherigen Block-Content die Sidebar-Inhalte, also das InspectorControls-Element, beinhaltet.

Hier noch einmal veranschaulicht:

vorher:

return 	(
					
			  		//ein paar div-container und eine RichText-Komponente
			);
    },

jetzt:

return 	[     /* aus normaler Klammer wird eine eckige Klammer */

					<InspectorControls>
						<PanelBody title={ __( 'Choose Style', 'wf-block' ) }>
							<p>Lorem ipsum</p>
						</PanelBody>
					</InspectorControls>,    /* hier ist ein Komma dazugekommen */

			  		// ein paar div-container und eine RichText-Komponente

			];     /* aus normaler Klammer wird eine eckige Klammer */
    },

Nach dem Abspeichern sind die Überschrift sowie der Platzhalter erfolgreich in der Sidebar angekommen.

Die Sidebar-Einstellungen auf einen Blick.

Nun muss das Ganze noch funktional werden:

Dropdown einfügen mit SelectControl

Schritt 1: SelectControl-Komponente importieren

Wir begeben uns wieder an den Anfang der index.js und importieren die SelectControl-Komponente.

const { SelectControl } = wp.components;
Schritt 2: Attribut mit Standardwert hinzufügen

Wir fügen unseren attributes ein weiteres Mitglied hinzu.

selectedStyle: {
	       type: 'string',
	       default: 'info',
	     },

Das neue Attribut selectedStyle beinhaltet den Standardwert für das Auswahlfeld.

Da ich schätze, dass der Info-Kasten der meist genutzte Stil sein wird, habe ich mit default: 'info', einen entsprechenden Standardwert festgelegt.

Schritt 3: Dropdown integrieren und Werte festlegen

In der edit-Funktion schmeiße ich den „Lorem ipsum“ Platzhalter raus und setze dafür das Dropdown-Menü ein.

<SelectControl
				        label={ __( 'Choose info box style', 'wf-block' ) }
				        value={ props.attributes.selectedStyle }
				        onChange={ ( selectedStyle ) => { props.setAttributes( { selectedStyle } ) } }
				        options={ [
				            { value: 'annotation', label: 'Anmerkung' },
				            { value: 'info', label: 'Info' },
				            { value: 'warning', label: 'Warnung' },
				        ] }
				    />

Der Code erklärt:

label lässt uns den Bezeichner für das Dropdown festlegen.

value ruft das Attribut, dem der Wert zugeordnet werden soll. Ein einfacher 'string' sollte auch möglich sein.

onChange lässt uns festlegen, was passieren soll, wenn der Nutzer eine Option des Dropdowns wählt. Hier hätten wir natürlich auch eine Funktion erstellen und aufrufen können, wie bei dem RichText-Element für das individuelle Eingabefeld.

options lässt uns die Auswahlmöglichkeiten festlegen. value ist der Wert, mit dem das System arbeitet und label die Beschriftung, die der Nutzer sieht.

Schritt 4: Dropdown-Werte auf die optische Darstellung anwenden

Je nachdem, welche Option der Nutzer ausgewählt hat, hat das Attribut selectedStyle einen der folgenden Werte:

Das Ziel ist es jetzt diesen Wert in der save-Funktion mittels CSS-Klasse an die Infobox zu übermitteln.

Hier noch einmal der aktuelle Stand der save-Funktion:

  save( { attributes } ) {
    const {
      title,
    } = attributes;
			return (
        <div className="wf-info-block-wrapper">
          <RichText.Content
            tagName="p"
            className="wf-info-block-header"
            value={ title }
          />

          <div className="wf-info-block-content">
          <InnerBlocks.Content />
          </div>
        </div>
			);
		},

Mit einer dynamischen CSS-Klasse ala wf-info-box-style-[hier den ausgewählten Wert einsetzen], die an den ersten, äußeren div-Container übermittelt wird, hätten wir eine ziemlich effiziente Lösung für das Styling gefunden, weil Icon zukünftig mittels CSS geändert werden können anstatt jeden Block einzeln in der index.js zu bearbeiten und Invalidation Errors zu riskieren.

Der Wrapper-Container, der im Frontend am Ende ausgegeben werden soll, ist bis jetzt <div className= "wf-info-block-wrapper">.

In der edit-Funktion machen wir aus dieser Zeile nun folgende:

<div className={ "wf-info-block-wrapper wf-info-block-style-" + attributes.selectedStyle }>

Diese Zeile beinhaltet die Wrapper-Klasse wf-info-block-wrapper und die Klasse wf-info-block-style-, die automatisch um den Wert der in der Sidebar ausgewählten Dropdown-Option erweitert wird.

Entsprechend der drei möglichen Auswahlpunkte entstehen somit folgende Klassen, die später per CSS festgelegt werden müssen:

Damit der Dropdown-Wert auch an die save-Funktion übermittelt und in den Post Content übergeben wird, fügen wir am Anfang der save-Funktion das Attribut selectedStyle hinzu und integrieren das Attribut entsprechend in den äußeren div-Container des Markups.

save( { attributes } ) {
    const {
      title,
			selectedStyle,
    } = attributes;

			return (
<div className={ "wf-info-block-wrapper wf-info-block-style-" + selectedStyle }>
…..

Ein kleiner Blick in die Chrome DevTools verrät, dass das Markup entsprechend der gewählten Stilrichtungen ausgegeben wird.

Im Backend klappt es ebenso problemlos.

Die dynamisch generierten CSS-Klassen werden wie gewünscht an den Post Content übermittelt und sind somit im Frontand verfügbar.

Anwendungsbeispiele für CheckBoxen, Textfelder und Weiteres mit InspectorControls gibt es auf GitHub.

Außerdem gibt es auf GitHub eine Liste mit allen Komponenten, falls du weitere Funktionen, wie ein festes Media-Upload-Feld, hinzufügen willst

Schritt 5: Finales Styling mit CSS

Mit nur wenigen Zeilen CSS sind die Blöcke gestaltet und werden dank dem smarten Setup automatisch auch im Backend entsprechend dargestellt.

Links Backend, Rechts die Frontend-Ansicht. für unseren ersten Block kann sich das Ergebnis durchaus sehen lassen.

Toolbar-Einstellungen verfeinern

Jetzt wo der Block an sich fertig ist, habe ich spontan festgestellt, dass eine weite Breite bei einer Infobox durchaus sinnvoll sein könnte.

Deshalb fügen wir nach dem attributes-array folgendes in die index.js:

supports: {
			align: [ 'wide' ],
		},

Nun kann der Nutzer die Option ‚weite Breite‘ wählen, wie es beim Bild-Block standardmäßig möglich ist.

Für weitere Einstellungen, wie zum Beispiel die Aktivierung des HTML-Anker Felds, das es sonst nur bei Überschriften gibt, lohnt ein Blick in das Block Editor Handbook.

Finale Optimierungen

Zu guter letzt räumen wir den Code noch ein bisschen auf.

Konstanten zu arrays machen

Aus:

/**
 * WordPress dependencies
 */
const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
const { RichText } = wp.editor;
const { InnerBlocks } = wp.editor;
const { InspectorControls } = wp.editor;
const { PanelBody } = wp.components;
const { SelectControl } = wp.components;

Wird:

/**
 * WordPress dependencies
 */
const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
const { InspectorControls,
				InnerBlocks,
				RichText
			} = wp.editor;
const { PanelBody,
				SelectControl
			} = wp.components;

Weitere Informationen für den Block bereitstellen

Zum aktuellen Zeitpunkt hat unser Block lediglich eine Bezeichnung und ist einer Kategorie zugeordnet.

Es wird daher Zeit ein eigenes Icon, eine passende Beschreibung und Suchbegriffe für die Block-Auswahl festzulegen festzulegen.

/**
 * Register block
 */
registerBlockType( 'webfluence-block/info-box',
	{

		title: __( 'Info Box', 'wf-block' ),

		description: __( 'Highlight important information in a custom info box.', 'wf-block' ),
		// Core categories: common, embed, formatting, layout, widgets
		category: 'layout',

		// Dashicons - https://developer.wordpress.org/resource/dashicons/#wordpress
		icon: 'warning',

		keywords: [
			__( 'annotation', 'wf-block' ),
			__( 'information', 'wf-block' ),
			__( 'warning', 'wf-block' ),
			__( 'box', 'wf-block' ),
		],

    attributes: {
….