Skip to content

epic(carto): rapport de recette 2026-08-19 — 14 bugs post-refonte (#472) #482

Description

@bmatge

Rapport de recette utilisateur (Bertrand, 2026-08-19) sur chartsbuilder.miweb.run/apps/builder-carto/ — build assets/index-ZDK0AVh4.js (post PR #472, avant PR #481).

14 bugs — 4 bloquants, 3 majeurs. Le triage indique où le fix doit atterrir.

Triage par responsabilité

🔴 Bloquants

# Titre Où fixer
1 Sources Grist : la réponse API n'est jamais aplatie (records lus au niveau 1 → id + fields seuls, les 25 colonnes réelles invisibles) packages/core — adapter Grist / <dsfr-data-source>
2 Auto-détection : lat/lon remplis avec les chaînes littérales latitude/longitude/fields (colonnes inexistantes) À reproduire d'abord sur state frais (bouton « Repartir de zéro ») — sinon builder-carto
3 Exception JS non catchée Invalid GeoJSON object à chaque rendu de couche geoshape packages/coredsfr-data-map-layer._addGeoshape
4 Mode Chaleur : couche rendue mais quasi invisible (alpha max ≈ 5%) packages/core — normalisation max de leaflet.heat

🟠 Majeurs

# Titre Où fixer
5 « Essayer avec un jeu d'exemple » donne carte vide (default type=Zones incompatible avec les 13 points sans géométrie) apps/builder-carto — forcer marker sur exemple
6 Résidus de clustering (bulles) restent affichés après switch vers Cercles packages/coredsfr-data-map-layer cleanup on type change
9 Panneau « Carte » replié : en-tête rogné / recouvert par « Couches » apps/builder-carto — CSS panneaux (scroll manquant)
11 Valeur = placeholder : impossible de distinguer « vide » de « rempli avec valeur bidon » (aggrave le #2) apps/builder-carto

🟡 Mineurs / 🟢 Cosmétique

# Titre Où fixer
7 Compteur « N éléments » faux + faute d'accord (« 1 éléments ») apps/builder-carto
8 Message erreur trompeur (pointe la localisation quand c'est la représentation qui bloque) apps/builder-carto
10 Flèche ▼ datalist sans effet au clic (comportement natif <input list>) apps/builder-carto — pattern datalist custom
12 id="inset-corse" dupliqué dans le panneau Territoires apps/builder-carto — render map-config
13 Select « Palette » : libellé tronqué apps/builder-carto — CSS
14 Bande grise en bas après zoom (map.invalidateSize() manquant) packages/core (probable) ou apps/builder-carto

Rapport complet


Rapport de recette — Générateur de cartes (builder-carto)

URL testée : https://chartsbuilder.miweb.run/apps/builder-carto/index.html
Date : 19/08/2026 · Navigateur : Chrome · Viewport : 1115×899
Build : assets/index-ZDK0AVh4.js, assets/leaflet-src-D6KLyPF5-HbUv2tdu.js

⚠️ La session de test a été interrompue (perte du pont navigateur) avant la fin. La liste des points non testés figure en fin de document.

🔴 1 — Sources Grist : la réponse API n'est jamais « aplatie »

Symptôme : avec la source Baromètre / Territoires (Grist), l'app affiche 108 enregistrements chargés mais aucun élément dessiné et 2 champs détectés, alors que la table contient 25 colonnes.

Cause racine (vérifiée) : l'API Grist renvoie

{ "records": [ { "id": 1, "fields": { "nom": …, "lat": …, "lon": …, "geojson": … } } ] }

Le scan de champs du builder lit les clés de premier niveau (id, fields) et ne déballe jamais fields. Preuve : toutes les <datalist> d'autocomplétion ne contiennent que ["fields", "id"] :

[...document.querySelectorAll('datalist')].map(d => [d.id, [...d.options].map(o=>o.value)])
// layer-lat-list        → ["id"]
// layer-geo-field-list  → ["fields","id"]
// layer-tooltip-list    → ["fields","id"]

Conséquence : aucune source Grist n'est utilisable. Renseigner manuellement lat/lon ne suffit pas non plus — les lignes transmises à la couche restent {id, fields:{…}}.

Note : la source mise en cache dans localStorage.dsfr-data-sources contient, elle, des lignes déjà aplaties. Il y a donc deux chemins de chargement incohérents (cache vs. fetch direct) — à unifier.

🔴 2 — Auto-détection : valeurs littérales latitude / longitude / fields

Symptôme : le bandeau vert annonce « Champs détectés automatiquement — modifiez si besoin », mais les champs contiennent des noms de colonnes inexistants :

document.getElementById('layer-lat').value       // "latitude"   (colonne réelle : "lat")
document.getElementById('layer-lon').value       // "longitude"  (colonne réelle : "lon")
document.getElementById('layer-geo-field').value // "fields"     (colonne réelle : "geojson")

Le fallback de détection écrit des valeurs par défaut au lieu de laisser vide, et choisit fields (clé d'enveloppe Grist) comme champ géométrique.

Attendu : ne rien pré-remplir quand la détection échoue, et afficher un état « champs non détectés » plutôt qu'un message de succès.

🔴 3 — Exception JS non interceptée Invalid GeoJSON object

Symptôme : à chaque rendu de couche, exception non catchée dans la console.

Error: Invalid GeoJSON object.
  at t.addData (leaflet-src…js)
  at K._addGeoshape (index-ZDK0AVh4.js:1370:12299)
  at K._renderLayer (index-ZDK0AVh4.js:1370:10666)
  at K.onSourceData / K._onMapReady

Elle se déclenche 2 à 3 fois par rendu (chargement source + init carte). Le rendu de la couche est interrompu silencieusement côté utilisateur.

Attendu : try/catch autour de _addGeoshape, ligne ignorée + message explicite du type « la colonne X ne contient pas de géométrie valide (n lignes ignorées) ».

🔴 4 — Mode « Chaleur » : couche rendue mais invisible

Repro : jeu d'exemple (13 chefs-lieux) → Éléments → Chaleur.

Symptôme : aucune tache de chaleur visible, à aucun zoom. Le canvas existe pourtant et contient des pixels :

const cv = document.querySelector('canvas.leaflet-heatmap-layer');
// 54 924 pixels non transparents… mais alpha MAX = 13 / 255  (≈ 5 %)

Passer le Rayon de 25 à 60 ne change rien → le problème vient de la normalisation d'intensité (max de leaflet.heat manifestement calé beaucoup trop haut), pas du rayon.

Statut affiché : 1 éléments affichés (13 enregistrements) (voir bug #7).

🟠 5 — « Essayer avec un jeu d'exemple » produit une carte vide

Repro : Couches → ChangerEssayer avec un jeu d'exemple.

Symptôme : 13 enregistrements chargés mais aucun élément dessiné.

Cause : la représentation par défaut est Zones, or le jeu d'exemple ne contient que lat/lon (pas de géométrie). Le mode « découverte » censé être le premier contact ne montre donc rien. Basculer sur Marqueurs fait apparaître les 13 points immédiatement.

Attendu : le jeu d'exemple doit forcer une représentation compatible (Marqueurs), ou l'app doit détecter l'absence de géométrie et basculer/avertir.

🟠 6 — Résidus de clustering après changement de représentation

Repro :

  1. Jeu d'exemple → Marqueurs → cocher « Regrouper les points proches » → dézoomer (2 crans)
  2. Basculer sur Cercles

Symptôme : les deux bulles de cluster (« 3 » et « 2 ») restent affichées par-dessus les cercles ; l'option clustering n'est pourtant plus proposée dans ce mode.

document.querySelectorAll('.leaflet-marker-icon').length  // 2  ← résidus
document.querySelectorAll('path.leaflet-interactive').length // 8 ← cercles

L'ancienne couche de clusters n'est pas retirée de la carte lors du changement de représentation.

🟡 7 — Compteur « éléments affichés » faux et mal accordé

Contexte Affiché Attendu
Marqueurs + clustering 10 éléments affichés (13 enregistrements) 13 (les bulles ne sont pas des éléments)
Cercles (avec résidus) 10 éléments affichés (13 enregistrements) 13
Chaleur 1 éléments affichés (13 enregistrements) 13 — et « 1 élément » au singulier

🟡 8 — Message d'erreur trompeur

108 enregistrements chargés mais aucun élément dessiné — vérifiez la localisation (panneau Couches).

En mode Zones avec des données ponctuelles, la localisation est correcte : c'est la représentation qui est incompatible. Le message envoie l'utilisateur au mauvais endroit. Aucun avertissement du type « Zones nécessite un champ géométrie ».

🟠 9 — Panneau « Carte » replié : en-tête rogné par le panneau « Couches »

Repro : replier le panneau Carte (clic sur son en-tête).

Symptôme : la moitié basse de l'en-tête « Carte » (et son texte) passe sous le panneau « Couches ». Mesures :

button.carto-panel__header  "Carte"   → y = 73,  h = 46   (donc 73 → 119)
panneau "Couches"                     → y = 116          ← chevauchement

Le chevauchement apparaît dès que la hauteur cumulée des trois panneaux dépasse la place disponible : la colonne déborde au lieu de scroller, et le premier panneau est poussé sous l'en-tête applicatif.

🟡 10 — Flèche ▼ du champ géographique sans effet

Le champ « Champ géographique » affiche une flèche de liste déroulante. Un clic dessus n'ouvre aucune liste (l'input list= natif n'ouvre pas au clic sur l'icône dessinée). Même comportement à prévoir sur les autres champs à datalist.

🟠 11 — Valeur identique au placeholder → champ qui paraît vide

Les inputs Latitude / Longitude affichent latitude / longitude en valeur, strictement identiques à leur propre placeholder. Visuellement impossible de distinguer « vide » de « rempli avec une valeur bidon ». C'est ce qui rend le bug #2 quasi indétectable pour l'utilisateur.

Attendu : ne jamais utiliser comme valeur par défaut une chaîne égale au placeholder ; distinguer typographiquement valeur et placeholder.

🟡 12 — id HTML dupliqué : inset-corse

Le panneau Carte → Territoire par territoire contient deux <input> avec id="inset-corse" :

const ids=[...document.querySelectorAll('[id]')].map(e=>e.id);
ids.filter((v,i)=>ids.indexOf(v)!==i)  // ["inset-corse"]

Conséquences : le <label for="inset-corse"> de la seconde case pointe sur la première (clic sans effet), et le DOM est invalide (a11y / RGAA).

🟢 13 — Select « Palette » : libellé tronqué

Séquentielle (clair → foncé) – — l'option est coupée, la largeur du <select> est insuffisante pour les libellés les plus longs.

🟢 14 — Bande de tuiles non chargées en bas de carte

Après un zoom, une bande grise d'environ 20 px persiste en bas du conteneur (x ≈ 360→860). map.invalidateSize() manquant après redimensionnement du conteneur.

Non testé (session interrompue)

  • Comportements « Au clic sur un élément » : infobulle, popup, panneau latéral G/D
  • Colorer par catégorie (champ region)
  • Animation temporelle (champ date)
  • Options avancées : filtre champ:opérateur:valeur, nombre max d'éléments
  • Multi-couches : ajouter / masquer / supprimer / réordonner, « Repartir de zéro »
  • Panneau Carte : fonds de carte, insets DROM, fit-bounds, locked, no-controls, sovereign-only, hauteur exportée, max-bounds
  • Obtenir le code, Exécuter, Favoris
  • Responsive (petite fenêtre / mobile) et navigation clavier

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpkg:coreTouche la lib packages/core (composants dsfr-data-*)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions