---
title: "Ajouter des composants personnalisés"
description: "Avec le framework React, vous pouvez créer vos propres composants depuis zéro. Ces composants peuvent être utilisés pour deux choses : les paramètres dynamiques et les graphiques"
url: https://docs.ovhcloud.com/fr/guides/public-cloud/data-platform/tutorials-app-development-custom-components
lang: fr
lastUpdated: 2026-09-14
---
> For AI agents: the complete documentation index is available at https://docs.ovhcloud.com/fr/llms.txt, the full documentation bundle is available at https://docs.ovhcloud.com/fr/llms-full.txt.

# Ajouter des composants personnalisés

## Objectif

Avec le framework React, vous pouvez **créer vos propres composants** depuis zéro. Ces composants peuvent être utilisés pour deux choses : les [paramètres dynamiques](https://docs.ovhcloud.com/fr/guides/public-cloud/data-platform/tutorials-app-development-custom-components.md#utiliser-un-composant-personnalis%C3%A9-comme-param%C3%A8tre-dynamique) et les [graphiques](https://docs.ovhcloud.com/fr/guides/public-cloud/data-platform/tutorials-app-development-custom-components.md#utiliser-un-composant-personnalis%C3%A9-comme-graphique).

:::info
À tout moment, n'hésitez pas à consulter le [**Guide technique**](https://docs.dataplatform.ovh.net/#/en/technical/sdk/app/custom-component) pour en savoir plus sur les composants personnalisés !
:::

## Utiliser un composant personnalisé comme paramètre dynamique

### Générer un composant

Voyons comment créer un composant personnalisé et comment l'utiliser comme paramètre dynamique.

À titre d'exemple, nous allons vous montrer étape par étape comment créer ce composant de groupe de cases à cocher :

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/dynamic-parameter-checkbox-final.png)
Pour générer un composant, vous pouvez utiliser notre script préconstruit en exécutant la commande ci-dessous :

```bash
yarn generate
```

Sélectionnez le générateur **component**, nommez-le `DynamicParameterCheckbox` et répondez « No » à la dernière question, comme indiqué ci-dessous :

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/generate-checkbox.png)
:::warning
Il est important que le nom de votre composant commence par **DynamicParameter** pour qu'il soit enregistré comme paramètre dynamique
:::

Ce script génère un dossier dans `src/components` contenant trois fichiers :

- `DynamicParameterCheckbox.jsx`
- `DynamicParameterCheckbox.less`
- `index.js`

### Déclarer un composant

Avant de commencer à travailler sur le composant lui-même, prenons une minute pour voir comment le déclarer et l'utiliser.

Suivez ces étapes pour déclarer le nouveau composant :

- Allez dans `src/components/index.js`

- Importez le composant en ajoutant cette ligne en haut du fichier :

  ```js
  import DynamicParameterCheckbox from "./DynamicParameterCheckbox";
  ```

- Déclarez-le en ajoutant son nom à l'objet components

  ```js
    components: {
        ...
        DynamicParameterCheckbox
  },
  ```

Vous pouvez maintenant appeler ce composant dans vos fichiers de configuration en spécifiant son **alias**. Pour obtenir l'alias d'un composant, transformez son nom du camelCase au kebab-case (`DynamicParameterCheckbox` devient `dynamic-parameter-checkbox`), puis retirez-en "dynamic-parameter" et/ou "chart".

Ici, l'alias du composant est `checkbox`. Allez maintenant dans `config/menus/sidebar.json` et changez la propriété _component_ de _dynamic-weekday_ en `"checkbox"` (c'est-à-dire l'alias).

Allez sur la page de votre application, remarquez que **le composant dynamic-weekday a changé**.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/dynamic-parameter-checkbox-1.png)
Le composant s'affiche mais n'est pas encore fonctionnel : son comportement est défini dans la section suivante.

### Modifier un composant

Le framework utilisé dans ce guide sera React.

:::info
Nous allons vous guider étape par étape pour créer vos composants, mais si vous rencontrez des difficultés parce que vous n'êtes pas familier avec ce framework, vous pouvez consulter la documentation [ici](https://reactjs.org/docs/getting-started.html).
:::

