Published by

Il y a 6 mois -

Temps de lecture 26 minutes

Des interfaces sur mesure avec le CMS Contentful – épisode 2 : Réaliser sa propre UI Extension Contentful

Cet article est l’épisode 2 d’une série de trois articles traitant de la réalisation d’interface sur mesure avec le CMS Headless Contentful et ses UI Extensions.

Il est la suite de “Réaliser des interfaces sur mesure avec les UI Extensions du CMS Contentful“, un article où nous avons introduit le concept de CMS Headless et où nous nous sommes intéressés au CMS headless Contentful. Nous avons notamment introduit la notion d’UI Extension, une fonctionnalité proposée par Contentful, permettant de développer ses propres interfaces.

Dans cet article, nous allons passer de la théorie à la pratique et réaliser notre propre UI Extension.

Cette extension prendra la forme d’un starter, c’est-à-dire un projet simple, qui pourra servir de base pour réaliser une UI Extension plus complète. Nous implémenterons les fonctionnalités minimum : modifier la valeur et le style d’un field en utilisant l’UI Extension.

Ce ne sont pas les frameworks qui manquent lorsqu’il s’agit de réaliser une petite application. Ici, l’UI Extension sera réalisée avec ReactJS.

L’ensemble du code de ce starter est disponible ici :  https://github.com/paqueDev/contentful-extension-reactjs-starter

Au programme :

  • Mise en place de la structure minimale requise pour réaliser une extension
  • Ajout d’un linter et Babel à notre projet
  • Lancement de notre projet en local
  • Développement de l’extension

Prêt ? C’est parti !

 

Initialisation

Pour commencer nous allons créer un dossier, ma-super-extension (ou tout autre nom avec davantage d’inspiration) dans lequel nous allons réaliser l’extension.

Comme nous l’avons vu, les UI extensions requièrent une structure minimale avec les fichiers index.html et extension.json.

Nous commençons donc par créer le fichier extension.json à la racine du projet. Dans ce dernier, nous allons rapidement décrire notre extension.

extension.json

Ce fichier permet de décrire notre extension.

ma-super-extension/extension.json

[pastacode lang= »markup » manual= »%7B%0A%20%22id%22%3A%20%22ma-super-extension%22%2C%0A%20%22name%22%3A%20%22Ma%20Super%20Extension%22%2C%0A%20%22srcdoc%22%3A%20%22.%2Fbuild%2Findex.html%22%2C%0A%20%22sidebar%22%3A%20false%2C%0A%20%22fieldTypes%22%3A%20%5B%22Object%22%5D%0A%7D » message= » » highlight= » » provider= »manual »/]

Un rapide coup d’oeil sur les différentes propriétés:

  • id : id utilisé dans le processus de développement
  • name : le nom de l’extension
  • srcdoc : url vers l’index.html de l’extension, ce dernier correspond ici à l’index.html généré au build de notre application
  • sideBar : détermine l’emplacement de l’extension dans l’interface de Contentful, s’il est défini à true, l’extension sera affichée dans la barre latérale de l’interface Contentful
  • fieldTypes : liste des types de champs compatibles, ici un objet

 

index.html

Nous déclarons ensuite notre index.html et l’index.js associé dans un nouveau dossier src :

ma-super-extension/src/index.html

[pastacode lang= »markup » manual= »%3C!DOCTYPE%20html%3E%0A%3Chtml%3E%0A%20%20%20%3Chead%3E%0A%20%20%20%20%20%20%20%3Cmeta%20charset%3D%22utf-8%22%2F%3E%0A%20%20%20%3C%2Fhead%3E%0A%20%20%20%3Cbody%3E%0A%20%20%20%20%20%20%20%3Cdiv%20id%3D%22root%22%3E%3C%2Fdiv%3E%0A%20%20%20%20%20%20%20%3Cscript%20type%3D%22text%2Fjavascript%22%20src%3D%22.%2Findex.js%22%3E%3C%2Fscript%3E%0A%20%20%20%3C%2Fbody%3E%0A%3C%2Fhtml%3E » message= » » highlight= » » provider= »manual »/]

Nous allons avoir besoin d’ajouter les dépendances nécessaires à notre index.js :

[pastacode lang= »javascript » manual= »npm%20install%20react%20react-dom%20contentful-ui-extensions-sdk » message= » » highlight= » » provider= »manual »/]

C’est dans l’index.js que nous initialisons l’extension. Nous utilisons la méthode init() exposée par l’UI extensions SDK. C’est le point d’entrée de notre application.

ma-super-extension/src/index.js

[pastacode lang= »javascript » manual= »import%20React%20from%20’react’%3B%0Aimport%20ReactDOM%20from%20’react-dom’%3B%0Aimport%20App%20from%20′.%2FApp’%3B%0Aimport%20%7B%20init%20%7D%20from%20’contentful-ui-extensions-sdk’%3B%0A%0Ainit(sdk%20%3D%3E%20%7B%0A%20%20%20ReactDOM.render(%0A%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%2C%20document.getElementById(‘root’)%0A%20%20%20)%3B%0A%7D)%3B » message= » » highlight= » » provider= »manual »/]

Notre App est quant à elle définie dans un fichier App.js.

ma-super-extension/src/App.js

[pastacode lang= »javascript » manual= »import%20React%20from%20’react’%3B%0A%0Aconst%20App%20%3D%20(%7B%20sdk%20%20%7D)%20%3D%3E%20%7B%0A%20%20%20%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello!%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0A%0Aexport%20default%20App%3B » message= » » highlight= » » provider= »manual »/]

Nous avons maintenant la base minimale de notre extension.

Vite fait bien fait, linter + Babel

Nous allons également ajouter rapidement Babel et lb et un linter à notre projet.

Babel

Babel est un compilateur JavaScript permettant de convertir du code ECMAScript 2015 ou plus récent vers une version qui sera compatible avec des navigateurs plus ancien.

Il sert également à convertir la syntaxe JSX utilisée par React.

Nombre de plugins sont mis à disposition. Pour en savoir plus : https://babeljs.io/docs/

Les dépendances à installer :

[pastacode lang= »markup » manual= »npm%20install%20%40babel%2Fcore%20%40babel%2Fplugin-proposal-class-properties%20%40babel%2Fpreset-env%20%40babel%2Fpreset-react%20–save-dev » message= » » highlight= » » provider= »manual »/]

Le fichier de configuration à ajouter :

ma-super-extension/.babelrc

[pastacode lang= »javascript » manual= »%7B%0A%20%20%22presets%22%3A%20%5B%20%22%40babel%2Fpreset-env%22%2C%22%40babel%2Fpreset-react%22%5D%2C%0A%20%20%22plugins%22%3A%20%5B%22%40babel%2Fplugin-proposal-class-properties%22%5D%0A%7D » message= » » highlight= » » provider= »manual »/]

Linter 

Afin d’assurer la lisibilité et la maintenabilité du code au fil du développement, nous mettons en place un linter: ESLint.

Les dépendances à installer :

[pastacode lang= »markup » manual= »%20npm%20install%20eslint%20eslint-config-standard%20eslint-plugin-import%20eslint-plugin-node%20eslint-plugin-promise%20eslint-plugin-react%20eslint-plugin-standard%20%40babel%2Feslint-parser%20%20%40babel%2Feslint-plugin%20–save-dev » message= » » highlight= » » provider= »manual »/]

La configuration du linter est déclarée dans le fichier .eslintrc.js.

ma-super-extension/.eslintrc.js

[pastacode lang= »javascript » manual= »module.exports%20%3D%20%7B%0A%20%20%20%20extends%3A%20%5B%22standard%22%5D%2C%0A%20%20%20%20plugins%3A%20%5B%22standard%22%2C%20%22react%22%2C%20%22%40babel%22%5D%2C%0A%20%20%20%20rules%3A%20%7B%0A%20%20%20%20%20%20%20%20%22eqeqeq%22%20%3A%20%22warn%22%2C%0A%20%20%20%20%20%20%20%20%22no-var%22%3A%20%22error%22%2C%20%2F%2F%20optional%2C%20recommended%20when%20using%20es6%2B%0A%20%20%20%20%20%20%20%20%22no-unused-vars%22%3A%201%2C%20%2F%2F%20recommended%0A%20%20%20%20%20%20%20%20%22arrow-spacing%22%3A%20%5B%22error%22%2C%20%7B%20before%3A%20true%2C%20after%3A%20true%20%7D%5D%2C%20%2F%2F%20recommended%0A%20%20%20%20%20%20%20%20indent%3A%20%5B%22warn%22%2C%204%5D%2C%0A%20%20%20%20%20%20%20%20%22comma-dangle%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22error%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20objects%3A%20%22only-multiline%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20arrays%3A%20%22only-multiline%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20imports%3A%20%22never%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20exports%3A%20%22never%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20functions%3A%20%22never%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20options%20to%20emulate%20prettier%20setup%0A%20%20%20%20%20%20%20%20semi%3A%20%5B%22error%22%2C%20%22always%22%5D%2C%0A%20%20%20%20%20%20%20%20%22template-curly-spacing%22%3A%20%5B%22error%22%2C%20%22always%22%5D%2C%0A%20%20%20%20%20%20%20%20%22arrow-parens%22%3A%20%5B%22error%22%2C%20%22as-needed%22%5D%2C%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20standard.js%0A%20%20%20%20%20%20%20%20%22space-before-function-paren%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22error%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20named%3A%20%22always%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20anonymous%3A%20%22always%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20asyncArrow%3A%20%22always%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20standard%20plugin%20-%20options%0A%20%20%20%20%20%20%20%20%22standard%2Fobject-curly-even-spacing%22%3A%20%5B%22error%22%2C%20%22either%22%5D%2C%0A%20%20%20%20%20%20%20%20%22standard%2Farray-bracket-even-spacing%22%3A%20%5B%22error%22%2C%20%22either%22%5D%2C%0A%20%20%20%20%20%20%20%20%22standard%2Fcomputed-property-even-spacing%22%3A%20%5B%22error%22%2C%20%22even%22%5D%2C%0A%20%20%20%20%20%20%20%20%22standard%2Fno-callback-literal%22%3A%20%5B%22error%22%2C%20%5B%22cb%22%2C%20%22callback%22%5D%5D%2C%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20react%20plugin%20-%20options%0A%20%20%20%20%20%20%20%20%22react%2Fjsx-uses-react%22%3A%20%22error%22%2C%0A%20%20%20%20%20%20%20%20%22react%2Fjsx-uses-vars%22%3A%20%22error%22%2C%0A%20%20%20%20%20%20%20%20%22no-tabs%22%3A%200%0A%0A%20%20%20%20%7D%2C%0A%20%20%20%20parser%3A%20%22%40babel%2Feslint-parser%22%2C%0A%20%20%20%20parserOptions%3A%20%7B%0A%20%20%20%20%20%20%20%20babelOptions%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20plugins%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%40babel%2Fplugin-proposal-class-properties%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%7D%0A%7D%3B » message= » » highlight= » » provider= »manual »/]

