Amélioration de l'accessibilité
Dans le chapitre précédent, nous avons vu comment intercepter les erreurs (y compris les erreurs 404) et afficher un contenu de repli à l'utilisateur. Cependant, il nous reste à aborder une autre pièce du puzzle : la validation des formulaires. Voyons comment implémenter une validation côté serveur avec les Server Actions, et comment afficher les erreurs de formulaire en utilisant le hook useActionState
de React - tout en gardant l'accessibilité à l'esprit !
Qu'est-ce que l'accessibilité ?
L'accessibilité consiste à concevoir et implémenter des applications web utilisables par tous, y compris les personnes en situation de handicap. C'est un vaste sujet qui couvre de nombreux aspects comme la navigation au clavier, le HTML sémantique, les images, les couleurs, les vidéos, etc.
Bien que nous n'abordions pas en profondeur l'accessibilité dans ce cours, nous discuterons des fonctionnalités d'accessibilité disponibles dans Next.js et de quelques bonnes pratiques pour rendre vos applications plus accessibles.
Si vous souhaitez en savoir plus sur l'accessibilité, nous recommandons le cours Learn Accessibility de web.dev.
Utilisation du plugin ESLint pour l'accessibilité dans Next.js
Next.js inclut le plugin eslint-plugin-jsx-a11y
dans sa configuration ESLint pour aider à détecter précocement les problèmes d'accessibilité. Par exemple, ce plugin avertit si vous avez des images sans texte alternatif (alt
), si vous utilisez incorrectement les attributs aria-*
et role
, etc.
Optionnellement, si vous souhaitez essayer, ajoutez next lint
comme script dans votre fichier package.json
:
Puis exécutez pnpm lint
dans votre terminal :
Cela vous guidera pour installer et configurer ESLint dans votre projet. Si vous exécutez pnpm lint
maintenant, vous devriez voir le résultat suivant :
Mais que se passerait-il si vous aviez une image sans texte alternatif ? Découvrons-le !
Allez dans /app/ui/invoices/table.tsx
et supprimez la prop alt
de l'image. Vous pouvez utiliser la fonction de recherche de votre éditeur pour trouver rapidement le composant <Image>
:
Exécutez à nouveau pnpm lint
, et vous devriez voir l'avertissement suivant :
Bien que l'ajout et la configuration d'un linter ne soient pas obligatoires, cela peut être utile pour détecter les problèmes d'accessibilité pendant le développement.
Amélioration de l'accessibilité des formulaires
Nous faisons déjà trois choses pour améliorer l'accessibilité de nos formulaires :
- HTML sémantique : Utilisation d'éléments sémantiques (
<input>
,<option>
, etc.) plutôt que des<div>
. Cela permet aux technologies d'assistance (TA) de se concentrer sur les éléments de saisie et de fournir des informations contextuelles appropriées à l'utilisateur, rendant le formulaire plus facile à naviguer et à comprendre. - Étiquetage : L'inclusion de
<label>
et de l'attributhtmlFor
garantit que chaque champ de formulaire a une étiquette textuelle descriptive. Cela améliore la prise en charge par les TA en fournissant du contexte et améliore aussi l'ergonomie en permettant aux utilisateurs de cliquer sur l'étiquette pour accéder au champ correspondant. - Contour de focus : Les champs sont correctement stylisés pour afficher un contour lorsqu'ils sont en focus. Ceci est crucial pour l'accessibilité car cela indique visuellement l'élément actif sur la page, aidant les utilisateurs de clavier et de lecteurs d'écran à comprendre où ils se trouvent dans le formulaire. Vous pouvez le vérifier en appuyant sur
tab
.
Ces pratiques constituent une bonne base pour rendre vos formulaires plus accessibles à de nombreux utilisateurs. Cependant, elles ne traitent pas de la validation des formulaires et des erreurs.
Validation des formulaires
Allez sur http://localhost:3000/dashboard/invoices/create, et soumettez un formulaire vide. Que se passe-t-il ?
Vous obtenez une erreur ! Cela se produit parce que vous envoyez des valeurs vides à votre Server Action. Vous pouvez éviter cela en validant votre formulaire côté client ou côté serveur.
Validation côté client
Il existe plusieurs façons de valider les formulaires côté client. La plus simple serait d'utiliser la validation native du navigateur en ajoutant l'attribut required
aux éléments <input>
et <select>
de vos formulaires. Par exemple :
Soumettez à nouveau le formulaire. Le navigateur affichera un avertissement si vous essayez de soumettre un formulaire avec des valeurs vides.
Cette approche est généralement acceptable car certaines TA prennent en charge la validation du navigateur.
Une alternative à la validation côté client est la validation côté serveur. Voyons comment l'implémenter dans la section suivante. Pour l'instant, supprimez les attributs required
si vous les avez ajoutés.
Validation côté serveur (Server-Side validation)
En validant les formulaires côté serveur, vous pouvez :
- Garantir que vos données sont dans le format attendu avant de les envoyer à votre base de données.
- Réduire le risque que des utilisateurs malveillants contournent la validation côté client.
- Avoir une source unique de vérité pour ce qui est considéré comme des données valides.
Dans votre composant create-form.tsx
, importez le hook useActionState
depuis react
. Comme useActionState
est un hook, vous devrez transformer votre formulaire en Composant Client en utilisant la directive "use client"
:
Dans votre Composant Formulaire, le hook useActionState
:
- Prend deux arguments :
(action, initialState)
. - Retourne deux valeurs :
[state, formAction]
- l'état du formulaire et une fonction à appeler lors de la soumission du formulaire.
Passez votre action createInvoice
comme argument de useActionState
, et dans l'attribut <form action={}>
, appelez formAction
.
L'initialState
peut être n'importe quoi que vous définissez. Dans ce cas, créez un objet avec deux clés vides : message
et errors
, et importez le type State
depuis votre fichier actions.ts
. State
n'existe pas encore, mais nous le créerons ensuite :
Cela peut sembler confus au début, mais cela prendra plus de sens une fois que vous aurez mis à jour l'action serveur. Faisons cela maintenant.
Dans votre fichier action.ts
, vous pouvez utiliser Zod pour valider les données du formulaire. Mettez à jour votre FormSchema
comme suit :
customerId
- Zod génère déjà une erreur si le champ client est vide car il attend un typestring
. Mais ajoutons un message convivial si l'utilisateur ne sélectionne pas de client.amount
- Comme vous convertissez le typeamount
destring
ànumber
, il sera par défaut à zéro si la chaîne est vide. Disons à Zod que nous voulons toujours un montant supérieur à 0 avec la fonction.gt()
.status
- Zod génère déjà une erreur si le champ statut est vide car il attend "pending" ou "paid". Ajoutons également un message convivial si l'utilisateur ne sélectionne pas de statut.
Ensuite, mettez à jour votre action createInvoice
pour accepter deux paramètres - prevState
et formData
:
formData
- identique à avant.prevState
- contient l'état passé depuis le hookuseActionState
. Vous ne l'utiliserez pas dans l'action dans cet exemple, mais c'est une prop requise.
Puis, changez la fonction parse()
de Zod en safeParse()
:
safeParse()
retournera un objet contenant soit un champ success
soit un champ error
. Cela aidera à gérer la validation plus élégamment sans avoir à mettre cette logique dans un bloc try/catch
.
Avant d'envoyer les informations à votre base de données, vérifiez si les champs du formulaire ont été validés correctement avec une condition :
Si validatedFields
n'est pas un succès, nous retournons la fonction tôt avec les messages d'erreur de Zod.
Astuce : console.log
validatedFields
et soumettez un formulaire vide pour voir sa forme.
Enfin, comme vous gérez la validation du formulaire séparément, en dehors de votre bloc try/catch, vous pouvez retourner un message spécifique pour toute erreur de base de données. Votre code final devrait ressembler à ceci :
Parfait, maintenant affichons les erreurs dans votre composant formulaire. De retour dans le composant create-form.tsx
, vous pouvez accéder aux erreurs en utilisant l'état state
du formulaire.
Ajoutez un opérateur ternaire qui vérifie chaque erreur spécifique. Par exemple, après le champ client, vous pouvez ajouter :
Astuce : Vous pouvez console.log
state
dans votre composant et vérifier que tout est correctement connecté. Vérifiez la console dans les outils de développement car votre formulaire est maintenant un Composant Client.
Dans le code ci-dessus, vous ajoutez également les étiquettes aria suivantes :
aria-describedby="customer-error"
: Cela établit une relation entre l'élémentselect
et le conteneur des messages d'erreur. Il indique que le conteneur avecid="customer-error"
décrit l'élémentselect
. Les lecteurs d'écran liront cette description lorsque l'utilisateur interagit avec la boîteselect
pour les informer des erreurs.id="customer-error"
: Cet attributid
identifie de manière unique l'élément HTML qui contient le message d'erreur pour l'entréeselect
. Ceci est nécessaire pour quearia-describedby
établisse la relation.aria-live="polite"
: Le lecteur d'écran doit notifier poliment l'utilisateur lorsque l'erreur à l'intérieur dudiv
est mise à jour. Lorsque le contenu change (par exemple, lorsqu'un utilisateur corrige une erreur), le lecteur d'écran annoncera ces changements, mais seulement lorsque l'utilisateur est inactif pour ne pas l'interrompre.
Pratique : Ajout d'étiquettes aria
En utilisant l'exemple ci-dessus, ajoutez des erreurs à vos autres champs de formulaire. Vous devriez également afficher un message en bas du formulaire si des champs sont manquants. Votre interface devrait ressembler à ceci :

Une fois prêt, exécutez pnpm lint
pour vérifier si vous utilisez correctement les étiquettes aria.
Si vous souhaitez vous challenger, prenez les connaissances apprises dans ce chapitre et ajoutez la validation de formulaire au composant edit-form.tsx
.
Vous devrez :
- Ajouter
useActionState
à votre composantedit-form.tsx
. - Modifier l'action
updateInvoice
pour gérer les erreurs de validation de Zod. - Afficher les erreurs dans votre composant et ajouter des étiquettes aria pour améliorer l'accessibilité.
Une fois prêt, développez l'extrait de code ci-dessous pour voir la solution :