De plus, puisque nous travaillons avec des composants fonctionnels, nous allons utiliser l'une des dernières fonctionnalités disponibles, les **React Hooks**. Vous pouvez consulter cette [page](https://reactjs.org/docs/hooks-intro.html) pour plus d'informations.

Commençons par modifier `DynamicParameterCheckbox.jsx`

Puisque ce composant est déclaré comme un dynamic-parameter, certaines props lui sont déjà automatiquement transmises par la logique de flux de données de la Platform. Ces props peuvent être journalisées lors du premier rendu du composant, en utilisant le hook [useEffect](https://reactjs.org/docs/hooks-reference.html#useeffect).

Commencez par importer `useEffect` et `useState` (dont vous aurez besoin plus tard) en modifiant la première ligne du fichier :

```js
import React, { useEffect, useState } from 'react'
```

Ajoutez ensuite `props` aux arguments du composant fonctionnel :

```jsx
const DynamicParameterCheckbox = (props) => {
```

Enfin, ajoutez le hook useEffect à l'intérieur de cette fonction, juste avant l'instruction return.

Votre composant fonctionnel devrait être défini comme suit :

```jsx
const DynamicParameterCheckbox = (props) => {
  useEffect(() => {
    console.log(props);
  }, []);

  return <div>DynamicParameterCheckbox</div>;
};
```

Ouvrez le panneau console de votre navigateur, vous devriez pouvoir voir les props.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/dynamic-parameter-checkbox-props.png)
Comme vous pouvez le voir, la plupart de ces props sont transmises dans `sidebar.json` lors de la définition du dynamic-parameter.\
La seule exception est `items`, qui est le résultat d'une requête interne interrogeant le champ spécifié dans `dictionary` (`week_day` dans ce cas)

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/dynamic-parameter-checkbox-items.png)
Créons maintenant les checkboxes.

Pour y parvenir, vous devrez installer un petit package : `react-checkbox-group`. Faites-le en exécutant :

```bash
yarn add react-checkbox-group
```

Ce package vous permet d'utiliser un composant de groupe de cases à cocher. Vous pouvez maintenant l'importer en ajoutant cette ligne en haut du fichier :

```js
import CheckboxGroup from 'react-checkbox-group'
```

Ajoutez une variable booléenne `expanded` à l'état du composant pour pouvoir afficher ou masquer le `CheckboxGroup` à l'aide d'une petite icône flèche :

```js
const DynamicParameterCheckbox = (props) => {
  const [expanded, setExpanded] = useState(true)
  ...
```

Changez l'instruction return de la fonction comme suit :

```jsx
return (
  <div>
    <div className='dyn-title checkbox-header'>
      {props.title}
      <i onClick={() => setExpanded(!expanded)} className={`fa fa-chevron-${expanded ? 'down' : 'right'} expand-icon`} /></div>
    {expanded && <CheckboxGroup name='options'>
      {(Checkbox) => (
        <React.Fragment>
          {props.items.map(option =>
            <div key={option.value}>
              <label className='dyn-checkbox-container' htmlFor={option.value}>
                <Checkbox className='dyn-checkbox' id={option.value} value={option.value} />
                <span className='dyn-checkbox-label'>{option.label}</span>
              </label>
            </div>)}
        </React.Fragment>
      )}
    </CheckboxGroup>}
  </div>
)
```

La section titre utilise _props.title_. Cela signifie que vous devez transmettre une prop `title` dans la définition de **dynamic-weekday** dans `config/menus/sidebar.json`, comme indiqué ci-dessous : (l'élément HTML a également été retiré puisqu'il est désormais redondant)

```json
{
  "id": "dyn-weekday",
  "items": [
    {
      "type": "dynamic-parameter",
      "dynamic-parameter": {
        "type": "filter",
        "id": "dynamic-weekday",
        "dictionary": "week_day_label",
        "component": "checkbox",
        "title": "Days"
      },
      "style": {
        "paddingBottom": "10px"
      }
    }
  ]
}
```

Enfin, stylisez votre composant en modifiant `DynamicParameterCheckbox.less`, à l'aide de l'extrait de code ci-dessous.

:::details DynamicParameterCheckbox.less

```less
// Feuille de style DynamicParameterCheckbox

@import "../../styles/_variables.less";

.checkbox-header {
  display: flex;
  flex-direction: row;
  align-items: center;
}

.dyn-checkbox-container {
  display: flex;
  align-items: flex-end;
  margin: 3px;
}

.dyn-checkbox {
  -webkit-appearance: none;
  background-color: #fafafa;
  border: 1px solid #cacece;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), inset 0px -15px 10px -12px rgba(0, 0, 0, 0.05);
  padding: 7px;
  border-radius: 3px;
  display: inline-block;
  position: relative;
}

.dyn-checkbox:hover {
  cursor: pointer;
  box-shadow: 0 1px 2px @primary-color, inset 0px 1px 3px @primary-color;
  border: 1px solid @primary-color;
}

.dyn-checkbox:active,
.dyn-checkbox:checked:active {
  box-shadow: 0 1px 2px @primary-color, inset 0px 1px 3px @primary-color;
}

.dyn-checkbox:checked {
  background-color: @primary-color;
  border: 1px solid @primary-color;
  box-shadow: 0 1px 2px @primary-color, inset 0px -15px 10px -12px
      @primary-color, inset 15px 10px -12px @primary-color;
  color: white;
}

.dyn-checkbox:checked:after {
  content: "\2714";
  font-size: 12px;
  position: absolute;
  top: -1px;
  left: 2px;
  color: white;
}

.dyn-checkbox-label {
  cursor: pointer;
  font-family: "Source Sans Pro";
  font-size: 14px;
  letter-spacing: 0;
  color: #97a7b7;
  line-height: 17px;
  margin-left: 5px;
}

.fa.expand-icon {
  color: #97a7b7;
  font-size: 12px;
  cursor: pointer;
  margin-left: 5px;
}

.fa.expand-icon:hover {
  color: #3e4550;
  cursor: pointer;
}
```

:::

Votre dynamic-parameter a maintenant une belle apparence, mais il ne fonctionne pas encore correctement.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/dynamic-parameter-checkbox-wip.png)
Il y a un problème, vous ne pouvez cocher aucune case !

La raison est que vous devez lier CheckboxGroup à notre composant dynamic-parameter. Pour ce faire, vous devez ajouter un tableau à l'état de votre composant. Il gardera une trace des options sélectionnées, alors nommez-le `selectedOptions` :

```jsx
const DynamicParameterCheckbox = (props) => {
  const [expanded, setExpanded] = useState(true)
  const [selectedOptions, setSelectedOptions] = useState([])
  ...
```

Ajoutez une fonction `onChange` pour mettre à jour le tableau que vous venez de définir :

```jsx
const onChange = options => {
  setSelectedOptions(options);
};
```

Enfin, vous devez ajouter les deux propriétés (`onChange` et `value`, un tableau contenant `selectedOptions`) à CheckboxGroup. Liez-les en modifiant l'instruction return :

```jsx
return (
  ...
  <CheckboxGroup name='options' value={selectedOptions || []} onChange={onChange}>
  ...
```

La dernière étape consiste à lier ce dynamic-parameter au [store global redux](https://redux.js.org/api/store) pour lui permettre d'affecter les requêtes des graphiques. Nous allons utiliser les hooks React Redux pour une approche plus moderne et rationalisée. Ajoutez ces lignes aux imports en haut du fichier :

```jsx
import { useSelector, useDispatch } from 'react-redux'
import { set } from 'forepaas/store/querystring/action'
```

Ensuite, à l'intérieur de votre composant fonctionnel DynamicParameterCheckbox, accédez à l'état Redux et à la fonction dispatch à l'aide des hooks. Placez ces lignes à l'intérieur de la fonction de votre composant :

```jsx
const options = useSelector((state) => state.querystring[props.id])
const dispatch = useDispatch()
```

- `useSelector` : ce hook vous permet d'extraire des données de l'état du store Redux. Ici, nous récupérons la valeur associée à props.id depuis la partie querystring de votre état Redux. Cette variable options se mettra automatiquement à jour à chaque fois que l'état Redux change pour cet ID spécifique.

- `useDispatch` : ce hook renvoie une référence vers la fonction dispatch du store Redux. Vous utiliserez cette fonction dispatch pour envoyer des actions afin de mettre à jour le store.

À l'intérieur de votre composant fonctionnel `DynamicParameterCheckbox`, créez une fonction utilitaire `updateModel` pour envoyer vos actions au store global :

```jsx
const updateModel = (model) => {
  if (props.id) {
    let value = (model || []).map((item) => item && item.value ? item.value : item)
    dispatch(set(props.id, value.length ? value : null))
  }
}
```

Cette fonction utilise maintenant la fonction dispatch obtenue depuis `useDispatch` pour envoyer l'action set, mettant à jour la querystring dans le store Redux.

Ensuite, mettez à jour votre fonction onChange pour simplement appeler updateModel avec les nouvelles options :

```jsx
const onChange = (options) => {
    updateModel(options)
  }
```

Pour vous assurer que l'état interne de votre composant (selectedOptions) est synchronisé avec l'état du store Redux (notamment après un rafraîchissement de page ou des changements externes à l'état Redux pour ce paramètre), mettez à jour votre hook `useEffect` comme suit :

```jsx
useEffect (() => {
    setSelectedOptions(options)
  }, [options])
```

Ce hook `useEffect` s'exécutera chaque fois que la valeur options (qui provient directement de votre store Redux via `useSelector`) change. Il maintient l'état local `selectedOptions` de votre composant synchronisé avec l'état Redux global, offrant une vue cohérente du paramètre dynamique.

Votre paramètre dynamique est maintenant connecté au store global. Vous pouvez vous en assurer car les graphiques connectés à dynamic-weekday sont affectés par ce paramètre dynamique.

Vous avez créé votre premier paramètre dynamique personnalisé.\
Le code final est disponible [ici](https://github.com/forepaas/getting-started/tree/release/advanced/src/components/DynamicParameterCheckbox).

### Ajouter une barre de recherche

Ajoutons un second paramètre dynamique pour pouvoir filtrer par station.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/dynamic-parameter-autocomplete-box.png)
Générez un nouveau composant :

```bash
yarn generate
```

Sélectionnez le générateur **component**. Nommez-le `DynamicParameterAutocompleteBox` et répondez « No » à la dernière question.

Remplacez le contenu du fichier .jsx nouvellement généré par ceci :

:::details DynamicParameterAutocompleteBox.jsx

```jsx
import React, { useState, useEffect } from 'react'
import Select, { createFilter }  from 'react-select'
import { useSelector, useDispatch } from 'react-redux'
import { set } from 'forepaas/store/querystring/action'

const searchIcon = () => ({
  alignItems: 'center',
  display: 'flex',
  ':before': {
    fontFamily: 'FontAwesome',
    content: '"\f002"',
    marginRight: 8
  }
})

const customStyles = {
  control: (base, state) => ({
    ...base,
    height: '57px',
    borderWidth: '0px',
    boxShadow: '5px 5px -2px rgba(0, 0, 0, 0.1)'
  }),
  option: (base, state) => ({
    ...base,
    color: state.theme.colors.neutral50,
    fontSize: '13px',
    height: '48px',
    display: 'flex',
    alignItems: 'center',
    fontWeight: 500,
    letterSpacing: 0
  }),
  menu: (base, state) => ({
    ...base,
    zIndex: 2000
  }),
  multiValue: (base, state) => ({
    ...base,
    borderRadius: '21px',
    backgroundColor: state.theme.colors.primary75,
    color: state.theme.colors.neutral0,
    height: '34px',
    alignItems: 'center'
  }),
  multiValueLabel: (base, state) => ({
    ...base,
    color: state.theme.colors.neutral0,
    display: 'flex',
    alignItems: 'center',
    marginLeft: '10px',
    fontWeight: 600,
    letterSpacing: 0
  }),
  placeholder: (base, state) => ({
    ...base,
    color: state.theme.colors.neutral20,
    fontSize: '14px',
    ...searchIcon()
  }),
  multiValueRemove: (base, state) => {
    return ({
      ...base,
      color: state.theme.colors.neutral0,
      borderRadius: '10px',
      padding: '0px',
      margin: '10px',
      border: `1px solid ${state.theme.colors.neutral0}`,
      ':hover': {
        backgroundColor: state.theme.colors.neutral0,
        color: state.theme.colors.primary75,
        cursor: 'pointer'
      }
    })
  }

}

const DynamicParameterAutocompleteBox = ({ items, ...props }) => {
  const [selectedOptions, setSelectedOptions] = useState([])
  const selectedValues = useSelector((state) => state.querystring[props.id])
  const dispatch = useDispatch()
  items.sort((a,b) => (a.label > b.label) ? 1 : ((b.label > a.label) ? -1 : 0))

  const customTheme = theme => ({
    ...theme,
    colors: {
      ...theme.colors,
      primary: props.primaryColor || '#00CCF9',
      primary25: props.primary25Color || 'rgba(16,182,233,0.06)',
      primary50: props.primary50Color || 'rgba(16,182,233,0.20)',
      primary75: props.primary75Color || 'rgba(16,182,233,0.8)',
      neutral20: props.placeholderColor || '#97A7B7',
      neutral50: props.menuColor || '#485465'
    }
  })

  useEffect (() => {
    const mappedOptions = items.filter(item => (selectedValues || []).includes(item.value))
    setSelectedOptions(mappedOptions)
  }, [selectedValues, items])

  const updateModel = (model) => {
    if (props.id) {
      let values = (model || []).map((item) => item && item.value ? item.value : item)
      dispatch(set(props.id, values.length ? values : null))
    }
  }

  const onChange = (options) => {
    if (options && !Array.isArray(options)) {
      options = [options]
    }
    updateModel(options)
  }

  return (
    <Select 
      value={selectedOptions || []} 
      styles={customStyles} theme={customTheme} 
      options={items} 
      isMulti={props.isMulti}
      placeholder={props.placeholder}
      onChange={onChange} 
      autoFocus={true}
      isClearable={true}
      isSearchable={true}
      pageSize={10}
      closeMenuOnSelect={true}
      filterOption={createFilter({ignoreCase: true, ignoreAccents: true, trim: true, matchFrom: 'start'})}
    />
  )
}

export default DynamicParameterAutocompleteBox
```

:::

[Déclarez-le](#déclarer-un-composant) dans `src/components/index.js`
en important votre nouveau composant et en l'ajoutant à l'objet `components` défini dans le fichier.

Allez dans `config/dashboards/rides-analytics.json`, cherchez le panel dont l'id est `search-bar` et **remplacez-le par un dynamic-parameter** comme suit :

```json
{
  "type": "dynamic-parameter",
  "id": "search-bar",
  "dynamic-parameter": {
    "type": "filter",
    "id": "dynamic-stations",
    "component": "autocomplete-box",
    "placeholder": "Search for a station",
    "isMulti": true,
    "dictionary": "station_name"
  },
  "sizeX": 100,
  "sizeY": 19,
  "col": 0,
  "row": 0,
  "items": []
}
```

Enfin, retirez tout l'objet _dyn-stations_ de `config/menus/sidebar.json`, car il est redondant avec la nouvelle barre de recherche.

## Utiliser un composant personnalisé comme graphique

Dans cette section, vous apprendrez à créer un composant personnalisé et à l'utiliser comme graphique.

À titre d'exemple, vous allez créer un composant de carte interactive à l'aide de la bibliothèque [react-leaflet](https://react-leaflet.js.org/). Le résultat final ressemblera à ceci :

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/custom-chart-map.png)
Commençons par créer le composant à l'aide du script, comme d'habitude.

```bash
yarn generate
```

Sélectionnez le générateur **component**, nommez-le `ChartMap` et répondez « No » à la dernière question.

:::warning
Il est important que le nom de votre composant de graphique commence par 'Chart', afin qu'il soit reconnu et enregistré correctement.
:::

[Déclarez-le](#déclarer-un-composant) dans `src/components/index.js`.

Vous pouvez maintenant l'utiliser dans votre dashboard analytics. Allez dans `rides-analytics.json`, et ajoutez le nouveau graphique ainsi que son titre au panel `custom-map` :

```json
{
  "type": "panel",
  "id": "custom-map",
  "sizeX": 75,
  "sizeY": 99,
  "row": 20,
  "col": 0,
  "items": [
    {
      "type": "html",
      "content": "Map view",
      "customclass": "chart-title",
      "sizeX": 100,
      "sizeY": 5
    },
    {
      "type": "chart",
      "chart": {
        "id": "chart-map",
        "component": "map",
        "dynamic-parameters": [
          "dynamic-date",
          "dynamic-holidays",
          "dynamic-stations",
          "dynamic-weekday"
        ],
        "request": {
          "data": {
            "fields": {
              "rides": ["avg"]
            }
          },
          "scale": {
            "fields": ["station_id", "station_name", "lat", "lng"]
          },
          "filter": {},
          "order": {}
        }
      },
      "sizeX": 100,
      "sizeY": 88,
      "row": 6,
      "col": 0
    }
  ]
}
```

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-map-init.png)
Comme les dynamic-parameters, les graphiques reçoivent également automatiquement certaines propriétés lors de leur montage. Voyons à quoi elles ressemblent.

Comme d'habitude, importez `useEffect` et `useState` en **modifiant la première ligne** du fichier `ChartMap.jsx` :

```jsx
import React, { useState, useEffect } from 'react'
```

Ajoutez ensuite `props` aux arguments du composant fonctionnel :

```jsx
const ChartMap = (props) => {
```

Enfin, ajoutez un hook useEffect à l'intérieur de cette fonction, juste avant l'instruction return.

Votre composant fonctionnel final devrait ressembler à ceci :

```jsx
const ChartMap = (props) => {
  useEffect(() => {
    console.log(props);
  }, []);
  return <div>ChartMap</div>;
}
```

Ouvrez le panneau console de votre navigateur, vous devriez pouvoir voir les props transmises à ce composant.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-map-props.png)
La plupart de ces props proviennent de la définition du graphique dans `config/dashboards/rides-analytics.json`.\
L'exception est `data`, qui est un JSON représentant le résultat de la requête associée au graphique. Ce JSON se veut aussi générique que possible, il peut donc être un peu fastidieux à manipuler.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-data-results.png)
Dans cet exemple -- et dans la plupart des cas -- l'objet JSON est trop complexe, nous utilisons donc cette fonction utilitaire pour l'aplatir et le rendre bien plus facile à manipuler :

```jsx
const flattenChartResults = results => {
  let flattenedChartResults = [];
  results.forEach(result => {
    let fieldEntries = [];
    Object.entries(result.data).forEach(([fieldName, computeModes]) => {
      Object.entries(computeModes).forEach(([computeModeName, rows]) => {
        computeModeName === "select"
          ? fieldEntries.push([computeModeName, rows[0].value])
          : fieldEntries.push([
              `${fieldName}_${computeModeName}`,
              rows[0].value
            ]);
      });
    });
    let flattenedObject = {
      ...result.scales,
      ...Object.fromEntries(fieldEntries)
    };
    flattenedChartResults.push(flattenedObject);
  });
  return flattenedChartResults;
}
```

Copiez-collez l'extrait de code ci-dessus entre les imports et ChartMap, puis mettez à jour le contenu de votre hook useEffect pour visualiser les nouveaux résultats aplatis :

```jsx
useEffect(() => {
  const results = flattenChartResults(props.chart.data.results);
  console.log(results);
}, [props.chart.data.results]);
```

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-flattened-results.png)
Pour afficher ces résultats sur la carte, vous devez stocker ces données dans l'état du composant. Déclarez un tableau à l'intérieur du composant `ChartMap` comme ceci :

```jsx
const ChartMap = (props) => {
  const [stations, setStations] = useState([])
  ...
```

Mettez à jour useEffect pour modifier ce tableau au lieu de journaliser les résultats :

```js
useEffect(() => {
  const results = flattenChartResults(props.chart.data.results);
  setStations(results);
}, [props.chart.data.results]);
```

Maintenant que vous avez préparé les données, voyons comment créer la carte.

Importez maintenant les composants react-leaflet en ajoutant cette ligne en haut de votre fichier :

```js
import { Map as LeafletMap, CircleMarker, Popup, TileLayer } from 'react-leaflet'
```

Allez dans `ChartMap.less` et modifiez la feuille de style à l'aide de l'extrait de code ci-dessous :

:::details ChartMap.less

```less
// Feuille de style ChartMap
@import "~leaflet/dist/leaflet.css";
@import "~react-leaflet-markercluster/dist/styles.min.css";

.leaflet-container {
  height: 100%;
}

.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  opacity: 0.85;
  background-color: #3e4550;
  box-shadow: inset 0 -3px 0 0 rgba(0, 0, 0, 0.13);
  color: #fff;
}

.leaflet-popup-content-wrapper {
  text-align: center;
  border-radius: 1px;
}

.popup-title {
  letter-spacing: 0;
  font-size: 13px;
}

.popup-value {
  font-size: 13px;
  font-weight: 600;
}
```

:::

Retournez dans `ChartMap.jsx` et définissez vos paramètres de carte par défaut (center, zoom, minZoom) juste en dessous de votre state :

```jsx
const center = [41.89, -87.6297982]
const zoom = 14
const minZoom = 7
```

Modifiez l'instruction return de votre fonction pour afficher une carte vide centrée sur Chicago :

```jsx
return (
  <LeafletMap center={center} zoom={zoom} minZoom={minZoom}>
    <TileLayer
      attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>'
      url="https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png"
    />
  </LeafletMap>
)
```

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-empty-map.png)
Ajoutez maintenant des circle markers à l'intérieur de `LeafletMap`, juste en dessous du `TileLayer`, pour représenter les stations :

