Audit complet de mise en forme — Liste à puces/numérotée (4e élément)

Implémente toutes les options manquantes identifiées pour les listes :
typographie complète, style/position/couleur/taille de puce (validés
selon le kind), puce personnalisée en SVG pour les listes à puces,
padding uniforme par élément (nouveau, il n'y en avait aucun), espacement
entre éléments réglable, et tous les attributs de boîte partagés sur la
liste entière. Bordure/fond/padding par élément individuel et sous-listes
imbriquées volontairement différés (portée actée avec l'utilisateur
avant implémentation : transformeraient le stockage des éléments en
objets structurés, chantier bien plus lourd).

Trois ajouts transversaux bénéficiant à plusieurs éléments : sections
"Contenu"/"Conteneur" dans tous les panneaux de propriétés, alignement
vertical du contenu dans son bloc (Titre/Paragraphe/Liste/Image
légendée), et une option pour retirer un thème appliqué ("Aucun modèle"
dans la modale, avec une nouvelle fonction db.remove_document_theme).

Quatre bugs réels trouvés et corrigés en chaîne pendant la validation
avec le thème "Sécurité incendie" : un badge de thème s'affichait
au-dessus du texte au lieu d'à côté ; le correctif a d'abord fait
disparaître les puces/numéros natifs de TOUTES les listes (bug plus
grave que celui corrigé) ; puis un marqueur natif redondant apparaissait
à côté du badge du thème ; puis une règle CSS site-large de spécificité
supérieure empêchait silencieusement ce dernier correctif. Chaque étape
vérifiée par navigateur automatisé sur un support jetable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-26 15:13:14 +02:00
co-authored by Claude Sonnet 5
parent 8f879f6321
commit 4db1269348
17 changed files with 1080 additions and 34 deletions
+195 -1
View File
@@ -298,7 +298,163 @@ sinon d'un état neutre. Bénéficie automatiquement à TOUS les éléments
qui utilisent déjà ce module (Titre/Paragraphe/Image/Bouton), aucune
modification nécessaire ailleurs.
### 4. Liste à puces / numérotée — à faire
### 4. Liste à puces / numérotée — ✅ audité et validé (commité)
**Portée actée avec l'utilisateur avant implémentation** (question
posée explicitement, deux catégories du tableau impliquaient de
transformer `items` — une simple liste de chaînes — en objets
structurés) : style sur la LISTE ENTIÈRE uniquement, jamais par élément
individuel ni de sous-listes imbriquées (chantier bien plus lourd,
différé à une demande séparée si besoin). Seule exception actée : un
padding UNIFORME appliqué à chaque élément (`item_padding`), pas encore
réglable individuellement.
Implémenté : gras/italique/souligné, police (liste déroulante web-safe,
`FORGE_DOC_FONT_FAMILY_OPTIONS` réutilisé), taille de police, hauteur de
ligne, couleur du texte, style de puce (`list_style_type`, options
propres à chaque kind — disque/cercle/carré/aucune pour puces,
1-2-3/01-02-03/i-ii-iii/I-II-III/a-b-c/A-B-C/aucune pour numérotée),
position de la puce (intérieure/extérieure), couleur et taille de puce
indépendantes du texte, puce personnalisée (icône SVG, liste à puces
uniquement — encodée en URI de données pour `list-style-image`),
espacement intérieur par élément (`item_padding`, **"6px" par défaut,
retour utilisateur explicite : "il faut un padding de base par élément
de liste car y en a pas aujourd'hui"** — n'existait pas du tout avant),
espacement entre éléments réglable (`item_spacing`), tous les attributs
de boîte partagés sur la liste entière (padding/margin/fond/bordure/
ombre/opacité/position du bloc).
Mécanisme technique notable : `marker_color`/`marker_size`/
`item_padding`/`item_spacing` ne peuvent pas passer par un style inline
classique sur le `<ul>`/`<ol>` (impossible de cibler le `::marker` ou le
padding des `<li>` enfants depuis le style de leur parent) — résolu via
des propriétés personnalisées CSS (`--doc-marker-color` etc.), posées
en inline sur le conteneur et consommées par une règle CSS dédiée
(`.docList li`/`.docList li::marker`) qui en hérite. Même technique déjà
utilisée pour la position du bloc des éléments enveloppés (Image).
Volontairement laissé de côté (portée actée ci-dessus) : style/bordure/
fond par élément individuel, sous-listes imbriquées — nécessiteraient
de transformer `items` (liste de chaînes) en objets structurés, refonte
du panneau et du stockage. Comme les autres éléments : responsive par
taille d'écran.
**Existant retroactivement inchangé** : une liste déjà créée avant ce
commit garde ses anciens attributs (juste `items`) — `item_padding`
n'apparaît en style inline QUE pour les nouvelles listes ; ouvrir le
panneau Propriétés d'une ancienne liste et toucher un réglage la fait
bénéficier des nouveaux défauts au passage.
**Bug réel corrigé pendant le test (retour utilisateur : "si j'enlève
les puces ou que les puces se mettent à l'intérieur, il reste un
espace devant la liste, cet espace doit être supprimé")** : le
`padding-left:1.4em` par défaut (réservé pour une puce EXTÉRIEURE)
n'a plus lieu d'être dès que `list_style_type="none"` ou
`list_style_position="inside"` — `_render_list` ajoute alors
automatiquement `padding-left:0;`, prioritaire sur le CSS mais toujours
cédant la place à un `padding` uniforme réglé explicitement par
ailleurs (attributs de boîte partagés).
**Deux ajouts transversaux pendant le test, bénéficiant à plusieurs
éléments à la fois :**
1. **Sections "Contenu"/"Conteneur" dans le panneau Propriétés (retour
utilisateur : "il faut distinguer par des sections la propriété qui
touche au conteneur de celles qui touchent à l'élément qu'il
contient sinon c'est pas compréhensible")** : un sous-titre visuel
(bordure du dessus) sépare maintenant, dans CHAQUE panneau qui
utilise `box_style.py` (Titre/Paragraphe, Image, Bouton, Liste), les
champs propres au CONTENU (texte, icône, puces...) de ceux qui
touchent au CONTENEUR (padding/margin/fond/bordure/ombre/opacité/
position du bloc/alignement du contenu — voir point 2). Le sous-titre
"Conteneur" est posé UNE SEULE FOIS, à l'intérieur de
`forgeDocRenderBoxFieldsHtml` (le module déjà partagé), jamais dupliqué
à chaque appelant.
2. **Alignement vertical du CONTENU dans son bloc (retour utilisateur :
"je peux augmenter la hauteur d'un conteneur mais pas l'alignement
vertical à l'intérieur, pour les listes et il faut aussi vérifier
pour les autres")** : nouvel attribut partagé `content_align`
(`"top"` par défaut, `"center"`/`"bottom"`) posé dans `BOX_DEFAULTS`,
rendu via `render_content_align(a)` — volontairement PAS fusionné
dans `render_box_style` (l'alignement du contenu dépend de l'axe
interne du conteneur : `justify-content` convient à un conteneur en
COLONNE — Titre/Paragraphe/Liste, rendus `display:flex;
flex-direction:column;` pour l'occasion, et la figure d'une image
légendée, déjà flex-colonne — mais serait FAUX pour le Bouton, une
RANGÉE icône+texte qui gère déjà son axe vertical via `align-items`,
déjà correct sans réglage). Contrôle exclu explicitement du panneau
Bouton (`includeContentAlign: false`) pour ne jamais afficher un
réglage sans effet.
**Bug réel corrigé, puis CORRIGÉ UNE SECONDE FOIS après un retour de
régression (capture à l'appui, thème "Sécurité incendie")** :
1. *Premier symptôme* ("dans les liste numéroté [...] la position des
élément à l'intérieur de base est verticale au lieu d'être
horizontale") : ce thème remplace la puce native d'une liste par un
badge (`::before`, voir `static/document/themes/
securite-incendie.css`), pensé pour s'afficher À CÔTÉ du texte.
Corrigé une première fois en posant `display:flex` sur `.docList li`
(`static/document/document-editor.css`).
2. *Régression introduite par ce premier correctif* ("quand j'enlève le
thème [...] on voit plus les puces ou les numéros") : `display:flex`
posé DIRECTEMENT sur le `<li>` remplace entièrement son
`display:list-item` natif — ça supprime le marqueur natif (puce/
numéro) pour TOUTE liste, avec ou sans thème (l'affirmation inverse
dans le premier correctif était FAUSSE, corrigée après une
vérification en conditions réelles, navigateur automatisé). **Corrigé
en ciblant le `::before` LUI-MÊME** (`display:inline-block;
vertical-align:middle; margin-right:10px;`), jamais son parent : le
`<li>` garde son `display:list-item` natif (donc son marqueur), et le
badge s'aligne quand même à côté du texte qui le suit dans le flux
normal.
3. *Redondance restante* : une fois le marqueur natif restauré, les
listes du thème affichaient À LA FOIS leur badge personnalisé ET le
marqueur natif (disc/decimal) en double — le thème ne les avait
jamais explicitement désactivés (aucun `list-style:none` dans
`securite-incendie.css`, il comptait implicitement sur le marqueur
natif pour disparaître tout seul). Corrigé en ajoutant ce reset —
avec le sélecteur d'élément (`ul.docList[...]`/`ol.docList[...]`),
jamais seulement les classes/attributs : `static/style.css` (site
large) porte une règle `.content ol:not([type]) {
list-style-type: decimal; }` d'une spécificité légèrement supérieure
qui l'emportait sinon silencieusement.
4. *Dernière régression du même correctif* : le badge numéroté du thème
posait lui-même `display:flex` (pour centrer son chiffre) — une
valeur qui BLOCKIFIE le `::before` (le repousse hors du flux en
ligne, au-dessus du texte), défaisant le point 1 pour ce cas précis.
Corrigé en `display:inline-flex` (garde le badge en ligne tout en
centrant quand même le chiffre à l'intérieur).
Chaque étape vérifiée par un navigateur automatisé (Playwright) contre
le serveur local, sur un support jetable créé puis supprimé pour
l'occasion — thème appliqué et retiré successivement, capture d'écran
et styles calculés (`getComputedStyle`) inspectés à chaque fois, pas
seulement supposés corrects.
**Audit final** : chaque ligne du tableau d'audit initial pour l'élément
Liste est couverte —
**Typo** (police/taille/gras/italique/souligné/couleur/interligne) ✅,
**Puces/numéros** (style de puce validé par kind, image de puce
personnalisée pour les puces, position intérieure/extérieure, couleur/
taille de puce indépendantes du texte via propriétés personnalisées
CSS) ✅,
**Boîte** (padding/margin sur la liste entière via `box_style.py`,
padding UNIFORME par élément — `item_padding`, avec un vrai défaut non
vide comme demandé —, espacement entre éléments réglable —
`item_spacing` —, indentation couverte par le padding partagé) ✅,
**Bordure/fond** sur la liste entière ✅.
Volontairement différés, portée actée explicitement AVANT
implémentation avec l'utilisateur (question posée, réponse : liste
entière seulement) : bordure/fond/padding/margin **par élément
individuel** (au-delà du padding uniforme) et **listes imbriquées** —
transformeraient `items` (liste de chaînes) en objets structurés, un
chantier bien plus lourd que le reste du tableau, à traiter séparément
si demandé. Comme les autres éléments : responsive par taille d'écran.
245 tests passent, ruff/mypy --strict/bandit/vulture/import-linter/
eslint/stylelint tous clean. Rien d'oublié constaté à cette relecture.
Validé par l'utilisateur (y compris les 4 bugs de thème trouvés et
corrigés pendant la validation), prêt à committer.
### 5. Étiquette (badge) — à compléter
@@ -349,3 +505,41 @@ distinct de `.is-active`/`.is-dragging`. Pas de test automatisé
possible côté client (aucune suite de tests n'existe pour
`document-editor.js`), vérification manuelle uniquement — le mécanisme
serveur sous-jacent, lui, reste couvert par les tests existants.
## Fonctionnalité hors tableau : retirer le thème appliqué
Retour utilisateur du 26/09/2026 : après avoir choisi un thème puis
vidé toutes les pages, le thème restait appliqué — question légitime
("c'est voulu ?"), réponse : OUI pour la séparation contenu/thème (déjà
le cas), mais il manquait un moyen de retirer un thème une fois choisi.
Ajout demandé : "dans la modale de choix des modèles, ajoute-en un qui
s'appelle Aucun modèle, si l'utilisateur le choisit ça enlève tout
modèle de style choisi pour revenir à un document de base".
- **Nouvelle fonction bas niveau** `db.remove_document_theme(slug)`
(`db/supports/remove_document_theme.py`) — supprime la LIGNE `_meta`
plutôt que d'y stocker une chaîne vide, pour que `get_document_theme`
continue de renvoyer `None` (son contrat documenté : "aucun thème
n'a jamais été appliqué"), jamais une chaîne vide qui le violerait
silencieusement pour tout appelant qui compare à `None` (dont le test
déjà existant `test_new_support_has_no_theme_by_default`).
- **Route** `/document/<slug>/theme/apply` : `theme_id` vide retire
désormais le thème et s'arrête là — `mode` n'a alors aucun sens
(aucun contenu de démonstration pour "aucun modèle") et est ignoré,
jamais validé ni utilisé dans ce cas.
- **Modale "Utiliser un modèle"** : nouvelle carte "Aucun modèle"
toujours en tête de liste (même catalogue vide), id sentinelle `""`
— distincte de `null` (qui reste réservé à "rien n'a encore été
cliqué dans la modale", `forgeDocSelectedTemplateId` à l'ouverture).
La sélectionner remplace l'aperçu (rien à prévisualiser, "aucun
modèle" n'a pas de contenu de démonstration) par un message explicite
et un unique bouton "Retirer le modèle" (jamais les deux boutons
"contenu actuel/du modèle", qui supposent un vrai thème choisi) —
confirmation native avant l'action (changement visuel notable, même
si le contenu n'est jamais touché).
- Testé : `db/supports/remove_document_theme.py` (2 tests bas niveau,
`tests/document/test_support_lifecycle.py`) + la route (2 tests,
`tests/document/test_document_routes.py` — retire vraiment le thème,
laisse le contenu intact, ignore `mode`). Le déclenchement côté
client (clic sur la carte/le bouton) reste manuel faute de suite de
tests JS, comme le reste de l'éditeur.