Commencer notre liste de tâches en React
========================================

* Précédent
* Aperçu : Client-side JavaScript frameworks
* Suivant

Disons que nous avons pour mission de créer une preuve de concept en React : une application qui permette d'ajouter, de modifier et de supprimer des tâches sur lesquelles on veut travailler, et de les marquer comme terminées sans les supprimer. Dans cet article, nous verrons comment mettre en place la structure de base et la mise en forme du composant `App` initial et qui servira de socle pour définir d'autres composants et implémenter l'interactivité par la suite.

**Note :** Si vous avez besoin de vérifier votre code par rapport à notre version, vous pouvez trouver une version terminée du code de cette application React d'exemple dans notre dépôt `todo-react`. Pour voir l'application fonctionner en direct, consultez https://mdn.github.io/todo-react-build/.


|  |  |
| --- | --- |
| Prérequis : | Connaissances des fondamentaux en HTML, CSS, et JavaScript, connaissance du terminal/de la ligne de commande. |
| Objectifs : | Présenter notre étude de cas avec une application de liste de tâches à faire (*todo list*) et mettre en place la structure et la mise en forme de base pour le composant général `App`. |

Les récits d'utilisation de notre application
---------------------------------------------

Dans le développement logiciel, un récit utilisateur (ou *user story* en anglais) est un objectif réalisable du point de vue de la personne utilisant l'application. Définir ces scénarios avant de commencer notre travail nous aidera à orienter correctement notre travail. Notre application devrait ici respecter les spécifications suivantes :

En tant qu'utilisatrice ou utilisateur, je peux :

* Lire une liste de tâches.
* Ajouter une tâche en utilisant la souris ou le clavier.
* Marquer une tâche comme étant terminée, en utilisant la souris ou le clavier.
* Supprimer n'importe quelle tâche, en utilisant la souris ou le clavier.
* Modifier n'importe quelle tâche, en utilisant la souris ou le clavier.
* Afficher un sous-ensemble spécifique de tâches :
    + Toutes les tâches
    + Seulement la tâche active
    + Seulement les tâches terminées.

Nous aborderons ces spécifications une par une.

Petit ménage avant de commencer
-------------------------------

`create-react-app` a créé quelques fichiers que nous n'utiliserons pas du tout pour notre projet.

* Nous ne créerons pas de feuilles de style pour chaque composant, vous pouvez donc commencer par supprimer l'importation de `App.css` en haut de `App.js`.
* Nous n'utiliserons pas non plus le fichier `logo.svg`, vous pouvez également supprimer cette importation.

Ensuite, copiez et collez les commandes suivantes dans votre terminal pour supprimer certains fichiers inutiles. Assurez-vous de démarrer dans le répertoire racine de l'application !


```bash
# Déplacez-vous dans le répertoire src de votre projet
cd src
# Supprimez les fichiers suivants
rm -- App.test.js App.css logo.svg serviceWorker.js setupTests.js
# Revenez dans le répertoire racine de votre projet
cd ..
```


**Note :** Deux des fichiers que nous avons supprimés sont destinés à tester l'application. Nous ne ferons pas de tests ici.

**Note :** Si vous avez arrêté votre serveur d'application afin d'effectuer les actions précédentes dans votre terminal, vous devrez redémarrer le serveur en utilisant `npm start`.

Code de démarrage pour notre projet
-----------------------------------

Pour démarrer ce projet, nous allons vous fournir deux choses :

* Une fonction `App()` pour remplacer celle que vous avez actuellement
* Un peu de CSS pour mettre en forme votre application.
### Le code JSX

Copiez le fragment de code suivant, puis collez-le dans `App.js` afin qu'il remplace la fonction `App()` existante :


