> For the complete documentation index, see [llms.txt](https://docs.devolutions.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.devolutions.net/powershell-universal/fr/apps/components/custom-components/building-custom-components.md).

# Création de composants JavaScript personnalisés

Universal est extensible et vous permet de créer des composants et des frameworks JavaScript personnalisés. Ce document explique comment créer des composants personnalisés qui s'intègrent à la plateforme d'applications Universal.

{% hint style="warning" %}
Il s'agit d'un sujet avancé qui n'est pas nécessaire si vous souhaitez simplement utiliser Universal Apps.
{% endhint %}

{% hint style="info" %}
Pour un exemple fonctionnel complet, consultez le projet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) sur GitHub. Il s'agit d'un composant personnalisé prêt pour la production qui intègre la bibliothèque de diagrammes Mermaid avec PowerShell Universal.
{% endhint %}

## Vue d'ensemble

La création d'un composant React personnalisé pour PowerShell Universal implique plusieurs éléments clés :

1. **Structure du projet** : Un projet Node.js avec Webpack pour regrouper les composants React
2. **Composant React (JSX)** : Le composant React qui affiche votre interface utilisateur
3. **Module PowerShell (PSM1)** : Les fonctions PowerShell qui créent les définitions de composants
4. **Manifeste du module (PSD1)** : Les métadonnées standard du module PowerShell
5. **Processus de compilation** : La configuration Webpack pour regrouper les ressources JavaScript
6. **Enregistrement du composant** : L'enregistrement de votre composant auprès de Universal Dashboard

### Fonctionnement

L'intégration entre PowerShell et React fonctionne de la manière suivante :

1. **Côté PowerShell** : Votre fonction PowerShell retourne une table de hachage avec les propriétés du composant
2. **Enregistrement des ressources** : Le JavaScript regroupé est enregistré auprès de l'AssetService de PowerShell Universal
3. **Type de composant** : La propriété `type` relie la table de hachage PowerShell au composant React
4. **Transmission des props** : Les propriétés de la table de hachage deviennent automatiquement des props React
5. **Rendu** : Universal Dashboard charge le bundle JavaScript et affiche le composant React

```
PowerShell Function → Hashtable → Asset Service → React Component → DOM
```

### L'exemple ud-mermaid

Tout au long de ce guide, nous ferons référence au projet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) comme exemple concret. Ce composant encapsule la bibliothèque de diagrammes Mermaid.js pour une utilisation dans PowerShell Universal, et illustre :

* L'intégration de bibliothèques JavaScript tierces
* L'utilisation des hooks React (useEffect, useRef)
* La transmission d'objets de configuration
* Une structure de projet professionnelle
* L'automatisation de la compilation

## Procédure pas à pas

La section suivante vous guidera étape par étape à travers les différents aspects de la création d'un composant Universal App.

### 1. Installation des dépendances

Vous devez installer les dépendances suivantes avant de créer votre composant.

* [NodeJS](https://nodejs.org/en/) - Requis pour npm et l'exécution des outils de compilation

### 2. Créer un nouveau projet

Créez un nouveau répertoire pour votre projet de composant :

```powershell
New-Item -Path .\MyComponent -ItemType Directory
Set-Location .\MyComponent
```

Initialisez un nouveau projet npm :

```powershell
npm init -y
```

Cela crée une structure de projet de base comprenant :

* `package.json` - Les dépendances Node.js et les scripts de compilation

### 3. Installer les dépendances JavaScript

Installez les outils de compilation et les dépendances React requis :

```powershell
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli css-loader style-loader @babel/plugin-proposal-class-properties @babel/plugin-syntax-dynamic-import --legacy-peer-deps
```

Installez le package Universal Dashboard :

```powershell
npm install universal-dashboard --legacy-peer-deps
```

Par exemple, le projet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) inclut le package `mermaid` comme dépendance supplémentaire :

```json
"dependencies": {
    "mermaid": "^9.4.3",
    "universal-dashboard": "^1.0.1"
}
```

Installez les bibliothèques supplémentaires dont votre composant a besoin :

```powershell
npm install mermaid --legacy-peer-deps
```

### 4. Créer la structure du projet

Créez les répertoires nécessaires pour votre composant :

```powershell
New-Item -Path .\Components -ItemType Directory
```

### 5. Créer votre composant React

Créez un composant React dans le répertoire `Components/`. Votre composant doit :

1. Importer depuis `universal-dashboard`
2. Utiliser le HOC (composant d'ordre supérieur) `withComponentFeatures`
3. Accepter des props correspondant aux paramètres de votre fonction PowerShell

**Exemple tiré de ud-mermaid** (`Components/mermaid.jsx`) :

```jsx
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
    
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]);

  return (
    <div className="mermaid" id={id} ref={mermaidRef}>
      {diagram}
    </div>
  );
};

export default withComponentFeatures(UDMermaid);
```

### 6. Enregistrer votre composant

Créez un fichier `index.js` dans le répertoire `Components/` qui enregistre votre composant auprès de Universal Dashboard :

```javascript
import UDMermaid from './mermaid';
UniversalDashboard.register("ud-mermaid", UDMermaid);
```

La chaîne de caractères que vous passez à `register()` devient la propriété `type` que vous utiliserez dans votre fonction PowerShell.

### 7. Créer les fonctions PowerShell

Vous devez maintenant rédiger le code du module PowerShell. Vous devrez mettre à jour le fichier PSM1 pour charger les ressources et définir les fonctions qui créent les définitions de composants.

Le fichier PSM1 doit :

1. Enregistrer le fichier JavaScript regroupé auprès de l'AssetService
2. Définir des fonctions qui retournent des tables de hachage avec les propriétés du composant

**Exemple tiré de ud-mermaid** (`UniversalDashboard.Mermaid.psm1`) :

```powershell
# Register JavaScript assets with PowerShell Universal
Get-ChildItem "$PSScriptRoot\*.js" | ForEach-Object {
    $Item = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($_.FullName)
    if ($_.Name.StartsWith("index.") -and $_.Name.EndsWith(".bundle.js")) {
        $AssetId = $Item
    }
}

function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        assetId = $AssetId 
        isPlugin = $true 
        type = "ud-mermaid"  # This matches the name used in UniversalDashboard.register()
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

**Propriétés clés de la table de hachage :**

* `assetId` - L'identifiant retourné par RegisterAsset
* `isPlugin` - Toujours défini à `$true` pour les composants personnalisés
* `type` - Doit correspondre au nom utilisé dans `UniversalDashboard.register()`
* `id` - Un identifiant unique pour l'instance du composant
* Les propriétés supplémentaires sont transmises comme props à votre composant React

### 8. Configurer Webpack

Votre fichier `webpack.config.js` doit regrouper vos composants et externaliser les dépendances React et Universal Dashboard. Voici la configuration essentielle tirée de ud-mermaid :

```javascript
module.exports = (env) => {
  return {
    entry: {
      'index': __dirname + '/components/index.js'
    },
    output: {
      path: BUILD_DIR,
      filename: '[name].[hash].bundle.js',
      library: 'udcomponent',
      libraryTarget: 'var'
    },
    module: {
      rules: [
        { test: /\.(js|jsx)$/, exclude: [/public/], loader: 'babel-loader' },
        { test: /\.css$/, loader: "style-loader!css-loader" }
      ]
    },
    externals: {
      'react': 'react',
      'react-dom': 'reactdom',
      UniversalDashboard: 'UniversalDashboard'
    },
    resolve: {
      extensions: ['.js', '.jsx']
    }
  };
}
```

**Externals importants :**

* `react` et `react-dom` - Fournis par PowerShell Universal
* `UniversalDashboard` - L'objet global Universal Dashboard

### 8.1. Configurer Babel

Créez un fichier `.babelrc` pour configurer la transformation JSX et du JavaScript moderne :

```json
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": [">0.5%", "not dead"]
      }
    }],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-nullish-coalescing-operator"
  ]
}
```

Cette configuration :

* Transforme JSX en JavaScript
* Transpile le JavaScript moderne pour la compatibilité avec les navigateurs
* Active des fonctionnalités utiles du langage JavaScript

### 9. Créer le manifeste du module

Créez un manifeste de module PowerShell standard (`.psd1`) avec les métadonnées de votre composant :

```powershell
@{
    RootModule = 'UniversalDashboard.Mermaid.psm1'
    ModuleVersion = '1.0.0'
    Author = 'Your Name'
    Description = 'Custom component description'
    FunctionsToExport = @('New-UDMermaid')
}
```

### 10. Compiler le projet

Vous pouvez maintenant compiler votre projet. Cela produira un module que vous pourrez charger dans PowerShell Universal.

Commencez par ajouter des scripts de compilation à votre fichier `package.json` :

```json
"scripts": {
    "build": "webpack -p --env production",
    "dev": "webpack-dev-server --config webpack.config.js -p --env development"
}
```

Puis lancez la compilation :

```powershell
npm run build
```

**Facultatif : Créer un script de compilation** (comme le fichier `component.build.ps1` de ud-mermaid) :

```powershell
# component.build.ps1
$OutputPath = "$PSScriptRoot\output"

