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/core — dsfr-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/core — dsfr-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 → Changer → Essayer 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 :
- Jeu d'exemple → Marqueurs → cocher « Regrouper les points proches » → dézoomer (2 crans)
- 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
Rapport de recette utilisateur (Bertrand, 2026-08-19) sur
chartsbuilder.miweb.run/apps/builder-carto/— buildassets/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
id+fieldsseuls, les 25 colonnes réelles invisibles)packages/core— adapter Grist /<dsfr-data-source>latitude/longitude/fields(colonnes inexistantes)Invalid GeoJSON objectà chaque rendu de couche geoshapepackages/core—dsfr-data-map-layer._addGeoshapepackages/core— normalisationmaxde leaflet.heat🟠 Majeurs
apps/builder-carto— forcer marker sur exemplepackages/core—dsfr-data-map-layercleanup on type changeapps/builder-carto— CSS panneaux (scroll manquant)apps/builder-carto🟡 Mineurs / 🟢 Cosmétique
apps/builder-cartoapps/builder-carto<input list>)apps/builder-carto— pattern datalist customid="inset-corse"dupliqué dans le panneau Territoiresapps/builder-carto— render map-configapps/builder-carto— CSSmap.invalidateSize()manquant)packages/core(probable) ouapps/builder-cartoRapport 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🔴 1 — Sources Grist : la réponse API n'est jamais « aplatie »
Symptôme : avec la source
Baromètre / Territoires(Grist), l'app affiche108 enregistrements chargés mais aucun élément dessinéet2 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 jamaisfields. Preuve : toutes les<datalist>d'autocomplétion ne contiennent que["fields", "id"]:Conséquence : aucune source Grist n'est utilisable. Renseigner manuellement
lat/lonne suffit pas non plus — les lignes transmises à la couche restent{id, fields:{…}}.Note : la source mise en cache dans
localStorage.dsfr-data-sourcescontient, 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/fieldsSymptôme : le bandeau vert annonce « Champs détectés automatiquement — modifiez si besoin », mais les champs contiennent des noms de colonnes inexistants :
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 objectSymptôme : à chaque rendu de couche, exception non catchée dans la console.
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/catchautour 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 :
Passer le Rayon de 25 à 60 ne change rien → le problème vient de la normalisation d'intensité (
maxde 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 → Changer → Essayer 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 :
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.
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é
10 éléments affichés (13 enregistrements)10 éléments affichés (13 enregistrements)1 éléments affichés (13 enregistrements)🟡 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 :
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/longitudeen 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 —
idHTML dupliqué :inset-corseLe panneau Carte → Territoire par territoire contient deux
<input>avecid="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)
region)champ:opérateur:valeur, nombre max d'élémentsfit-bounds,locked,no-controls,sovereign-only, hauteur exportée, max-bounds