Nous ajoutons un fichier .eslintignore pour éviter que le linter ne prenne en compte le dossier de build.

ma-super-extension/.eslintignore

[pastacode lang= »markup » manual= »build%2F*.js » message= » » highlight= » » provider= »manual »/]

et enfin nous ajoutons un script linter-fix dans le package.json. La commande npm run linter-fix permettra de corriger les problèmes qui peuvent l’être.

ma-super-extension/package.json

[pastacode lang= »markup » manual= »%20%22scripts%22%3A%20%7B%0A%20%20%20%22linter-fix%22%3A%20%22eslint%20–fix%20.%20–ext%20.js%2C.jsx%20src%22%2C%0A%20%7D » message= » » highlight= » » provider= »manual »/]

Il est temps de voir enfin le résultat s’afficher dans notre navigateur, non ?

Encore faut-il pouvoir lancer un build et faire tourner l’extension en local.

Et c’est justement la suite.

Lancer le projet en local

Avant de poursuivre, il y a quelques prérequis :

  • avoir créé un compte Contentful
  • avoir créé une organization et un space
  • avoir déclaré au moins un Content Model

Si ce n’est pas déjà fait, rendez-vous sur https://www.contentful.com/help/contentful-101/

En lançant notre projet en local dans Contentful, nous allons pouvoir suivre le développement de notre application au fur et à mesure, en hot reload.

Installons les dépendances nécessaires :

[pastacode lang= »markup » manual= »npm%20install%20contentful-cli%20%40contentful%2Fcontentful-extension-scripts%20%40contentful%2Fforma-36-tokens » message= » » highlight= » » provider= »manual »/]

Nous ajoutons dans notre package.json les différents scripts qui permettront de lancer, build, et déployer notre application, obtenant ainsi :

ma-super-extension/package.json

[pastacode lang= »markup » manual= »%7B%0A%20%22name%22%3A%20%22ma-super-extension%22%2C%0A%20%22version%22%3A%20%221.0.0%22%2C%0A%20%22private%22%3A%20false%2C%0A%20%22description%22%3A%20%22Une%20UI%20Extension%20en%20ReactJS%22%2C%0A%20%22devDependencies%22%3A%20%7B%0A%20%20%20%22%40babel%2Fcore%22%3A%20%227.3.4%22%2C%0A%20%20%20%22%40babel%2Fplugin-proposal-class-properties%22%3A%20%227.3.4%22%2C%0A%20%20%20%22%40babel%2Fplugin-transform-runtime%22%3A%20%227.3.4%22%2C%0A%20%20%20%22%40babel%2Fpreset-env%22%3A%20%227.3.4%22%2C%0A%20%20%20%22%40babel%2Fpreset-react%22%3A%20%227.0.0%22%2C%0A%20%20%20%22%40contentful%2Fcontentful-extension-scripts%22%3A%20%220.3.0%22%2C%0A%20%20%20%22babel-eslint%22%3A%20%2210.0.1%22%2C%0A%20%20%20%22babel-plugin-transform-es2015-modules-commonjs%22%3A%20%226.26.2%22%2C%0A%20%20%20%22babel-plugin-syntax-dynamic-import%22%3A%20%226.18.0%22%2C%0A%20%20%20%22contentful-cli%22%3A%20%220.20.0%22%2C%0A%20%20%20%22eslint%22%3A%20%225.16.0%22%2C%0A%20%20%20%22eslint-config-standard%22%3A%20%2212.0.0%22%2C%0A%20%20%20%22eslint-plugin-import%22%3A%20%222.17.2%22%2C%0A%20%20%20%22eslint-plugin-node%22%3A%20%228.0.1%22%2C%0A%20%20%20%22eslint-plugin-promise%22%3A%20%224.1.1%22%2C%0A%20%20%20%22eslint-plugin-react%22%3A%20%227.12.4%22%2C%0A%20%20%20%22eslint-plugin-standard%22%3A%20%224.0.0%22%0A%20%7D%2C%0A%20%22dependencies%22%3A%20%7B%0A%20%20%20%22%40contentful%2Fforma-36-tokens%22%3A%20%220.2.0%22%2C%0A%20%20%20%22contentful-ui-extensions-sdk%22%3A%20%223.5.0%22%2C%0A%20%20%20%22react%22%3A%20%2216.11.0%22%2C%0A%20%20%20%22react-dom%22%3A%20%2216.11.0%22%0A%20%7D%2C%0A%20%22scripts%22%3A%20%7B%0A%20%20%20%22prestart%22%3A%20%22contentful%20space%20use%20%26%26%20contentful%20extension%20update%20–src%20http%3A%2F%2Flocalhost%3A1234%20–force%22%2C%0A%20%20%20%22start%22%3A%20%22contentful-extension-scripts%20start%22%2C%0A%20%20%20%22build%22%3A%20%22contentful-extension-scripts%20build%22%2C%0A%20%20%20%22deploy%22%3A%20%22npm%20run%20build%20%26%26%20contentful%20space%20use%20%26%26%20contentful%20extension%20update%20–force%22%2C%0Ainter-fix%22%3A%20%22eslint%20–fix%20.%20–ext%20.js%2C.jsx%20src%22%2C%0A%20%20%20%22l%20%20%20%22linter-fix%22%3A%20%22eslint%20–fix%20.%20–ext%20.js%2C.jsx%20src%22%2C%0A%20%20%20%22login%22%3A%20%22contentful%20login%22%2C%0A%20%20%20%22logout%22%3A%20%22contentful%20logout%22%2C%0A%20%20%20%22help%22%3A%20%22contentful-extension-scripts%20help%22%0A%20%20%20%0A%20%7D%2C%0A%20%22browserslist%22%3A%20%5B%0A%20%20%20%22last%205%20Chrome%20version%22%2C%0A%20%20%20%22%3E%201%25%22%2C%0A%20%20%20%22not%20ie%20%3C%3D%2011%22%0A%20%5D%0A%7D » message= » » highlight= » » provider= »manual »/]

A ce stade, lorsque nous lançons la commande npm run start, un message d’erreur s’affiche dans notre terminal.

Contentful nous demande de nous authentifier.

Il existe plusieurs façon de se logger :

  • Soit en utilisant contentful-cli via la commande contentful login : vous trouverez la documentation de cette méthode d’authentification ici.
  • Soit en ajoutant à la racine de notre projet un fichier .contentfulrc.json

ma-super-extension/.contentfulrc.json

[pastacode lang= »markup » manual= »%7B%0A%20%22cmaToken%22%3A%20%22%3Cyour-content-management-api-token%3E%22%2C%0A%20%22activeSpaceId%22%3A%20%22%3Cyou-space-id%3E%22%2C%0A%20%22activeEnvironmentId%22%3A%20%22master%22%0A%7D » message= » » highlight= » » provider= »manual »/]

Pour obtenir votre content management token,  allez dans Settings > API Keys. Cliquez sur Content management tokens, puis Generate Personal Token.

Pour obtenir votre space ID,  aller dans Settings > API Keys, sélectionnez une API Key dans laquelle vous trouverez votre space ID.

Attention à bien spécifier .contentfulrc dans votre .gitignore afin de ne pas l’héberger dans votre repository et rendre alors accessibles vos clés d’API. 

Nous sommes désormais connectés !

Lorsque nous lançons la commande npm run start, nous avons maintenant dans notre terminal :

Ça tourne !

Par contre lorsque nous ouvrons notre navigateur pour voir notre localhost:1234, le résultat n’est pas très satisfaisant.

Et oui, rien n’apparaît. Notre extension est faite pour tourner dans une iframe.

Nous allons donc visualiser notre application directement depuis Contentful.

Oui, nous y sommes presque !

 

Allez dans Settings > Extensions.

C’est ici que sont listées les extensions et, après avoir lancé notre extension en local, celle-ci apparaît déjà. Elle a été ajoutée automatiquement dans la liste après le lancement du script start.

Si elle n’apparaît pas, nous pouvons également l’ajouter manuellement via le bouton Add extension.

Il ne reste plus qu’à utiliser cette extension dans le field d’un content model (Ici le nom du content model sera simplement “Test”) :

  • Ajoutez un field de type JSON Object,
  • Nommez-le (ici, nous le nommerons “custom” ) et aller dans Appearance
  • Sélectionnez l’extension

Une fois un content créé, vous pourrez voir les différents champs définis depuis le content model et… notre extension!

Nous pouvons désormais visualiser notre extension en local et la voir évoluer lors du développement.

Développement de l’application

L’objectif  de cette extension est de pouvoir attribuer une valeur au field via une interface que nous concevons nous même.

Dans cette partie nous allons  donc :

  • modifier la valeur du field
  • récupérer la valeur du field lorsque l’on ouvre le content et initialiser l’extension avec cette valeur
  • styliser un minimum notre interface

Bien que pour cet exemple nous ne travaillons que sur une valeur du type object très simple, nous mettrons en place Redux, en prévision d’une évolution de l’extension en une application plus complexe.

Commençons par modifier la valeur de notre field.

Nous partirons sur le principe que nous souhaitons enregistrer un objet simple, comprenant une key “message” qui contiendra une chaîne de caractères. Par exemple :

[pastacode lang= »markup » manual= »%7B%0A%20%20%20message%3A%20’Vous%20ne%20pouvez%20pas%20comprendre%20la%20r%C3%A9cursivit%C3%A9%20sans%20avoir%20d%E2%80%99abord%20compris%20la%20r%C3%A9cursivit%C3%A9’%0A%7D » message= » » highlight= » » provider= »manual »/]

Comme dit précédemment, nous allons utiliser Redux.

Nous ne décrirons pas ici le fonctionnement de Redux. Si vous souhaitez en savoir davantage sur ce dernier, rendez-vous sur la documentation officielle : https://redux.js.org/.

Nous installons les dépendances :

[pastacode lang= »markup » manual= »npm%20install%20redux%20react-redux%20redux-logger » message= » » highlight= » » provider= »manual »/]

puis déclarons et initialisons le store :

ma-super-extension/src/index.js

[pastacode lang= »javascript » manual= »import%20React%20from%20’react’%3B%0Aimport%20ReactDOM%20from%20’react-dom’%3B%0Aimport%20%7B%20createStore%2C%20applyMiddleware%20%7D%20from%20’redux’%3B%0Aimport%20%7B%20Provider%20%7D%20from%20’react-redux’%3B%0Aimport%20rootReducer%20from%20′.%2Freducers’%3B%0Aimport%20logger%20from%20’redux-logger’%3B%0A%0Aimport%20%7B%20init%20%7D%20from%20’contentful-ui-extensions-sdk’%3B%0A%0Aimport%20App%20from%20′.%2FApp’%3B%0A%0Aconst%20initialState%20%3D%20%7B%0A%20%20%20fieldValue%3A%20%7B%0A%20%20%20%20%20%20%20message%3A%20 »%0A%20%20%20%7D%0A%7D%3B%0A%0Aconst%20store%20%3D%20createStore(rootReducer%2C%20initialState%2C%20applyMiddleware(logger))%3B%0A%0Ainit(sdk%20%3D%3E%20%7B%0A%20%20%20ReactDOM.render(%0A%20%20%20%20%20%20%20%3CProvider%20store%3D%7Bstore%7D%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CApp%20sdk%3D%7Bsdk%7D%2F%3E%0A%20%20%20%20%20%20%20%3C%2FProvider%3E%2C%0A%20%20%20%20%20%20%20document.getElementById(‘root’)%0A%20%20%20)%3B%0A%7D)%3B » message= » » highlight= » » provider= »manual »/]

Nous initialisons le store avec un initialState dont la structure correspond à celle que nous souhaitons attribuer à notre field.

Nous rendons le store disponible via le <Provider> pour tous les composants imbriqués qui seront encapsulés dans la fonction connect().

Afin de pouvoir tracer les actions dans la console, nous utilisons redux-logger.

Nous allons maintenant créer le champ par lequel nous modifierons la valeur du store.

Nous créons un nouveau composant EditMessage.

ma-super-extension/src/components/EditMessage/index.js

[pastacode lang= »javascript » manual= »import%20React%20from%20’react’%3B%0Aimport%20PropTypes%20from%20’prop-types’%3B%0A%0Aconst%20EditMessage%20%3D%20(%7B%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Clabel%3EUpdate%20Message%3C%2Flabel%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0AEditMessage.propTypes%20%3D%20%7B%7D%3B%0A%0Aexport%20default%20EditMessage%3B » message= » » highlight= » » provider= »manual »/]

Dans ce dernier, nous utilisons prop-types pour documenter les types des propriétés passées au composant.

Pour ajouter prop-type aux dépendances :

[pastacode lang= »markup » manual= »npm%20install%20prop-types » message= » » highlight= » » provider= »manual »/]

Nous connectons notre composant au store et récupérons la valeur message de celui-ci  que nous passons en propriété à notre composant.

ma-super-extension/src/components/EditMessage/index.js

[pastacode lang= »markup » manual= »import%20React%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20PropTypes%20from%20’prop-types’%3B%0A%0Aconst%20EditMessage%20%3D%20(%7B%20storedMessage%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Clabel%3EUpdate%20Message%3C%2Flabel%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0AEditMessage.propTypes%20%3D%20%7B%0A%20%20%20storedMessage%3A%20PropTypes.string%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20storedMessage%3A%20fieldValue.message%20%7C%7C%20null%0A%7D)%3B%0A%0Aexport%20default%20connect(mapStateToProps)(EditMessage)%3B » message= » » highlight= » » provider= »manual »/]

 

Nous créons ensuite un textarea. Celui-ci affichera par défaut la valeur de message récupéré depuis le store.

ma-super-extension/src/components/EditMessage/index.js

[pastacode lang= »markup » manual= »import%20React%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20PropTypes%20from%20’prop-types’%3B%0A%0Aconst%20EditMessage%20%3D%20(%7B%20storedMessage%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Clabel%3EUpdate%20Message%3C%2Flabel%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ctextarea%20type%3D%7B’text’%7D%20%20defaultValue%3D%7BstoredMessage%20%7C%7C%20 »%7D%20%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0AEditMessage.propTypes%20%3D%20%7B%0A%20%20%20storedMessage%3A%20PropTypes.string%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20storedMessage%3A%20fieldValue.message%20%7C%7C%20null%0A%7D)%3B%0A%0Aexport%20default%20connect(mapStateToProps)(EditMessage)%3B » message= » » highlight= » » provider= »manual »/]

 

Nous importons notre nouveau composant dans App.

ma-super-extension/src/App.js

[pastacode lang= »markup » manual= »import%20React%20%20from%20’react’%3B%0Aimport%20EditMessage%20from%20′.%2Fcomponents%2FEditMessage’%3B%0A%0Aconst%20App%20%3D%20(%7B%20sdk%20%7D)%20%3D%3E%20%7B%0A%20%20%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CEditMessage%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0Aexport%20default%20App%3B » message= » » highlight= » » provider= »manual »/]

Voyons le résultat :

Pour rendre le résultat un peu plus propre visuellement, nous allons styliser l’ensemble à l’aide de styled-components.

Si vous ne connaissez pas les styled-components et/ou souhaitez vous documenter, le lien vers la documentation est ici.

Nous ajoutons les dépendances nécessaires :

[pastacode lang= »markup » manual= »npm%20install%20styled-components » message= » » highlight= » » provider= »manual »/]

Nous créons au même niveau que l’index.js de notre composant un fichier styled.js, puis y définissons un styled-component Container

ma-super-extension/src/components/EditMessage/styled.js

[pastacode lang= »javascript » manual= »import%20styled%20from%20’styled-components’%3B%0A%0Aexport%20const%20Container%20%3D%20styled.div%60%0A%20display%20%3A%20flex%3B%0A%20flex-direction%20%3A%20column%3B%20%0A%0A%20%26%20label%20%7B%0A%20%20%20margin%20%3A%2010px%200%3B%0A%20%20%20line-height%20%3A%2015px%3B%0A%20%20%20font-size%3A13px%3B%0A%20%20%20font-weight%20%3A%20400%3B%0A%20%7D%0A%0A%20%26%20textarea%7B%0A%20%20%20min-height%20%3A%20100px%3B%0A%20%7D%0A%60%3B » message= » » highlight= » » provider= »manual »/]

et utilisons le styled-component Container dans le composant EditMessage :

ma-super-extension/src/components/EditMessage/index.js

[pastacode lang= »markup » manual= »import%20React%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20PropTypes%20from%20’prop-types’%3B%0Aimport%20%7B%20Container%20%7D%20from%20′.%2Fstyled’%3B%0Aimport%20%7B%20updateMessage%20%7D%20from%20′..%2F..%2Factions%2Findex’%3B%0A%0Aconst%20EditMessage%20%3D%20(%7B%20storedMessage%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3CContainer%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Clabel%3EUpdate%20Message%3C%2Flabel%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ctextarea%20type%3D%7B’text’%7D%20%20defaultValue%3D%7BstoredMessage%20%7C%7C%20 »%7D%20%2F%3E%0A%20%20%20%20%20%20%20%3C%2FContainer%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0AEditMessage.propTypes%20%3D%20%7B%0A%20%20%20storedMessage%3A%20PropTypes.string%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20storedMessage%3A%20fieldValue.message%20%7C%7C%20null%0A%7D)%3B%0A%0Aexport%20default%20connect(mapStateToProps)(EditMessage)%3B » message= » » highlight= » » provider= »manual »/]

Nous obtenons :

Penchons-nous maintenant sur la modification du store au changement de valeur dans notre textarea.

À l’évènement onChange, nous allons envoyer au store l’action dispatch à laquelle nous passons en paramètre la valeur courante du textarea.

ma-super-extension/src/components/EditMessage/index.js

[pastacode lang= »markup » manual= »import%20React%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20PropTypes%20from%20’prop-types’%3B%0Aimport%20%7B%20Container%20%7D%20from%20′.%2Fstyled’%3B%0Aimport%20%7B%20updateMessage%20%7D%20from%20′..%2F..%2Factions%2Findex’%3B%0A%0Aconst%20EditMessage%20%3D%20(%7B%20dispatch%2C%20storedMessage%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3CContainer%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Clabel%3EUpdate%20Message%3C%2Flabel%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ctextarea%20type%3D%7B’text’%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20defaultValue%3D%7BstoredMessage%20%7C%7C%20 »%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20onChange%3D%7Be%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dispatch(updateMessage(e.target.value))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%7D%2F%3E%0A%20%20%20%20%20%20%20%3C%2FContainer%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0AEditMessage.propTypes%20%3D%20%7B%0A%20%20%20storedMessage%3A%20PropTypes.string%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20storedMessage%3A%20fieldValue.message%20%7C%7C%20null%0A%7D)%3B%0A%0Aexport%20default%20connect(mapStateToProps)(EditMessage)%3B » message= » » highlight= » » provider= »manual »/]

 

Nous décrivons notre action dans un dossier ./src/actions.

Considérant que notre store sera amené à évoluer et devenir plus complexe, nous créons un fichier dédié aux actions qui modifieront fieldValue dans notre store : .src/actions/fieldValue.js.

Nous y déclarons notre action updateMessage, avec un payload contenant la valeur courante du textarea.

Nous avons donc dans notre dossiers ./src/actions deux fichiers :

ma-super-extension/src/actions/fieldValue.js

[pastacode lang= »javascript » manual= »export%20const%20updateMessage%20%3D%20value%20%3D%3E%20(%7B%0A%20%20%20type%3A%20’UPDATE_MESSAGE’%2C%0A%20%20%20payload%3A%20%7B%0A%20%20%20%20%20%20%20value%3A%20value%0A%20%20%20%7D%0A%7D)%3B » message= » » highlight= » » provider= »manual »/]

et

ma-super-extension/src/actions/index.js

[pastacode lang= »javascript » manual= »export%20*%20from%20′.%2FfieldValue’%3B » message= » » highlight= » » provider= »manual »/]

Intéressons nous maintenant au reducer correspondant.

En suivant la même structure que pour les actions, nous avons ainsi un dossier ./src/reducers dans lequel se trouve deux fichiers : index.js et fieldValue.js.

ma-super-extension/src/reducers/index.js

[pastacode lang= »javascript » manual= »import%20%7B%20combineReducers%20%7D%20from%20’redux’%3B%0Aimport%20fieldValue%20from%20′.%2FfieldValue’%3B%0A%0Aexport%20default%20combineReducers(%7B%0A%20%20%20fieldValue%0A%7D)%3B » message= » » highlight= » » provider= »manual »/]

ma-super-extension/src/reducers/fieldValue.js

[pastacode lang= »javascript » manual= »const%20fieldValue%20%3D%20(state%20%3D%20%5B%5D%2C%20action)%20%3D%3E%20%7B%0A%20%20%20switch%20(action.type)%20%7B%0A%0A%20%20%20%20case%20’UPDATE_MESSAGE’%20%3A%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20…state%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20message%3A%20action.payload.value%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%0A%20%20%20%20default%3A%0A%20%20%20%20%20%20%20%20return%20state%3B%0A%20%20%20%20%7D%0A%20%20%7D%3B%0A%0Aexport%20default%20fieldValue%3B » message= » » highlight= » » provider= »manual »/]

Dans le cas de ‘UPDATE_MESSAGE’, nous modifions le state, attribuant à message sa nouvelle valeur.

Désormais, lorsque nous écrivons dans notre textarea, le store est modifié et nous voyons apparaître nos actions dans la console.

La dernière étape est d’attribuer cette nouvelle valeur au field Contentful. Pour cela nous allons utiliser la fonction setValue() fournie par l’UI extensions SDK.

Pour en savoir plus, vous pouvez aller voit la documentation du SDK.

Nous allons réaliser cette étape dans le fichier App.js.

Nous connectons App au store afin de récupérer la valeur de fieldValue.

ma-super-extension/src/App.js

[pastacode lang= »markup » manual= »import%20React%20%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20EditMessage%20from%20′.%2Fcomponents%2FEditMessage’%3B%0A%0Aconst%20App%20%3D%20(%7B%20sdk%2C%20fieldValue%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CEditMessage%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20fieldValue%3A%20fieldValue%2C%0A%7D)%3B%0A%0Aexport%20default%20connect(mapStateToProps)(App)%3B » message= » » highlight= » » provider= »manual »/]

 

A l’aide d’un hook useEffect, nous allons surveiller la valeur de fieldValue.

ma-super-extension/src/App.js

[pastacode lang= »markup » manual= »import%20React%20%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20EditMessage%20from%20′.%2Fcomponents%2FEditMessage’%3B%0A%0Aconst%20App%20%3D%20(%7B%20sdk%2C%20fieldValue%20%7D)%20%3D%3E%20%7B%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CEditMessage%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20fieldValue%3A%20fieldValue%2C%0A%7D)%3B%0A%0Aexport%20default%20connect(mapStateToProps)(App)%3B » message= » » highlight= » » provider= »manual »/]

 

Si la valeur de fieldValue change, alors nous attribuons sa nouvelle valeur au field Contentful. Pour cela nous définissons une méthode setFieldValue que nous appelons dans le hook.

ma-super-extension/src/App.js

[pastacode lang= »markup » manual= »import%20React%2C%20%7B%20useEffect%20%7D%20from%20’react’%3B%0Aimport%20%7B%20connect%20%7D%20from%20’react-redux’%3B%0Aimport%20isEqual%20from%20’lodash%2FisEqual’%3B%0Aimport%20%7B%20initFieldValue%20%7D%20from%20′..%2Factions’%3B%0Aimport%20EditMessage%20from%20′.%2Fcomponents%2FEditMessage’%3B%0A%0Aconst%20App%20%3D%20(%7B%20fieldValue%2C%20sdk%2C%20dispatch%20%7D)%20%3D%3E%20%7B%0A%0A%20%20%20useEffect(()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20setFieldValue()%3B%0A%20%20%20%7D%2C%20%5BfieldValue%5D)%3B%0A%0A%20%20%20const%20setFieldValue%20%3D%20()%20%3D%3E%20sdk.field.setValue(fieldValue)%3B%0A%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CEditMessage%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7B%20fieldValue%20%7D)%20%3D%3E%20(%7B%0A%20%20%20fieldValue%3A%20fieldValue%2C%0A%7D)%3B%0Aexport%20default%20connect(mapStateToProps)(App)%3B » message= » » highlight= » » provider= »manual »/]

 

Ça y est ! Lorsque nous modifions la valeur de notre textarea, la valeur du field Contentful est modifiée et nous voyons le bouton “current status” de notre content passer en “publish changes”. Notre modification a bien été prise en compte,  elle est en “draft”.

Il reste encore quelque chose à régler : lorsque nous rafraîchissons notre page, ou que nous quittons notre content pour ensuite y revenir, nous ne voyons plus notre nouveau message.

Il faut en effet initialiser notre store avec la valeur du field Contentful.

Pour cela nous ajoutons un nouveau hook qui se déclenchera une fois au démarrage.

Dans ce hook, si le field Contentful possède déjà une valeur message alors nous appelons l’action initFieldValue. Sinon nous attribuons au field la valeur fieldValue du state initial de notre store.

ma-super-extension/src/container/App.js

[pastacode lang= »markup » manual= »import%20React%2C%20%7BuseEffect%7D%20from%20’react’%3B%0Aimport%20%7Bconnect%7D%20from%20’react-redux’%3B%0Aimport%20%7BinitFieldValue%7D%20from%20′..%2Factions’%3B%0Aimport%20EditMessage%20from%20′.%2Fcomponents%2FEditMessage’%3B%0A%0Aconst%20App%20%3D%20(%7BfieldValue%2C%20sdk%2C%20dispatch%7D)%20%3D%3E%20%7B%0A%20%20%20useEffect(()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20if%20(sdk.field.getValue()%20%26%26%20sdk.field.getValue().message%20!%3D%3D%20undefined)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20dispatch(initFieldValue(sdk.field.getValue()))%3B%0A%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20setFieldValue()%3B%0A%20%20%20%20%20%20%20%7D%0A%20%20%20%7D%2C%20%5B%5D)%3B%0A%0A%20%20%20useEffect(()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20setFieldValue()%3B%0A%20%20%20%7D%2C%20%5BfieldValue%5D)%3B%0A%0A%20%20%20const%20setFieldValue%20%3D%20()%20%3D%3E%20sdk.field.setValue(fieldValue)%3B%0A%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CEditMessage%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7BfieldValue%7D)%20%3D%3E%20(%7B%0A%20%20%20fieldValue%3A%20fieldValue%2C%0A%7D)%3B%0Aexport%20default%20connect(mapStateToProps)(App)%3B » message= » » highlight= » » provider= »manual »/]

Nous ajoutons une nouvelle action initFieldValue :

ma-super-extension/src/actions/fieldValue.js

[pastacode lang= »javascript » manual= »export%20const%20initFieldValue%20%3D%20object%20%3D%3E%20(%7B%0A%20%20%20type%3A%20’INIT_FIELD_VALUE’%2C%0A%20%20%20payload%3A%20%7B%0A%20%20%20%20%20%20%20value%3A%20object%0A%20%20%20%7D%0A%7D)%3B%0A%0Aexport%20const%20updateMessage%20%3D%20value%20%3D%3E%20(%7B%0A%20%20%20type%3A%20’UPDATE_MESSAGE’%2C%0A%20%20%20payload%3A%20%7B%0A%20%20%20%20%20%20%20value%3A%20value%0A%20%20%20%7D%0A%7D)%3B » message= » » highlight= » » provider= »manual »/]

et le reducer associé INIT_FIELD_VALUE :

ma-super-extension/src/reducers/fieldValue.js

[pastacode lang= »javascript » manual= »const%20fieldValue%20%3D%20(state%20%3D%20%5B%5D%2C%20action)%20%3D%3E%20%7B%0A%20%20%20switch%20(action.type)%20%7B%0A%20%20%20case%20’INIT_FIELD_VALUE’%20%3A%0A%20%20%20%20%20%20%20return%20action.payload.value%3B%0A%0A%20%20%20case%20’UPDATE_MESSAGE’%20%3A%0A%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20…state%2C%0A%20%20%20%20%20%20%20%20%20%20%20message%3A%20action.payload.value%0A%20%20%20%20%20%20%20%7D%3B%0A%0A%20%20%20default%3A%0A%20%20%20%20%20%20%20return%20state%3B%0A%20%20%20%7D%0A%7D%3B%0A%0Aexport%20default%20fieldValue%3B » message= » » highlight= » » provider= »manual »/]

Et voilà !

Un dernier petit détail supplémentaire pour le confort :

Nous utilisons dans le hook appelé au rendu de notre App la méthode window.startAutoResizer fournie par le SDK.

ma-super-extension/src/App.js

[pastacode lang= »markup » manual= »import%20React%2C%20%7BuseEffect%7D%20from%20’react’%3B%0Aimport%20%7Bconnect%7D%20from%20’react-redux’%3B%0Aimport%20isEqual%20from%20’lodash%2FisEqual’%3B%0Aimport%20%7BinitFieldValue%7D%20from%20′..%2Factions’%3B%0Aimport%20EditMessage%20from%20′.%2Fcomponents%2FEditMessage’%3B%0A%0Aconst%20App%20%3D%20(%7BfieldValue%2C%20sdk%2C%20dispatch%7D)%20%3D%3E%20%7B%0A%20%20%20useEffect(()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20if%20(sdk.field.getValue()%20%26%26%20sdk.field.getValue()%20!%3D%3D%20%7B%7D)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20dispatch(initFieldValue(sdk.field.getValue()))%3B%0A%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20setFieldValue()%3B%0A%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20sdk.window.startAutoResizer()%3B%0A%0A%20%20%20%20%20%20%20return%20()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20sdk.window.stopAutoResizer()%3B%0A%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%7D%2C%20%5B%5D)%3B%0A%0A%20%20%20useEffect(()%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20setFieldValue()%3B%0A%20%20%20%7D%2C%20%5BfieldValue%5D)%3B%0A%0A%20%20%20const%20setFieldValue%20%3D%20()%20%3D%3E%20sdk.field.setValue(fieldValue)%3B%0A%0A%20%20%20return%20(%0A%20%20%20%20%20%20%20%3Cdiv%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3Ch1%3EHello%3C%2Fh1%3E%0A%20%20%20%20%20%20%20%20%20%20%20%3CEditMessage%2F%3E%0A%20%20%20%20%20%20%20%3C%2Fdiv%3E%0A%20%20%20)%3B%0A%7D%3B%0A%0Aconst%20mapStateToProps%20%3D%20(%7BfieldValue%7D)%20%3D%3E%20(%7B%0A%20%20%20fieldValue%3A%20fieldValue%2C%0A%7D)%3B%0Aexport%20default%20connect(mapStateToProps)(App)%3B » message= » » highlight= » » provider= »manual »/]

sdk.window.startAutoResizer() permet de mettre à jour automatiquement la hauteur de l’extension. Sans cela, si la height du DOM de l’application devient plus grande, l’iframe sera visuellement coupé et il faudra scroller pour voir le reste de l’application dépassant de la hauteur initiale :

Avec l’autoResizer, l’iframe s’adapte automatiquement.

Ça y est !

Vous avez une UI Extension en React !

Tips  : 

Contentful met également à disposition un design System, Forma 36, utilisable dans les  extensions. Dans la continuité de l’application en ReactJS que nous avons réalisée précédemment, nous pourrions utiliser ces composants avec la bibliothèque de composants React @contentful/forma-36-react-components.

 

Si nous avons la satisfaction d’avoir désormais notre propre UI Extension, cela nous amène naturellement une autre question : jusqu’où peut-on aller ?

C’est le sujet du 3ème et dernier article de cette série : “Une interface sur mesure avec les UI Extensions du CMS Contentful, REX“, où nous verrons jusqu’où peuvent nous mener ces UI Extensions à travers un retour d’expérience : une UI Extension dédiée à notre chargé de SEO, avec une interface adaptée à ses besoins.

 

Ressources :

Published by

Commentaire

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.