```jsx
function App(props) {
  return (
    <div className="todoapp stack-large">
 <h1>TodoMatic</h1>
 <form>
 <h2 className="label-wrapper">
 <label htmlFor="new-todo-input" className="label\_\_lg">
 Qu'y a-t-il à faire&nbsp;?
 </label>
 </h2>
 <input
 type="text"
 id="new-todo-input"
 className="input input\_\_lg"
 name="text"
 autoComplete="off"
 />
 <button type="submit" className="btn btn\_\_primary btn\_\_lg">
 Ajouter
 </button>
 </form>
 <div className="filters btn-group stack-exception">
 <button type="button" className="btn toggle-btn" aria-pressed="true">
 <span className="visually-hidden">Montrer </span>
 <span>Toutes</span>
 <span className="visually-hidden"> les tâches</span>
 </button>
 <button type="button" className="btn toggle-btn" aria-pressed="false">
 <span className="visually-hidden">Montrer </span>
 <span className="visually-hidden">les tâches </span>
 <span>Actives</span>
 </button>
 <button type="button" className="btn toggle-btn" aria-pressed="false">
 <span className="visually-hidden">Montrer </span>
 <span className="visually-hidden">les tâches </span>
 <span>Terminées</span>
 </button>
 </div>
 <h2 id="list-heading">3 tâches restantes</h2>
 <ul
 role="list"
 className="todo-list stack-large stack-exception"
 aria-labelledby="list-heading">
 <li className="todo stack-small">
 <div className="c-cb">
 <input id="todo-0" type="checkbox" defaultChecked={true} />
 <label className="todo-label" htmlFor="todo-0">
 Manger
 </label>
 </div>
 <div className="btn-group">
 <button type="button" className="btn">
 Éditer <span className="visually-hidden">Manger</span>
 </button>
 <button type="button" className="btn btn\_\_danger">
 Supprimer <span className="visually-hidden">Manger</span>
 </button>
 </div>
 </li>
 <li className="todo stack-small">
 <div className="c-cb">
 <input id="todo-1" type="checkbox" />
 <label className="todo-label" htmlFor="todo-1">
 Dormir
 </label>
 </div>
 <div className="btn-group">
 <button type="button" className="btn">
 Éditer <span className="visually-hidden">Dormir</span>
 </button>
 <button type="button" className="btn btn\_\_danger">
 Supprimer <span className="visually-hidden">Dormir</span>
 </button>
 </div>
 </li>
 <li className="todo stack-small">
 <div className="c-cb">
 <input id="todo-2" type="checkbox" />
 <label className="todo-label" htmlFor="todo-2">
 Recommencer
 </label>
 </div>
 <div className="btn-group">
 <button type="button" className="btn">
 Éditer <span className="visually-hidden">Recommencer</span>
 </button>
 <button type="button" className="btn btn\_\_danger">
 Supprimer <span className="visually-hidden">Recommencer</span>
 </button>
 </div>
 </li>
 </ul>
 </div>
  );
}
```


Ouvrez maintenant `public/index.html` et changez l'élément `<title>` pour y inscrire `TodoMatic`. De cette façon, cela correspondra à la balise `<h1>` en haut de notre application.


```html
<title>TodoMatic</title>
```


Après avoir rafraîchi la page dans votre navigateur, vous pourrez voir quelque chose comme ceci :

![Une application de liste de tâches, sans mise en forme, affichant un bazar de libellés, un champ de saisie, des cases à cocher et des boutons](/fr/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning/unstyled-app.png)

C'est plutôt laid et cela ne fonctionne pas encore, mais ce n'est pas grave : nous allons ajouter la mise en forme dans un instant. Tout d'abord, revoyons le JSX utilisé plus tôt et comment il correspond à nos scénarios d'utilisation :

* Nous avons un élément `<form>`, avec un élément `<input type="text">` qui permet d'écrire une nouvelle tâche, ainsi qu'un bouton pour soumettre le formulaire.
* Nous avons une série de boutons qui seront utilisés pour filtrer nos tâches.
* Nous avons également un titre qui nous indique le nombre de tâches restantes.
* Nos trois tâches sont présentées sous forme de liste non-ordonnée. Chaque tâche est un élément de liste (`<li>`), et dispose de boutons pour la modifier et la supprimer, ainsi qu'une case à cocher pour la marquer comme terminée.

Le formulaire nous permettra de *créer* des tâches ; les boutons nous permettront de les *filtrer* ; l'en-tête et la liste nous permettront de les *lire*. L'interface utilisateur pour l'*édition* d'une tâche est manifestement absente pour le moment. Rien de trop grave, nous l'écrirons plus tard.

### Fonctionnalités pour l'accessibilité

Vous pourriez remarquer ici certains attributs inhabituels. Par exemple :


```html
<button type="button" className="btn toggle-btn" aria-pressed="true">
  <span className="visually-hidden">Montrer </span>
  <span>Toutes</span>
  <span className="visually-hidden"> les tâches</span>
</button>
```


Ici, `aria-pressed` indique aux outils d'assistance (comme les lecteurs d'écran) que le bouton peut prendre un de ces deux états : `pressed` ou `unpressed`. Vous pouvez voir ces états comme des analogues pour `on` et `off`. Le fait d'utiliser une valeur `true` pour l'attribut signifie que le bouton est pressé par défaut.

La classe `visually-hidden` n'a pas encore d'effet, car nous n'avons pas inclus de CSS. Une fois que nous aurons mis en place nos styles, tout élément avec cette classe ne sera plus visible à l'écran, mais toujours disponible pour les personnes navigant à l'aide de lecteurs d'écran. En effet, ces mots ne sont pas nécessaires pour les personnes voyantes ; ils sont là pour fournir plus d'informations sur ce que fait le bouton pour les personnes utilisant des lecteurs d'écran qui n'ont pas le contexte visuel supplémentaire pour les aider.

