diff --git a/screens/rendering/render_element_html.py b/screens/rendering/render_element_html.py index a569f8c0..240df2df 100644 --- a/screens/rendering/render_element_html.py +++ b/screens/rendering/render_element_html.py @@ -56,25 +56,34 @@ def render_element_html(el, slug=None, children_map=None, ctx=None, parent_flex_ bound_ctx = _resolve_bound_row_ctx(slug, attrs_raw) if bound_ctx: ctx = {**(ctx or {}), **bound_ctx} + # Chaque branche "special_render" passe elle aussi par _mark() : sans + # ça, un widget de ce genre (Icône, Tableau, Superposition...) actuellement + # VISIBLE sous condition ne portait aucun marqueur, et refreshRuntimeData() + # (play.html) ne savait donc jamais qu'il fallait le réévaluer après une + # action "Modifier une donnée/variable" — il restait figé dans son état + # de l'affichage précédent (ex: une icône "enveloppe fermée" restée + # visible après l'ouverture du mail, en même temps que "enveloppe + # ouverte" qui apparaît, elle, correctement puisqu'un élément CACHÉ + # porte toujours son marqueur via le placeholder ci-dessus). special = meta.get("special_render") if special == "table": - return _render_table(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_table(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "select_options": - return _render_select(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_select(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "fieldset_legend": - return _render_fieldset(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_fieldset(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "checkbox_label": - return _render_checkbox_or_radio(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_checkbox_or_radio(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "repeater": - return _render_repeater(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_repeater(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "jauge": - return _render_jauge(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_jauge(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "overlay": - return _render_overlay(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_overlay(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "onglets": - return _render_onglets(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_onglets(el, meta, slug, children_map, ctx, parent_flex_direction)) if special == "icone": - return _render_icone(el, meta, slug, children_map, ctx, parent_flex_direction) + return _mark(_render_icone(el, meta, slug, children_map, ctx, parent_flex_direction)) tag = meta.get("tag") if tag is None: # widget à balise dynamique (ex: "titre" -> h1/h2/h3) diff --git a/tests/test_visibility_condition.py b/tests/test_visibility_condition.py index 9b4cc325..7ed63bc4 100644 --- a/tests/test_visibility_condition.py +++ b/tests/test_visibility_condition.py @@ -79,6 +79,30 @@ def test_objet_mode_uses_most_recent_row_outside_a_repeater(client, game): assert "display:none" not in _rendered_html(client, game) +def test_visible_special_render_widget_carries_refresh_marker(client, game): + """Régression : une icône (special_render, pas le chemin générique) + actuellement VISIBLE sous condition doit quand même porter le + marqueur "visibilityGated" — sinon refreshRuntimeData() (play.html) ne + sait jamais qu'il faut la réévaluer après une action "Modifier une + donnée/variable", et elle reste figée dans son état précédent. Cas + concret remonté : une icône "enveloppe fermée" (visible tant que + is_opened=0) reste affichée EN MÊME TEMPS que l'icône "enveloppe + ouverte" (qui, elle, se met à jour normalement puisqu'un élément + CACHÉ porte toujours son marqueur via son placeholder).""" + screen_id = _create_screen(client, game) + element_id = _add_element(client, game, screen_id, "icone") + client.post(f"/game/{game}/variables/new", data={"name": "porte_ouverte", "type": "booleen", "default_value": "0"}) + client.post(f"/game/{game}/elements/{element_id}/save", data={ + "ctrl_visibilite_cond_mode": "variable", + "ctrl_visibilite_cond_variable": "porte_ouverte", + "ctrl_visibilite_cond_operateur": "egal", + "ctrl_visibilite_cond_valeur": "0", + }) + html = _rendered_html(client, game) + assert "display:none" not in html + assert "visibilityGated" in html + + def test_objet_mode_inside_a_repeater_evaluates_each_row_independently(client, game): """Régression : une "Mail card" (élément de jeu réutilisable posé dans un Répéteur d'e-mails) doit afficher l'icône "ouvert"/"fermé" qui