Remplace les onglets texte de pages par de vraies vignettes miniatures
Build and deploy / test-python (push) Successful in 7m23s
Build and deploy / test-js (push) Successful in 1m24s
Build and deploy / lint-python (push) Successful in 6m29s
Build and deploy / lint-js (push) Failing after 1m31s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 5m18s

Les vignettes réutilisent le HTML réellement rendu de chaque page
(CSS scale trick) et sont centrées dans le conteneur du milieu, au
lieu d'une barre pleine largeur.

Corrige au passage deux bugs réels trouvés en écrivant les tests :
- une page contenant un mini-jeu (bouton Suivant/Recommencer) cassait
  le parsing HTML car .docPageThumbCard était un <button> englobant
  un autre <button> ; passage en div role="button" + équivalent
  clavier, contenu copié rendu inert.
- le renommage d'une page par double-clic ne fonctionnait plus du
  tout (sélecteur .docPageTab oublié lors du renommage des classes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-21 13:50:05 +02:00
co-authored by Claude Sonnet 5
parent ecd483f352
commit 57c4de3d8a
5 changed files with 273 additions and 125 deletions
+35 -12
View File
@@ -92,18 +92,6 @@
aria-label="Changer de thème"></button>
</div>
</div>
<div class="docPageTabs" id="docPageTabs">
{% for page in pages %}
<button type="button"
class="docPageTab {{ 'is-active' if page.id == active_page.id else '' }}"
data-page-id="{{ page.id }}"
draggable="true">
<span class="docPageTabTitle">{{ page.title }}</span>
<span class="docPageTabDelete" data-page-id="{{ page.id }}" title="Supprimer cette page">✕</span>
</button>
{% endfor %}
<button type="button" class="docPageTabAdd" id="docPageTabAdd" aria-label="Ajouter une page" title="Ajouter une page">+</button>
</div>
<div class="docBodyWrap">
<div class="docSidebarClose" id="docCloseLeft">← Retour au document</div>
<div class="docSidebar docSidebarLeft" id="docSidebarLeft">
@@ -137,6 +125,41 @@
{% endfor %}
</div>
<div class="docCanvasArea" id="docCanvasArea">
<div class="docPageThumbs" id="docPageTabs">
{% for page in pages %}
<div class="docPageThumbCard {{ 'is-active' if page.id == active_page.id else '' }}"
role="button"
tabindex="0"
data-page-id="{{ page.id }}"
draggable="true">
{# //NOSONAR Web:S6819,Web:MouseEventWithoutKeyboardEquivalentCheck - div+role=button
est ICI le bon choix, pas une entorse à l'accessibilité : la vignette englobe le
HTML réel de la page (rendered_pages[page.id]), qui peut contenir de vrais <button>
(Suivant/Recommencer d'un mini-jeu) ; un <button> dans un <button> est invalide et
le parseur HTML referme le bouton englobant trop tôt, corrompant l'arbre DOM (bug
constaté en jsdom ET reproductible dans un vrai navigateur, même algorithme de
parsing — voir CODE_QUALITY.md). L'équivalent clavier (Entrée/Espace) est posé côté
JS sur ces cartes (forgeDocRenderPageTabs), donc le finding clavier est un faux
positif : Sonar ne voit pas les addEventListener attachés dynamiquement. #}
<span class="docPageThumbFrame">
<span class="docPageThumbInner" data-page-id="{{ page.id }}" inert>{{ rendered_pages[page.id]|safe }}</span>
{# //NOSONAR S5247 - rendered_pages[page.id] vient de document_engine.render_document, même garantie que rendered_document plus bas (échappement déjà fait côté Python) #}
{# inert : les vrais <button>/liens éventuels du contenu de mini-jeu copié ici ne doivent jamais être atteignables au clavier (pure prévisualisation figée, pointer-events:none déjà côté CSS pour la souris) #}
<span class="docPageThumbDelete"
data-page-id="{{ page.id }}"
title="Supprimer cette page">✕</span>
</span>
<span class="docPageThumbLabel">{{ page.title }}</span>
</div>
{% endfor %}
<button type="button"
class="docPageThumbCard docPageThumbAddCard"
id="docPageTabAdd"
aria-label="Ajouter une page"
title="Ajouter une page">
<span class="docPageThumbFrame docPageThumbAddFrame">+</span>
</button>
</div>
<div class="docPage" id="docPage">
<div class="docPageContent" id="docPageContent">{{ rendered_document|safe }}</div>
{# //NOSONAR S5247 - rendered_document vient de document_engine.render_document, qui échappe (html.escape) tout contenu utilisateur avant interpolation (voir document_engine/rendering/render_document_element.py) ; jamais de HTML brut non échappé ici #}