Plus bas, vous pouvez trouver notre élément `<ul>` :


```html
<ul
 role="list"
 className="todo-list stack-large stack-exception"
 aria-labelledby="list-heading">
  …
</ul>
```


L'attribut `role` aide les technologies d'assistance à expliquer le type d'élément qu'une balise représente. Par défaut, une balise `<ul>` est traitée comme une liste, mais les styles que nous allons ajouter vont casser cette fonctionnalité. Ce rôle va restaurer la signification "liste" de la balise `<ul>`. Si vous voulez en savoir plus sur la raison pour laquelle cela est nécessaire, vous pouvez consulter l'article de Scott O'Hara, *Fixing Lists* (en anglais).

L'attribut `aria-labelledby` indique aux technologies d'assistance que nous traitons notre en-tête de liste comme une étiquette qui décrit l'objectif de la liste en dessous. Cette association donne à la liste un contexte plus informatif, qui peut aider les utilisatrices et utilisateurs de lecteurs d'écran à mieux comprendre l'objectif de celle-ci.

Enfin, les libellés et les champs de saisie dans nos éléments de liste sont dotés d'attributs propres à JSX :


```jsx
<input id="todo-0" type="checkbox" defaultChecked={true} />
<label className="todo-label" htmlFor="todo-0">
 Eat
</label>
```


L'attribut `defaultChecked` dans la balise `<input/ >` indique à React de cocher cette case initialement. Si nous utilisions `checked`, comme pour du HTML normal, React diffuserait des avertissements dans la console de notre navigateur concernant la gestion des évènements sur la case à cocher, ce que nous voulons éviter. Ne vous inquiétez pas trop de ça pour l'instant, nous en parlerons plus tard lorsque nous aborderons l'utilisation des évènements.

L'attribut `htmlFor` correspond à l'attribut `for` utilisé en HTML. Il n'est pas possible d'utiliser `for` comme attribut en JSX, car `for` est un mot réservé en JavaScript, React utilise donc `htmlFor` à la place.

**Note :** Pour utiliser des valeurs booléennes (`true` and `false`) dans les attributs JSX, vous devez les mettre entre accolades. Si vous écrivez `defaultChecked="true"`, la valeur de `defaultChecked` sera `"true"` : une chaine de caractère. Souvenez-vous, ce qui est écrit en JSX est interprété comme du JavaScript, pas comme du HTML !

**Note :** L'attribut `aria-pressed` utilisé dans notre exemple de code précédent vaut `"true"` parce que `aria-pressed` n'est pas un véritable attribut booléen comme `checked`.

### Implémenter la mise en forme

Collez le code CSS suivant dans `src/index.css` afin qu'il remplace ce qui y est actuellement :


```css
/\* Réinitialisations \*/
\*,
\*::before,
\*::after {
  box-sizing: border-box;
}
\*:focus {
  outline: 3px dashed #228bec;
  outline-offset: 0;
}
html {
  font: 62.5% / 1.15 sans-serif;
}
h1,
h2 {
  margin-bottom: 0;
}
ul {
  list-style: none;
  padding: 0;
}
button {
  border: none;
  margin: 0;
  padding: 0;
  width: auto;
  overflow: visible;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: normal;
  -webkit-font-smoothing: inherit;
  -moz-osx-font-smoothing: inherit;
  appearance: none;
}
button::-moz-focus-inner {
  border: 0;
}
button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}
button,
input {
  overflow: visible;
}
input[type="text"] {
  border-radius: 0;
}
body {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  font:
    1.6rem/1.25 Arial,
    sans-serif;
  background-color: #f5f5f5;
  color: #4d4d4d;
}
@media screen and (min-width: 620px) {
  body {
    font-size: 1.9rem;
    line-height: 1.31579;
  }
}
/\* Fin des réinitialisations \*/
/\* Styles globaux \*/
.form-group > input[type="text"] {
  display: inline-block;
  margin-top: 0.4rem;
}
.btn {
  padding: 0.8rem 1rem 0.7rem;
  border: 0.2rem solid #4d4d4d;
  cursor: pointer;
  text-transform: capitalize;
}
.btn.toggle-btn {
  border-width: 1px;
  border-color: #d3d3d3;
}
.btn.toggle-btn[aria-pressed="true"] {
  text-decoration: underline;
  border-color: #4d4d4d;
}
.btn\_\_danger {
  color: #fff;
  background-color: #ca3c3c;
  border-color: #bd2130;
}
.btn\_\_filter {
  border-color: lightgrey;
}
.btn\_\_primary {
  color: #fff;
  background-color: #000;
}
.btn-group {
  display: flex;
  justify-content: space-between;
}
.btn-group > \* {
  flex: 1 1 49%;
}
.btn-group > \* + \* {
  margin-left: 0.8rem;
}
.label-wrapper {
  margin: 0;
  flex: 0 0 100%;
  text-align: center;
}
.visually-hidden {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}
[class\*="stack"] > \* {
  margin-top: 0;
  margin-bottom: 0;
}
.stack-small > \* + \* {
  margin-top: 1.25rem;
}
.stack-large > \* + \* {
  margin-top: 2.5rem;
}
@media screen and (min-width: 550px) {
  .stack-small > \* + \* {
    margin-top: 1.4rem;
  }
  .stack-large > \* + \* {
    margin-top: 2.8rem;
  }
}
.stack-exception {
  margin-top: 1.2rem;
}
/\* Fin des styles globaux \*/
.todoapp {
  background: #fff;
  margin: 2rem 0 4rem 0;
  padding: 1rem;
  position: relative;
  box-shadow:
    0 2px 4px 0 rgba(0, 0, 0, 0.2),
    0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1);
}
@media screen and (min-width: 550px) {
  .todoapp {
    padding: 4rem;
  }
}
.todoapp > \* {
  max-width: 50rem;
  margin-left: auto;
  margin-right: auto;
}
.todoapp > form {
  max-width: 100%;
}
.todoapp > h1 {
  display: block;
  max-width: 100%;
  text-align: center;
  margin: 0;
  margin-bottom: 1rem;
}
.label\_\_lg {
  line-height: 1.01567;
  font-weight: 300;
  padding: 0.8rem;
  margin-bottom: 1rem;
  text-align: center;
}
.input\_\_lg {
  padding: 2rem;
  border: 2px solid #000;
}
.input\_\_lg:focus {
  border-color: #4d4d4d;
  box-shadow: inset 0 0 0 2px;
}
[class\*="\_\_lg"] {
  display: inline-block;
  width: 100%;
  font-size: 1.9rem;
}
[class\*="\_\_lg"]:not(:last-child) {
  margin-bottom: 1rem;
}
@media screen and (min-width: 620px) {
  [class\*="\_\_lg"] {
    font-size: 2.4rem;
  }
}
/\* Todo item styles \*/
.todo {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
.todo > \* {
  flex: 0 0 100%;
}
.todo-text {
  width: 100%;
  min-height: 4.4rem;
  padding: 0.4rem 0.8rem;
  border: 2px solid #565656;
}
.todo-text:focus {
  box-shadow: inset 0 0 0 2px;
}
/\* Styles pour les cases à cocher \*/
.c-cb {
  box-sizing: border-box;
  font-family: Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  font-weight: 400;
  font-size: 1.6rem;
  line-height: 1.25;
  display: block;
  position: relative;
  min-height: 44px;
  padding-left: 40px;
  clear: left;
}
.c-cb > label::before,
.c-cb > input[type="checkbox"] {
  box-sizing: border-box;
  top: -2px;
  left: -2px;
  width: 44px;
  height: 44px;
}
.c-cb > input[type="checkbox"] {
  -webkit-font-smoothing: antialiased;
  cursor: pointer;
  position: absolute;
  z-index: 1;
  margin: 0;
  opacity: 0;
}
.c-cb > label {
  font-size: inherit;
  font-family: inherit;
  line-height: inherit;
  display: inline-block;
  margin-bottom: 0;
  padding: 8px 15px 5px;
  cursor: pointer;
  touch-action: manipulation;
}
.c-cb > label::before {
  content: "";
  position: absolute;
  border: 2px solid currentcolor;
  background: transparent;
}
.c-cb > input[type="checkbox"]:focus + label::before {
  border-width: 4px;
  outline: 3px dashed #228bec;
}
.c-cb > label::after {
  box-sizing: content-box;
  content: "";
  position: absolute;
  top: 11px;
  left: 9px;
  width: 18px;
  height: 7px;
  transform: rotate(-45deg);
  border: solid;
  border-width: 0 0 5px 5px;
  border-top-color: transparent;
  opacity: 0;
  background: transparent;
}
.c-cb > input[type="checkbox"]:checked + label::after {
  opacity: 1;
}
```


Sauvegardez puis rechargez la page dans votre navigateur, vous pourrez voir que l'application possède un style plus approprié.

Résumé
------

Notre application de liste de tâches (*todo list*) ressemble un peu plus à une véritable application ! Mais voilà le problème : concrètement, elle ne permet de rien faire. Nous commencerons à corriger ceci dans le prochain chapitre !

* Précédent
* Aperçu : Client-side JavaScript frameworks
* Suivant