```jsx
{
  stations.map(station => (
    <CircleMarker
      key={station.station_id}
      center={[station.lat, station.lng]}
    />
  ))
}
```

:::info
Vous pouvez personnaliser vos circle markers en ajoutant des props pour contrôler leur taille, leur épaisseur, leur couleur, etc. Vous pouvez trouver la documentation complète [ici](https://leafletjs.com/reference-1.6.0.html#circlemarker)
:::

Ci-dessous, un extrait de code qui change la couleur des cercles et rend leur taille proportionnelle au nombre de trajets.

```jsx
<CircleMarker
  key={station.station_id}
  center={[station.lat, station.lng]}
  radius={Math.max(
    10 + (station.rides_avg / 1000) * Math.log(zoom),
    ((2 * station.rides_avg) / 1000) * Math.log(zoom)
  )}
  color={"#0089C0"}
  weight={1}
  fillColor={"rgba(0,204,249,0.5)"}
/>
```

Ensuite, ajoutez un popup pour afficher le nom de la station et le nombre de trajets qui lui sont associés lors du clic sur un cercle.

Ajoutez-le à l'intérieur de `CircleMarker` comme indiqué ci-dessous :

```jsx
<CircleMarker
  key={station.station_id}
  center={[station.lat, station.lng]}
  radius={Math.max(
    10 + (station.rides_avg / 1000) * Math.log(zoom),
    ((2 * station.rides_avg) / 1000) * Math.log(zoom)
  )}
  color={"#0089C0"}
  weight={1}
  fillColor={"rgba(0,204,249,0.5)"}
>
  <Popup closeButton={false} position={[station.lat, station.lng]}>
    <div className="popup-title">{station.station_name}</div>
    <div className="popup-value">{station.rides_avg}</div>
  </Popup>
</CircleMarker>
```

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-map-rides-no-format.png)
Vous pouvez faire en sorte que le nombre moyen de trajets suive les règles de formatage spécifiées dans `config/formatter.json` pour `rides`, en utilisant `FpMeasure`.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chart-rides-formatter.png)
Importez FpMeasure :

```js
import { FpMeasure } from 'forepaas/formatter'
```

Modifiez la valeur du popup :

```jsx
<div className="popup-value">
  {new FpMeasure("rides").setValue(station.rides_avg).toString()}
</div>
```

Si vous jouez un peu avec votre carte, vous remarquerez qu'elle continue de se recharger lorsque vous changez vos paramètres dynamiques. Vous pouvez désactiver ce comportement en ajoutant une propriété `noReload` dans les options de configuration de votre graphique, dans `config/dashboards/rides-analytics.json` :

```json
"chart": {
  "id": "chart-map",
  "component": "map",
  "noReload": true,
  "dynamic-parameters": [
    ...
  ],
  "request": {
    ...
  }
},
```

Réorganisez vos cercles par clusters pour éviter les chevauchements ; c'est assez simple grâce à la bibliothèque `react-leaflet-markercluster`.

Importez le composant MarkerClusterGroup :

```js
import MarkerClusterGroup from 'react-leaflet-markercluster'
```

Puis **enveloppez vos circle markers** dans `MarkerClusterGroup` comme ceci :

```jsx
<MarkerClusterGroup showCoverageOnHover={false}>
  {stations.map(station => (
    <CircleMarker
      ...
    </CircleMarker>
  ))}
</MarkerClusterGroup>
```