Remove-Item -Path $OutputPath -Force -ErrorAction SilentlyContinue -Recurse
Remove-Item -Path "$PSScriptRoot\public" -Force -ErrorAction SilentlyContinue -Recurse

npm install --legacy-peer-deps
npm run build

New-Item -Path $OutputPath -ItemType Directory

Copy-Item $PSScriptRoot\public\*.* $OutputPath
Copy-Item $PSScriptRoot\UniversalDashboard.MyComponent.psd1 $OutputPath
Copy-Item $PSScriptRoot\UniversalDashboard.MyComponent.psm1 $OutputPath
```

Puis exécutez-le :

```powershell
.\component.build.ps1
```

Le processus de compilation :

1. Regroupe tout le code JavaScript/React à l'aide de Webpack
2. Produit des fichiers regroupés avec des noms de hachage (ex. `index.78a6d857.bundle.js`)
3. Copie les fichiers du module (`.psm1`, `.psd1`) dans le répertoire de sortie

### 11. Utiliser dans PowerShell Universal

Dans votre application, chargez votre module et exécutez la fonction.

```powershell
Import-Module .\output\UniversalDashboard.Mermaid.psd1

New-UDApp -Content {
   New-UDMermaid -Diagram @"
graph TD
    A[Start] --> B[Process]
    B --> C[End]
"@
}
```

## Exemple de structure de projet

Voici la structure typique d'un projet de composant personnalisé (tiré de [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid)) :

```
project/
├── Components/
│   ├── index.js              # Component registration
│   └── mermaid.jsx           # React component
├── output/                   # Build output (git ignored)
│   ├── index.[hash].bundle.js
│   ├── UniversalDashboard.Mermaid.psm1
│   └── UniversalDashboard.Mermaid.psd1
├── package.json              # Node.js dependencies
├── webpack.config.js         # Webpack configuration
├── component.build.ps1       # Optional build script
├── UniversalDashboard.Mermaid.psm1   # PowerShell module
└── UniversalDashboard.Mermaid.psd1   # Module manifest
```

## Props

Les props sont des valeurs qui sont soit transmises depuis la table de hachage PowerShell fournie par l'utilisateur, soit par la fonction d'ordre supérieur `withComponentsFeature` de Universal App.

### Standard

Les propriétés que vous définissez dans votre table de hachage PowerShell seront automatiquement transmises comme props au composant React.

Par exemple, si vous définissez les propriétés `diagram` et `config` dans la table de hachage :

```powershell
function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        type = "ud-mermaid"
        isPlugin = $true
        assetId = $AssetId 
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

Vous aurez alors accès à ces props dans React :

```javascript
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
  }, [diagram, config]);

  return <div className="mermaid" id={id}>{diagram}</div>;
};

export default withComponentFeatures(UDMermaid);
```

**Bonnes pratiques pour les props :**

* Utilisez des noms de props descriptifs correspondant aux noms des paramètres PowerShell
* Gérez les props facultatifs avec des valeurs par défaut ou une logique conditionnelle
* Les tables de hachage PowerShell deviennent automatiquement des objets JavaScript

### Hooks React et cycle de vie des composants

Lors de la création de composants personnalisés, vous pouvez utiliser tous les hooks React standard. Le composant ud-mermaid illustre l'utilisation de `useEffect` et `useRef` pour gérer le cycle de vie du composant et les références DOM :

```javascript
import React, { useEffect, useRef } from 'react';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { diagram, config } = props;

  // Run when diagram or config changes
  useEffect(() => {
    mermaid.initialize(config || {});
    
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]); // Dependency array

  return <div ref={mermaidRef}>{diagram}</div>;
};
```

**Modèles courants :**

* `useEffect` - Pour l'initialisation, le nettoyage et la réponse aux changements de props
* `useRef` - Pour accéder directement aux éléments DOM
* `useState` - Pour gérer l'état interne du composant
* `useMemo` / `useCallback` - Pour l'optimisation des performances

### Points de terminaison

Les points de terminaison sont particuliers dans la façon dont ils sont enregistrés et transmis comme props à votre composant. Vous devez appeler `Register` sur le point de terminaison dans PowerShell et transmettre les variables Id et PSCmdlet.

```powershell
function New-UD95Button {
    param(
        [Parameter()]
        [string]$Id = [Guid]::NewGuid(),
        [Parameter()]
        [string]$Text,
        [Parameter()]
        [Endpoint]$OnClick
    )

    if ($OnClick)
    {
        $OnClick.Register($Id, $PSCmdlet)
    }

    @{
        type = "ud95-button"
        isPlugin = $true 
        assetId = $AssetId

        id = $Id 
        text = $Text 
        onClick = $OnClick
    }
}
```

Les points de terminaison sont créés à partir de ScriptBlocks et sont exécutés lorsque l'événement correspondant se produit.

```powershell
New-UD95Button -Text 'Hello' -OnClick {
    Show-UDToast -Message 'Test' 
}
```

Universal connectera automatiquement le point de terminaison à une fonction JavaScript. Cela signifie que vous pouvez utiliser les props pour appeler ce point de terminaison.

Remarquez l'appel de la fonction `props.onClick`. Cela appellera automatiquement le bloc de script PowerShell sur le serveur.

```javascript
import React from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import { Button } from 'react95';

const UD95Button = props => {

    const p = {
        onClick: () => props.onClick()
    }

    return <Button {...p}>{props.text}</Button>
}

export default withComponentFeatures(UD95Button);
```

### setState

La prop `setState` est utilisée pour définir l'état du composant. Cela garantit que l'état est suivi et que votre composant fonctionnera avec `Get-UDElement`.

Par exemple, avec un champ de texte, vous souhaiterez appeler `props.setState` et transmettre la nouvelle valeur de texte pour l'état.

```javascript
const UDTextField = (props) => {
    const onChange = (e) => {
        props.setState({value: e.target.value})
    }

    return <TextField  {...props} onChange={onChange} />
}

export default withComponentFeatures(UDTextField);
```

### children

La prop `children` est une prop React standard. Si votre composant prend en charge des éléments enfants, comme une liste ou une boîte de sélection, vous devez utiliser la prop standard `props.children` pour vous assurer que les cmdlets `Add-UDElement`, `Remove-UDElement` et `Clear-UDElement` fonctionnent correctement.

## Résolution des problèmes et débogage

### Problèmes courants

**Le composant ne s'affiche pas :**

1. Vérifiez que la propriété `type` dans votre fonction PowerShell correspond au nom utilisé dans `UniversalDashboard.register()`
2. Vérifiez que la ressource est correctement enregistrée auprès de l'AssetService
3. Assurez-vous que `isPlugin` est défini à `$true`
4. Confirmez que le fichier JavaScript regroupé existe dans le répertoire du module

**Les props ne sont pas transmis correctement :**

