Créer une page de paramètres à l’aide des composants WordPress Block Editor (Gutenberg)
Nous avons déjà examiné le stockage des options et des paramètres à l’aide de l’éditeur de blocs WordPress (Gutenberg) et l’extension du script de création de bloc pour permettre des points de terminaison supplémentaires. Dans ce guide, nous allons tous les rassembler pour créer une page de paramètres utilisant des composants Gutenberg.
La page des paramètres que nous sommes sur le point de créer
Mais d’abord, tout le mérite revient, l’inspiration pour ce guide va à l’article Code in WP de Hardeep Asrani: Making a "Plugin Options Page" With Gutenberg Components.
Conditions préalables
- Avoir suivi le guide Création d’un plugin pour l’éditeur de blocs WordPress (Gutenberg)
- Avoir suivi le guide Utilisation des options pour stocker des données dans l’éditeur de blocs WordPress (Gutenberg)
- Avoir suivi le guide Ajouter des points d’entrée au guide WordPress Créer un script de bloc
Créer la page des paramètres en PHP
Suite aux guides des prérequis, ouvrez le fichier PHP racine du plugin (dans ce cas wholesome-plugin.php) et ajoutez ce qui suit :
Enregistrez les paramètres
Comme dans le guide d’utilisation des options pour stocker les données, ajoutez les paramètres suivants au fichier :
function wholesomecode_wholesome_plugin_register_settings() {
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_select',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text_2',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text_3',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_toggle',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
}
add_action( 'init', 'wholesomecode_wholesome_plugin_register_settings', 10 );
Enregistrez les paramètres auxquels nous aurons accès sur la page des paramètres. Assurez-vous de régler show_in_restsur truepour chacun, afin qu’ils soient accessibles via Gutenberg.
Enregistrez la page des paramètres
Ajoutez le bloc de code pour enregistrer la page des paramètres :
function wholesomecode_wholesome_plugin_settings_page() {
add_options_page(
__( 'Wholesome Plugin Settings', 'wholesome-plugin' ),
__( 'Wholesome Plugin Settings', 'wholesome-plugin' ),
'manage_options',
'wholesome_plugin_settings',
function() {
?>
<div id="wholesome-plugin-settings"></div>
<?php
},
);
}
add_action( 'admin_menu', 'wholesomecode_wholesome_plugin_settings_page', 10 );
Le code ci-dessus ajoute une nouvelle page au menu des paramètres. Notez que tout ce qu’il fait est de sortir un <div>, c’est ce que nous utiliserons pour rendre les composants Gutenberg basés sur React.
Élément du menu Paramètres
Mettre en file d’attente les ressources d’administration
Pour le prochain bloc de code, nous devons avoir suivi toutes les étapes du guide Ajouter des points d’entrée au guide Créer un script de bloc. Assurez-vous de suivre toutes les étapes de ce guide avant cette étape, puis revenez et suivez le reste de ce guide.
function wholesomecode_wholesome_plugin_admin_scripts() {
$dir = __DIR__;
$script_asset_path = "$dir/build/admin.asset.php";
if (! file_exists( $script_asset_path)) {
throw new Error(
'You need to run `npm start` or `npm run build` for the "wholesomecode/wholesome-plugin" block first.'
);
}
$admin_js = 'build/admin.js';
$script_asset = require( $script_asset_path );
wp_enqueue_script(
'wholesomecode-wholesome-plugin-admin-editor',
plugins_url( $admin_js, __FILE__ ),
$script_asset['dependencies'],
$script_asset['version']
);
wp_set_script_translations( 'wholesomecode-wholesome-plugin-block-editor', 'wholesome-plugin' );
$admin_css = 'build/admin.css';
wp_enqueue_style(
'wholesomecode-wholesome-plugin-admin',
plugins_url( $admin_css, __FILE__ ),
['wp-components'],
filemtime( "$dir/$admin_css") );
}
add_action( 'admin_enqueue_scripts', 'wholesomecode_wholesome_plugin_admin_scripts', 10 );
Construire la page d’administration en JavaScript
Si vous avez suivi toutes les étapes du guide Add Entry Points to the Create Block Script, vous devriez avoir un /src/admin.jsfichier. Ouvrez ce fichier et supprimez son contenu.
Rendre le composant
N’oubliez pas d’exécuter npm startdans votre terminal conformément au guide de création de plug -ins et ajoutez ce qui suit à votre /src/admin.jsfichier.
import './admin.scss';
import { Icon } from '@wordpress/components';
import {
Fragment,
render,
Component,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
class App extends Component {
constructor() {
super( ...arguments );
}
render() {
return (<Fragment>
<div className="wholesome-plugin__header">
<div className="wholesome-plugin__container">
<div className="wholesome-plugin__title">
<h1>{ __( 'Wholesome Plugin Settings', 'wholesome-plugin') } <Icon icon="admin-plugins" /></h1>
</div>
</div>
</div>
<div className="wholesome-plugin__main"></div>
</Fragment>) }
}
document.addEventListener( 'DOMContentLoaded',() => {
const htmlOutput = document.getElementById( 'wholesome-plugin-settings' );
if (htmlOutput) {
render(
<App />,
htmlOutput
);
}
});
Si vous accédez à votre page de paramètres dans le navigateur, vous devriez maintenant voir ce qui suit :
Rendu des composants dans l’écran des paramètres
Ajouter les champs de paramètres
Vous souvenez-vous des étapes supplémentaires du guide "Utilisation des options pour stocker des données" ? Eh bien, nous allons à peu près coller ces verbatim dans ce composant, vous devriez donc vous retrouver avec un code qui ressemble un peu à ceci :
import './admin.scss';
import api from '@wordpress/api';
import {
Button,
Icon,
Panel,
PanelBody,
PanelRow,
Placeholder,
SelectControl,
Spinner,
TextControl,
ToggleControl,
} from '@wordpress/components';
import {
Fragment,
render,
Component,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
class App extends Component {
constructor() {
super( ...arguments );
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
api.loadPromise.then(() => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Fragment>
<div className="wholesome-plugin__header">
<div className="wholesome-plugin__container">
<div className="wholesome-plugin__title">
<h1>{ __( 'Wholesome Plugin Settings', 'wholesome-plugin') } <Icon icon="admin-plugins" /></h1>
</div>
</div>
</div>
<div className="wholesome-plugin__main">
<Panel>
<PanelBody
title={ __( 'Panel Body One', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Two', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Three', 'wholesome-plugin') }
icon="admin-plugins"
>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Four', 'wholesome-plugin') }
icon="admin-plugins"
>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
<Button
isPrimary
isLarge
onClick={ () => {}}
>
{ __( 'Save', 'wholesome-plugin') }
</Button>
</Panel>
</div>
</Fragment>) }
}
document.addEventListener( 'DOMContentLoaded', () => {
const htmlOutput = document.getElementById( 'wholesome-plugin-settings' );
if (htmlOutput) {
render(
<App />,
htmlOutput
);
}
});
Outre la suppression du subscribein componentDidMountque nous utilisions précédemment pour enregistrer dans ce guide et l’ajout du bouton, le code est à peu près un copier-coller.
Tout va bien, notre page de paramètres devrait maintenant ressembler un peu à ceci :
Rendu des champs de paramètres
Ne vous inquiétez pas, nous allons nettoyer les styles dans la section 4 de ce guide.
Gérer la sauvegarde
Dans le onClickgestionnaire du <button>composant ajoutez le code suivant :
<Button
isPrimary
isLarge
onClick={ () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle? 'true': '',
} );
settings.save();
}}
>
{ __( 'Save', 'wholesome-plugin') }
</Button>
Cela enregistrera nos options et paramètres lorsque le bouton sera cliqué. Cependant, rien n’indique que les options ont été enregistrées par défaut.
Créer la notification
Pour informer notre utilisateur que les options et les paramètres ont été enregistrés, implémentons une notification "snackbar". Il s’agit de la même notification utilisée dans l’écran principal de l’éditeur de publication que l’éditeur de bloc utilise lorsque la publication a été enregistrée.
Pour ajouter cela, nous devons porter un composant principal de Gutenberg dans notre build, car la liste des notifications n’est pas disponible à l’aide des instructions d’importation habituelles.
Nous devrons ajouter le code suivant dans le fichier :
import { SnackbarList } from '@wordpress/components';
import {
dispatch,
useDispatch,
useSelect,
} from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
const Notices = () => {
const notices = useSelect( (select) =>
select( noticesStore) .getNotices()
.filter( (notice) => notice.type === 'snackbar' ),
[]
);
const { removeNotice } = useDispatch( noticesStore );
return (<SnackbarList
className="edit-site-notices"
notices={ notices }
onRemove={ removeNotice }
/>
);
};
Ensuite, dans le rendu principal du <App>composant, ajoutez ce qui suit avant la fermeture</Fragment> :
<div className="wholesome-plugin__notices">
<Notices/>
</div>
Enfin, ajoutez ce qui suit au onClickgestionnaire du bouton :
dispatch('core/notices').createNotice(
'success',
__( 'Settings Saved', 'wholesome-plugin' ),
{
type: 'snackbar',
isDismissible: true,
}
);
Cela créera une petite fenêtre contextuelle "snackbar" chaque fois que les paramètres seront enregistrés.
Notification de snack en action
Je sais, je sais, nous devons encore corriger ces styles.
/src/admin.jsLe dossier complet
Pour référence, voici le /src/admin.jscode complet du fichier :
import './admin.scss';
import api from '@wordpress/api';
import {
Button,
Icon,
Panel,
PanelBody,
PanelRow,
Placeholder,
SelectControl,
SnackbarList,
Spinner,
TextControl,
ToggleControl,
} from '@wordpress/components';
import {
dispatch,
useDispatch,
useSelect,
} from '@wordpress/data';
import {
Fragment,
render,
Component,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
const Notices = () => {
const notices = useSelect( (select) =>
select( noticesStore) .getNotices()
.filter( (notice) => notice.type === 'snackbar' ),
[]
);
const { removeNotice } = useDispatch( noticesStore );
return (<SnackbarList
className="edit-site-notices"
notices={ notices }
onRemove={ removeNotice }
/>
);
};
class App extends Component {
constructor() {
super( ...arguments );
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Fragment>
<div className="wholesome-plugin__header">
<div className="wholesome-plugin__container">
<div className="wholesome-plugin__title">
<h1>{ __( 'Wholesome Plugin Settings', 'wholesome-plugin') } <Icon icon="admin-plugins" /></h1>
</div>
</div>
</div>
<div className="wholesome-plugin__main">
<Panel>
<PanelBody
title={ __( 'Panel Body One', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Two', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Three', 'wholesome-plugin') }
icon="admin-plugins"
>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
</PanelBody>
<PanelBody
title={ __( 'Panel Body Four', 'wholesome-plugin') }
icon="admin-plugins"
>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
<Button
isPrimary
isLarge
onClick={ () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle? 'true': '',
} );
settings.save();
dispatch('core/notices').createNotice(
'success',
__( 'Settings Saved', 'wholesome-plugin' ),
{
type: 'snackbar',
isDismissible: true,
}
);
}}
>
{ __( 'Save', 'wholesome-plugin') }
</Button>
</Panel>
</div>
<div className="wholesome-plugin__notices">
<Notices/>
</div>
</Fragment>) }
}
document.addEventListener( 'DOMContentLoaded', () => {
const htmlOutput = document.getElementById( 'wholesome-plugin-settings' );
if (htmlOutput) {
render(
<App />,
htmlOutput
);
}
});
Ajouter le SCSS
Il est temps de corriger ces styles. Ajoutez simplement ce qui suit dans le /src/admin.scssfichier (que vous auriez créé dans le guide Ajouter des points d’entrée au guide Créer un script de bloc.
#wholesome-plugin-settings {
.components-placeholder {
background: #f1f1f1;
}
.wholesome-plugin__header {
background-color: #ffffff;
box-shadow: 0 1px 0 rgba(213, 213, 213, .5), 0 1px 2px #eeeeee;
margin-left: -2em;
padding: 20px 10px;
.wholesome-plugin__container {
margin: 0 auto;
max-width: 750px;
.wholesome-plugin__title {
align-items: center;
display: flex;
justify-content: center;
.dashicon {
color: #757575;
}
}
}
}
.wholesome-plugin__main {
margin-left: auto;
margin-right: auto;
max-width: 750px;
.components-panel {
background: none;
border: none;
}
.components-panel__body {
background: #ffffff;
border: 1px solid #e2e4e7;
margin: 1rem 0;
}
}
.components-base-control__help {
margin-top: .5rem;
}
.components-panel__row {
> div {
flex-grow: 1;
margin-right: 1rem;
&:last-of-type {
margin-right: 0;
}
}
}
.wholesome-plugin__notices {
.components-snackbar {
bottom: .5rem;
position: fixed;
}
}
}
Affichage de la page des paramètres
Voici le résultat final :
La page des paramètres
Certains plugins ont un lien "paramètres" dans la page des plugins comme ceci :
Lien Paramètres sur le panneau de la page des paramètres du plugin
Pour ce faire, ajoutez le bloc de code suivant à la racine de votre fichier de plug-in (dans ce caswholesome-plugin.php :
function wholesomecode_wholesome_plugin_settings_link( $links ): array {
$label = esc_html__( 'Settings', 'wholesome-plugin' );
$slug = 'wholesome_plugin_settings';
array_unshift( $links, "<a href='options-general.php?page=$slug'>$label</a>" );
return $links;
}
add_action( 'plugin_action_links_'. plugin_basename( __FILE__ ), 'wholesomecode_wholesome_plugin_settings_link', 10 );
- Découvrez comment créer des blocs enfants imbriqués avec le
InnerBlockscomposant - Jetez un œil à l’utilisation des champs post-méta dans les blocs Gutenberg
- Jetez un œil à la création d’un panneau de personnalisation avec des composants Gutenberg