Vous avez créé votre premier composant personnalisé.\
Le code final est disponible [ici](https://github.com/forepaas/getting-started/tree/release/advanced/src/components/ChartMap).

:::info
Pour en savoir plus sur la création de vos composants personnalisés, consultez le [**Guide technique**](https://docs.dataplatform.ovh.net/#/en/technical/sdk/app/custom-component) !
:::

## Publier votre application locale sur la Data Platform

Maintenant que vous avez terminé votre application en local, il est temps de la connecter à la platform.\
Vous pouvez le faire soit en [chargeant l'application sous forme de fichier zip](#importer-votre-application-en-tant-que-fichier-zip), soit en [la connectant à un repository git](#connecter-votre-application-à-un-repository-git).

### Importer votre application en tant que fichier zip

Tout d'abord, vous devez exporter votre application au format zip. Votre fichier zippé ne doit pas contenir les dossiers suivants : `node_modules`, `forepaas`, `webpack`, `.git`.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/zip-getting-started.png)
Vous pouvez également utiliser notre script préconstruit pour zipper votre application ; pour générer un fichier zip prêt à être chargé sur votre Projet, exécutez simplement cette commande.

```bash
yarn zip
```

Un fichier zip portant le même nom que le dossier de votre application sera généré.

Ensuite, vous pouvez soit créer une nouvelle application à l'aide de ce fichier .zip en suivant [ces étapes](https://docs.ovhcloud.com/fr/guides/public-cloud/data-platform/getting-started-visualize-data.md#build-and-deploy-a-front-end-application), soit modifier une application existante et charger le fichier zip pour créer une nouvelle version de votre application :

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/getting-started-zip-upload.png)
### Connecter votre application à un repository git

Pour relier un repository git à la platform, veuillez suivre ce [guide](https://docs.ovhcloud.com/fr/guides/public-cloud/data-platform/app-services-apps-git-integration.md).

Une fois votre application chargée, buildez-la puis déployez-la.

![alt text](/images/public-cloud/data-platform/getting-further/app-dev/picts/chicago-layout-final.png)
Votre superbe application est maintenant déployée sur la Data Platform !

## Aller plus loin

Si vous avez besoin d'une formation ou d'une assistance technique pour la mise en oeuvre de nos solutions, contactez votre commercial ou cliquez sur [ce lien](https://www.ovhcloud.com/fr/professional-services/) pour obtenir un devis et demander une analyse personnalisée de votre projet à nos experts de l’équipe Professional Services.

Posez vos questions, faites-nous part de vos commentaires et interagissez directement avec l’équipe qui développe la Data Platform sur le [canal Discord](https://discord.gg/ovhcloud) dédié.

Si vous avez besoin d'une assistance concernant vos services OVHcloud, créez une demande depuis notre [centre d'aide](https://help.ovhcloud.com/csm?id=csm_get_help).

Rejoignez notre [communauté d'utilisateurs](https://community.ovhcloud.com/).