1. Vérifiez que les noms de propriétés correspondent entre la table de hachage PowerShell et le composant React
2. Vérifiez la console du navigateur pour détecter les erreurs JavaScript
3. Utilisez React DevTools pour inspecter les props du composant

**Échecs de compilation :**

1. Exécutez `npm install --legacy-peer-deps` pour vous assurer que les dépendances sont installées
2. Vérifiez les erreurs de syntaxe dans les fichiers JSX
3. Vérifiez que les externals de webpack.config.js sont correctement configurés
4. Assurez-vous que babel est correctement configuré pour la transformation JSX

### Conseils de débogage

**Console du navigateur :** Ouvrez les outils de développement du navigateur (F12) pour voir les erreurs et avertissements JavaScript.

**React DevTools :** Installez l'extension de navigateur React DevTools pour inspecter la hiérarchie des composants et les props.

**Débogage PowerShell :** Utilisez `Write-Host` ou `Write-Debug` dans vos fonctions PowerShell pour tracer l'exécution.

**Webpack Dev Server :** Lors du développement, utilisez webpack-dev-server pour le rechargement à chaud :

```powershell
npm run dev
```

Configurez ensuite PowerShell Universal pour charger depuis l'URL du serveur de développement.

## Exemple : flux de travail complet d'un composant

Voici un exemple complet basé sur le projet [ud-mermaid](https://github.com/ironmansoftware/ud-mermaid) :

**1. Créer le composant React** (`Components/mermaid.jsx`) :

```jsx
import React, { useEffect, useRef } from 'react';
import { withComponentFeatures } from 'universal-dashboard';
import mermaid from 'mermaid';

const UDMermaid = (props) => {
  const mermaidRef = useRef(null);
  const { id, diagram, config } = props;

  useEffect(() => {
    mermaid.initialize(config || {});
    if (mermaidRef.current) {
      mermaidRef.current.removeAttribute('data-processed');
      mermaid.contentLoaded();
    }
  }, [diagram, config]);

  return <div className="mermaid" id={id} ref={mermaidRef}>{diagram}</div>;
};

export default withComponentFeatures(UDMermaid);
```

**2. Enregistrer le composant** (`Components/index.js`) :

```javascript
import UDMermaid from './mermaid';
UniversalDashboard.register("ud-mermaid", UDMermaid);
```

**3. Créer la fonction PowerShell** (`UniversalDashboard.Mermaid.psm1`) :

```powershell
Get-ChildItem "$PSScriptRoot\*.js" | ForEach-Object {
    $Item = [UniversalDashboard.Services.AssetService]::Instance.RegisterAsset($_.FullName)
    if ($_.Name.StartsWith("index.") -and $_.Name.EndsWith(".bundle.js")) {
        $AssetId = $Item
    }
}

function New-UDMermaid {
    param(
        [Parameter()]
        [string]$Id = (New-Guid).ToString(),
        [Parameter(Mandatory)]
        [string]$Diagram,
        [Parameter()]
        [hashtable]$Config
    )

    @{
        assetId = $AssetId 
        isPlugin = $true 
        type = "ud-mermaid"
        id = $Id
        diagram = $Diagram
        config = $Config
    }
}
```

**4. Compiler et tester** :

```powershell
# Build
npm run build

# Test in PowerShell Universal
Import-Module .\output\UniversalDashboard.Mermaid.psd1

New-UDApp -Content {
    New-UDMermaid -Diagram @"
graph TD
    A[Christmas] -->|Get money| B(Go shopping)
    B --> C{Let me think}
    C -->|One| D[Laptop]
    C -->|Two| E[iPhone]
    C -->|Three| F[Car]
"@
}
```

## Ressources supplémentaires

* **Référentiel GitHub ud-mermaid** : <https://github.com/ironmansoftware/ud-mermaid> - Exemple fonctionnel complet
* **Documentation React** : <https://react.dev>
* **Documentation Webpack** : <https://webpack.js.org>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.devolutions.net/powershell-universal/fr/apps/components/custom-components/building-custom-components.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
