Audit complet de mise en forme — Bouton (3e élément du tableau)

Implémente toutes les options manquantes identifiées pour l'élément
Bouton : typographie (gras/italique/transformation/police/taille/
espacement des lettres/couleur), icône SVG optionnelle avec position
(avant/après le texte) et taille réglables — au-delà de l'Étiquette,
qui n'a qu'une icône fixe —, tous les attributs de boîte partagés déjà
créés pour Titre/Paragraphe/Image, et un effet visuel universel au
survol/clic.

Deux ajouts transversaux demandés pendant le test : un réglage "les 4
côtés de la bordure à la fois" dans le module de boîte partagé
(bénéficie automatiquement à tous les éléments qui l'utilisent), et le
glisser-déposer d'un élément du canevas vers une autre page via le
panneau Pages (le mécanisme serveur existait déjà pour la pagination
automatique, seule l'interaction manuelle manquait).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-26 12:47:46 +02:00
co-authored by Claude Sonnet 5
parent 9ad50c58b8
commit 8f879f6321
8 changed files with 529 additions and 16 deletions
+126 -7
View File
@@ -692,17 +692,136 @@ def test_image_box_style_targets_the_link_wrapper_when_no_caption(tmp_support_sl
)
def test_bouton_default_attributes_include_empty_attachment_fields(tmp_support_slug_cleanup: Any) -> None:
def test_bouton_default_attributes_include_all_new_formatting_fields(tmp_support_slug_cleanup: Any) -> None:
"""Audit du 26/09/2026 (réglages manquants — colonne "Bouton") : chaque
nouveau champ doit exister avec une valeur "neutre" par défaut
(comportement historique inchangé, voir _render_button)."""
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
element = doc_engine.get_document_element(slug, element_id)
assert element is not None
assert element["attributes"] == {
"label": "Bouton",
"target": "",
"attachment_stored_name": "",
"attachment_filename": "",
}
attrs = element["attributes"]
assert attrs["label"] == "Bouton"
assert attrs["target"] == ""
assert attrs["attachment_stored_name"] == ""
assert attrs["attachment_filename"] == ""
assert attrs["bold"] is False
assert attrs["italic"] is False
assert attrs["text_transform"] == "none"
assert attrs["font_family"] == ""
assert attrs["font_size"] == ""
assert attrs["letter_spacing"] == ""
assert attrs["text_color"] == ""
assert attrs["svg_markup"] == ""
assert attrs["icon_position"] == "before"
assert attrs["icon_size"] == ""
assert attrs["padding"] == ""
assert attrs["align_self"] == "stretch"
assert attrs["border"] == doc_engine_box_default_border()
html = doc_engine.render_document([element])
assert html == (
f'<button type="button" class="docButton" data-element-id="{element_id}" '
f'data-kind="bouton"><span class="docButtonLabel">Bouton</span></button>'
)
def test_bouton_bold_and_italic_render_without_lowering_default_weight(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
doc_engine.update_document_element_attributes(slug, element_id, {"label": "X", "bold": True, "italic": True})
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "font-weight:800;" in html
assert "font-style:italic;" in html
def test_bouton_typography_attributes_render_when_set(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
doc_engine.update_document_element_attributes(
slug,
element_id,
{
"label": "X",
"text_transform": "uppercase",
"font_family": "Georgia, serif",
"font_size": "18px",
"letter_spacing": "0.05em",
"text_color": "#ffffff",
},
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "text-transform:uppercase;" in html
assert "font-family:Georgia, serif;" in html
assert "font-size:18px;" in html
assert "letter-spacing:0.05em;" in html
assert "color:#ffffff;" in html
def test_bouton_icon_renders_before_by_default_and_after_when_set(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
svg = '<svg><circle cx="1" cy="1" r="1"/></svg>'
doc_engine.update_document_element_attributes(slug, element_id, {"label": "X", "svg_markup": svg})
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert html.index("docButtonIcon") < html.index("docButtonLabel")
doc_engine.update_document_element_attributes(
slug, element_id, {"label": "X", "svg_markup": svg, "icon_position": "after"}
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert html.index("docButtonLabel") < html.index("docButtonIcon")
def test_bouton_icon_size_sets_explicit_width_and_height(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
doc_engine.update_document_element_attributes(
slug, element_id, {"label": "X", "svg_markup": "<svg></svg>", "icon_size": "24px"}
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert 'style="width:24px; height:24px;"' in html
def test_bouton_svg_markup_is_sanitized(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
malicious = '<svg onload="alert(1)"><script>alert(2)</script></svg>'
doc_engine.update_document_element_attributes(slug, element_id, {"label": "X", "svg_markup": malicious})
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "<script>" not in html
assert "onload" not in html
def test_bouton_box_style_attributes_render_via_shared_box_style(tmp_support_slug_cleanup: Any) -> None:
slug, page_id = _new_support(tmp_support_slug_cleanup)
element_id = doc_engine.add_document_element(slug, "bouton", page_id=page_id)
border = doc_engine_box_default_border()
border["bottom"] = {"style": "dashed", "width": "3px", "color": "#123456"}
doc_engine.update_document_element_attributes(
slug,
element_id,
{
"label": "X",
"padding": "12px 20px",
"margin": "5px",
"background_color": "#fafafa",
"border_radius": "4px",
"align_self": "center",
"border": border,
"box_shadow": "0 4px 12px rgba(0,0,0,.2)",
"opacity": "0.9",
},
)
html = doc_engine.render_document(doc_engine.list_document_elements(slug, page_id))
assert "padding:12px 20px;" in html
assert "margin:5px;" in html
assert "background-color:#fafafa;" in html
assert "border-radius:4px;" in html
assert "align-self:center;" in html
assert "border-bottom:3px dashed #123456;" in html
assert "box-shadow:0 4px 12px rgba(0,0,0,.2);" in html
assert "opacity:0.9;" in html
def test_replace_document_content_rebuilds_pages_and_elements_from_seed(tmp_support_slug_cleanup: Any) -> None: