Make Jauge, Onglets, and every form-field widget real native Bulma elements
- Jauge now renders a real <progress class="progress"> instead of a hand-built pair of absolutely-positioned divs. The bas/haut color interpolation still works, set via Bulma's own --bulma-progress-value-background-color CSS custom property rather than fighting the class. - Onglets' tab strip is now genuine Bulma tabs markup (tabs > ul > li, is-active on the li) instead of custom forgeTabBar/forgeTabBtn classes; forgeShowTab (duplicated in screen_edit.html and play.html) now toggles is-active to match. - Champ texte/email/mot de passe get class="input", Zone de texte gets class="textarea", Case à cocher/Bouton radio's existing <label> wrapper gets class="checkbox"/"radio" (Bulma's own convention — the structure already matched, just needed the class), Liste déroulante is wrapped in Bulma's required <div class="select"> (a bare class on the <select> itself has zero effect in Bulma), Tableau gets class="table is-bordered is-fullwidth" with the per-cell inline borders removed so Bulma's own table styling applies. - Removed the now-dead .forgeTabBar/.forgeTabBtn CSS. Updated tests/test_jauge.py and tests/test_onglets_widget.py assertions to match the new markup (value="X" attribute instead of width:X% inline style, --bulma-progress-value-background-color instead of background-color, forgeShowTab(this) marker instead of the removed forgeTabBtn class) — same behavior, different rendering mechanism. Full suite green (89 passed), verified end to end against the real "test" project via the actual /play route.
This commit is contained in:
@@ -12,4 +12,8 @@ def _render_checkbox_or_radio(el, meta, slug=None, children_map=None, ctx=None,
|
|||||||
attrs["type"] = input_type
|
attrs["type"] = input_type
|
||||||
style = _style_string(el, " display:flex; align-items:center; gap:8px;", parent_flex_direction)
|
style = _style_string(el, " display:flex; align-items:center; gap:8px;", parent_flex_direction)
|
||||||
content = _apply_ctx(el.get("content") or "", ctx)
|
content = _apply_ctx(el.get("content") or "", ctx)
|
||||||
return f'<label {_attr_string({}, style)}><input {_attr_string(attrs, "")}> {html_lib.escape(content)}</label>'
|
# <label class="checkbox"/"radio"> entourant l'input EST la façon dont
|
||||||
|
# Bulma stylise nativement une case à cocher/un bouton radio — pas de
|
||||||
|
# wrapper supplémentaire nécessaire, la structure existante correspondait
|
||||||
|
# déjà exactement à cette convention.
|
||||||
|
return f'<label {_attr_string({"class": input_type}, style)}><input {_attr_string(attrs, "")}> {html_lib.escape(content)}</label>'
|
||||||
|
|||||||
@@ -127,14 +127,21 @@ def _render_jauge(el, meta, slug, children_map=None, ctx=None, parent_flex_direc
|
|||||||
f'color:#e8eaf0; text-align:{text_align};">{html_lib.escape(str(nom_value))}</div>'
|
f'color:#e8eaf0; text-align:{text_align};">{html_lib.escape(str(nom_value))}</div>'
|
||||||
)
|
)
|
||||||
|
|
||||||
# La classe "jaugeBar" est repérée côté client (play.html, refreshRuntimeData)
|
# Vraie balise <progress> Bulma ("class=progress") plutôt qu'une barre
|
||||||
# pour savoir que ce wrapper doit être régénéré après toute action
|
# maison faite de <div> positionnés — la couleur dégradée bas/haut (voir
|
||||||
# "Modifier une donnée" — sinon la jauge resterait figée sur sa valeur
|
# _interpolate_color ci-dessus) reste possible nativement : Bulma pilote
|
||||||
# de départ après un changement, exactement comme il fallait déjà
|
# justement le remplissage via la variable CSS --bulma-progress-value-
|
||||||
# repérer "repeaterItem" pour les Répéteurs (voir 1.1/1.2).
|
# background-color, qu'on peut fixer en ligne à la couleur calculée sans
|
||||||
|
# sortir du système de classes de Bulma. La classe "jaugeBar" est
|
||||||
|
# repérée côté client (play.html, refreshRuntimeData) pour savoir que
|
||||||
|
# cette balise doit être régénérée après toute action "Modifier une
|
||||||
|
# donnée" — sinon la jauge resterait figée sur sa valeur de départ après
|
||||||
|
# un changement, exactement comme il fallait déjà repérer "repeaterItem"
|
||||||
|
# pour les Répéteurs (voir 1.1/1.2).
|
||||||
bar = (
|
bar = (
|
||||||
f'<div class="jaugeBar" style="position:absolute; left:0; top:0; bottom:0; width:{percent}%; '
|
f'<progress class="progress jaugeBar" value="{percent}" max="100" '
|
||||||
f'background-color:{fill_color}; transition:width .3s ease, background-color .3s ease;"></div>'
|
f'style="--bulma-progress-value-background-color:{fill_color}; --bulma-progress-bar-background-color:#1c2130; '
|
||||||
|
f'margin:0; border-radius:6px; transition:background-color .3s ease;">{percent}%</progress>'
|
||||||
)
|
)
|
||||||
# min-height (pas 0) : un élément posé DANS un conteneur a une hauteur
|
# min-height (pas 0) : un élément posé DANS un conteneur a une hauteur
|
||||||
# "auto" par défaut (voir _style_string) — "flex:1 1 auto" seul n'a
|
# "auto" par défaut (voir _style_string) — "flex:1 1 auto" seul n'a
|
||||||
@@ -145,7 +152,7 @@ def _render_jauge(el, meta, slug, children_map=None, ctx=None, parent_flex_direc
|
|||||||
# l'élément a une vraie hauteur (posé directement sur un écran, ou
|
# l'élément a une vraie hauteur (posé directement sur un écran, ou
|
||||||
# hauteur fixée via "Position & taille").
|
# hauteur fixée via "Position & taille").
|
||||||
bar_wrap = (
|
bar_wrap = (
|
||||||
f'<div style="position:relative; overflow:hidden; background:#1c2130; border-radius:6px; '
|
f'<div style="position:relative; display:flex; align-items:center; '
|
||||||
f'flex:1 1 auto; min-height:14px;">{bar}{label_html}</div>'
|
f'flex:1 1 auto; min-height:14px;">{bar}{label_html}</div>'
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -38,10 +38,14 @@ def _render_onglets(el, meta, slug, children_map=None, ctx=None, parent_flex_dir
|
|||||||
active = i == 0
|
active = i == 0
|
||||||
tab_html = render_element_html(tab, slug, children_map, ctx, parent_flex_direction=flex_dir)
|
tab_html = render_element_html(tab, slug, children_map, ctx, parent_flex_direction=flex_dir)
|
||||||
target_id = f"tabpanel-{tab['id']}"
|
target_id = f"tabpanel-{tab['id']}"
|
||||||
|
# <li> Bulma natif ("tabs" > "ul" > "li", "is-active" sur le <li> —
|
||||||
|
# pas le <a> — pour l'onglet en cours) : le <li> porte le clic ET les
|
||||||
|
# data-attributes ciblés par forgeShowTab (voir screen_edit.html /
|
||||||
|
# play.html), le <a> imbriqué n'est là que pour le rendu visuel Bulma.
|
||||||
bar_items.append(
|
bar_items.append(
|
||||||
f'<button type="button" class="forgeTabBtn{" active" if active else ""}" '
|
f'<li class="{"is-active" if active else ""}" '
|
||||||
f'data-tab-btn="{group_id}" data-tab-target="{target_id}" '
|
f'data-tab-btn="{group_id}" data-tab-target="{target_id}" '
|
||||||
f'onclick="forgeShowTab(this)">{html_lib.escape(str(label))}</button>'
|
f'onclick="forgeShowTab(this)"><a>{html_lib.escape(str(label))}</a></li>'
|
||||||
)
|
)
|
||||||
hidden_style = "" if active else ' style="display:none;"'
|
hidden_style = "" if active else ' style="display:none;"'
|
||||||
panels.append(
|
panels.append(
|
||||||
@@ -51,7 +55,7 @@ def _render_onglets(el, meta, slug, children_map=None, ctx=None, parent_flex_dir
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
f'<div {_attr_string(attrs, style)}>'
|
f'<div {_attr_string(attrs, style)}>'
|
||||||
f'<div class="forgeTabBar">{"".join(bar_items)}</div>'
|
f'<div class="tabs"><ul>{"".join(bar_items)}</ul></div>'
|
||||||
f'<div class="forgeTabPanels" style="flex:1 1 auto; min-height:0;">{"".join(panels)}</div>'
|
f'<div class="forgeTabPanels" style="flex:1 1 auto; min-height:0;">{"".join(panels)}</div>'
|
||||||
f"</div>"
|
f"</div>"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,12 +4,22 @@ from .visible_attrs import _visible_attrs
|
|||||||
from .apply_ctx import _apply_ctx
|
from .apply_ctx import _apply_ctx
|
||||||
from .style_string import _style_string
|
from .style_string import _style_string
|
||||||
from .attr_string import _attr_string
|
from .attr_string import _attr_string
|
||||||
|
from .auto_id import _auto_id
|
||||||
|
|
||||||
|
|
||||||
def _render_select(el, meta, slug=None, children_map=None, ctx=None, parent_flex_direction=None):
|
def _render_select(el, meta, slug=None, children_map=None, ctx=None, parent_flex_direction=None):
|
||||||
attrs = _visible_attrs(el, meta, ctx)
|
# Bulma stylise un <select> via la classe "select" posée sur un <div>
|
||||||
style = _style_string(el, parent_flex_direction=parent_flex_direction)
|
# QUI L'ENTOURE (le petit triangle est un ::after de ce wrapper) — la
|
||||||
|
# poser directement sur la balise <select> elle-même n'aurait aucun
|
||||||
|
# effet. Le wrapper porte donc l'id/positionnement habituel de
|
||||||
|
# l'élément (comme tous les autres widgets), le <select> reste à
|
||||||
|
# l'intérieur avec ses propres attributs de formulaire (name, required).
|
||||||
|
outer_attrs = _visible_attrs(el, meta, ctx)
|
||||||
|
select_attrs = {k: v for k, v in outer_attrs.items() if k not in ("id", "class", "data-element-id", "data-anim-target")}
|
||||||
|
wrapper_attrs = {k: v for k, v in outer_attrs.items() if k in ("id", "data-element-id", "data-anim-target")}
|
||||||
|
style = _style_string(el, " display:flex;", parent_flex_direction)
|
||||||
content = _apply_ctx(el.get("content") or "", ctx)
|
content = _apply_ctx(el.get("content") or "", ctx)
|
||||||
options = [o.strip() for o in content.split("\n") if o.strip()]
|
options = [o.strip() for o in content.split("\n") if o.strip()]
|
||||||
opts_html = "".join(f'<option value="{html_lib.escape(o)}">{html_lib.escape(o)}</option>' for o in options)
|
opts_html = "".join(f'<option value="{html_lib.escape(o)}">{html_lib.escape(o)}</option>' for o in options)
|
||||||
return f'<select {_attr_string(attrs, style)}>{opts_html}</select>'
|
select_html = f'<select {_attr_string(select_attrs, "")}>{opts_html}</select>'
|
||||||
|
return f'<div {_attr_string(wrapper_attrs, style)}><div class="select is-fullwidth">{select_html}</div></div>'
|
||||||
|
|||||||
@@ -10,15 +10,12 @@ def _render_table(el, meta, slug=None, children_map=None, ctx=None, parent_flex_
|
|||||||
attrs = _visible_attrs(el, meta, ctx)
|
attrs = _visible_attrs(el, meta, ctx)
|
||||||
header_row = (el.get("attributes") or {}).get("_header_row") in ("1", "true", "on")
|
header_row = (el.get("attributes") or {}).get("_header_row") in ("1", "true", "on")
|
||||||
content = _apply_ctx(el.get("content") or "", ctx)
|
content = _apply_ctx(el.get("content") or "", ctx)
|
||||||
style = _style_string(el, " border-collapse:collapse;", parent_flex_direction)
|
style = _style_string(el, "", parent_flex_direction)
|
||||||
rows = [r for r in content.split("\n") if r.strip()]
|
rows = [r for r in content.split("\n") if r.strip()]
|
||||||
html_rows = []
|
html_rows = []
|
||||||
for i, r in enumerate(rows):
|
for i, r in enumerate(rows):
|
||||||
cells = r.split(";")
|
cells = r.split(";")
|
||||||
cell_tag = "th" if (header_row and i == 0) else "td"
|
cell_tag = "th" if (header_row and i == 0) else "td"
|
||||||
cells_html = "".join(
|
cells_html = "".join(f"<{cell_tag}>{html_lib.escape(c.strip())}</{cell_tag}>" for c in cells)
|
||||||
f'<{cell_tag} style="border:1px solid #2b3140; padding:6px 8px;">{html_lib.escape(c.strip())}</{cell_tag}>'
|
|
||||||
for c in cells
|
|
||||||
)
|
|
||||||
html_rows.append(f"<tr>{cells_html}</tr>")
|
html_rows.append(f"<tr>{cells_html}</tr>")
|
||||||
return f'<table {_attr_string(attrs, style)}>{"".join(html_rows)}</table>'
|
return f'<table {_attr_string(attrs, style)}>{"".join(html_rows)}</table>'
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ WIDGETS = {
|
|||||||
},
|
},
|
||||||
"champ_texte": {
|
"champ_texte": {
|
||||||
"label": "Champ — texte", "icon": "⌨️", "tag": "input", "has_content": False, "self_closing": True,
|
"label": "Champ — texte", "icon": "⌨️", "tag": "input", "has_content": False, "self_closing": True,
|
||||||
"w": 40, "h": 8, "default_content": "", "fixed_attrs": {"type": "text"},
|
"w": 40, "h": 8, "default_content": "", "fixed_attrs": {"type": "text", "class": "input"},
|
||||||
"controls": [
|
"controls": [
|
||||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Votre réponse", group="Contenu"),
|
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Votre réponse", group="Contenu"),
|
||||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. reponse1", group="Contenu"),
|
c_text("name", "Nom du champ", "attr:name", placeholder="ex. reponse1", group="Contenu"),
|
||||||
@@ -109,7 +109,7 @@ WIDGETS = {
|
|||||||
},
|
},
|
||||||
"champ_email": {
|
"champ_email": {
|
||||||
"label": "Champ — email", "icon": "📧", "tag": "input", "has_content": False, "self_closing": True,
|
"label": "Champ — email", "icon": "📧", "tag": "input", "has_content": False, "self_closing": True,
|
||||||
"w": 40, "h": 8, "default_content": "", "fixed_attrs": {"type": "email"},
|
"w": 40, "h": 8, "default_content": "", "fixed_attrs": {"type": "email", "class": "input"},
|
||||||
"controls": [
|
"controls": [
|
||||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. votre@email.com", default="votre@email.com", group="Contenu"),
|
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. votre@email.com", default="votre@email.com", group="Contenu"),
|
||||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. email", group="Contenu"),
|
c_text("name", "Nom du champ", "attr:name", placeholder="ex. email", group="Contenu"),
|
||||||
@@ -118,7 +118,7 @@ WIDGETS = {
|
|||||||
},
|
},
|
||||||
"champ_mot_de_passe": {
|
"champ_mot_de_passe": {
|
||||||
"label": "Champ — mot de passe", "icon": "🔒", "tag": "input", "has_content": False, "self_closing": True,
|
"label": "Champ — mot de passe", "icon": "🔒", "tag": "input", "has_content": False, "self_closing": True,
|
||||||
"w": 40, "h": 8, "default_content": "", "fixed_attrs": {"type": "password"},
|
"w": 40, "h": 8, "default_content": "", "fixed_attrs": {"type": "password", "class": "input"},
|
||||||
"controls": [
|
"controls": [
|
||||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Mot de passe", group="Contenu"),
|
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Mot de passe", group="Contenu"),
|
||||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. mot_de_passe", group="Contenu"),
|
c_text("name", "Nom du champ", "attr:name", placeholder="ex. mot_de_passe", group="Contenu"),
|
||||||
@@ -149,7 +149,7 @@ WIDGETS = {
|
|||||||
},
|
},
|
||||||
"zone_texte": {
|
"zone_texte": {
|
||||||
"label": "Zone de texte", "icon": "🗒️", "tag": "textarea", "has_content": False,
|
"label": "Zone de texte", "icon": "🗒️", "tag": "textarea", "has_content": False,
|
||||||
"w": 40, "h": 18, "default_content": "",
|
"w": 40, "h": 18, "default_content": "", "fixed_attrs": {"class": "textarea"},
|
||||||
"controls": [
|
"controls": [
|
||||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Votre message", group="Contenu"),
|
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Votre message", group="Contenu"),
|
||||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. message", group="Contenu"),
|
c_text("name", "Nom du champ", "attr:name", placeholder="ex. message", group="Contenu"),
|
||||||
@@ -195,6 +195,7 @@ WIDGETS = {
|
|||||||
"tableau": {
|
"tableau": {
|
||||||
"label": "Tableau", "icon": "🗂️", "tag": "table", "has_content": True, "special_render": "table",
|
"label": "Tableau", "icon": "🗂️", "tag": "table", "has_content": True, "special_render": "table",
|
||||||
"w": 55, "h": 25, "default_content": "Colonne 1;Colonne 2\nValeur A;Valeur B",
|
"w": 55, "h": 25, "default_content": "Colonne 1;Colonne 2\nValeur A;Valeur B",
|
||||||
|
"fixed_attrs": {"class": "table is-bordered is-fullwidth"},
|
||||||
"controls": [
|
"controls": [
|
||||||
c_textarea("content", "Contenu — une ligne par rangée, colonnes séparées par ;", "content",
|
c_textarea("content", "Contenu — une ligne par rangée, colonnes séparées par ;", "content",
|
||||||
default="Colonne 1;Colonne 2\nValeur A;Valeur B", group="Contenu"),
|
default="Colonne 1;Colonne 2\nValeur A;Valeur B", group="Contenu"),
|
||||||
|
|||||||
+3
-8
@@ -254,14 +254,9 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
|
|||||||
}
|
}
|
||||||
.contextMenu button:hover{ background:var(--panel2); }
|
.contextMenu button:hover{ background:var(--panel2); }
|
||||||
|
|
||||||
/* Élément à onglets (voir render_onglets.py) */
|
/* Élément à onglets (voir render_onglets.py) : la barre d'onglets elle-même
|
||||||
.forgeTabBar{ display:flex; flex-wrap:wrap; gap:2px; border-bottom:1px solid var(--border); }
|
est un vrai composant Bulma ("tabs" > "ul" > "li", voir render_onglets.py)
|
||||||
.forgeTabBtn{
|
— il ne reste ici que les panneaux, que Bulma n'a pas de composant pour. */
|
||||||
background:none; border:none; border-bottom:2px solid transparent; color:var(--text-dim);
|
|
||||||
padding:8px 14px; font-size:14px; font-family:inherit; cursor:pointer; white-space:nowrap;
|
|
||||||
}
|
|
||||||
.forgeTabBtn:hover{ color:var(--text); }
|
|
||||||
.forgeTabBtn.active{ color:var(--text); border-bottom-color:var(--accent); font-weight:600; }
|
|
||||||
.forgeTabPanels{ min-width:0; }
|
.forgeTabPanels{ min-width:0; }
|
||||||
.forgeTabPanel{ padding-top:10px; }
|
.forgeTabPanel{ padding-top:10px; }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@
|
|||||||
var groupId = btn.dataset.tabBtn;
|
var groupId = btn.dataset.tabBtn;
|
||||||
var targetId = btn.dataset.tabTarget;
|
var targetId = btn.dataset.tabTarget;
|
||||||
document.querySelectorAll('[data-tab-btn="' + groupId + '"]').forEach(function (b) {
|
document.querySelectorAll('[data-tab-btn="' + groupId + '"]').forEach(function (b) {
|
||||||
b.classList.toggle('active', b === btn);
|
b.classList.toggle('is-active', b === btn);
|
||||||
});
|
});
|
||||||
document.querySelectorAll('[data-tab-group="' + groupId + '"]').forEach(function (p) {
|
document.querySelectorAll('[data-tab-group="' + groupId + '"]').forEach(function (p) {
|
||||||
p.style.display = (p.id === targetId) ? '' : 'none';
|
p.style.display = (p.id === targetId) ? '' : 'none';
|
||||||
|
|||||||
@@ -758,7 +758,7 @@ function forgeShowTab(btn) {
|
|||||||
var groupId = btn.dataset.tabBtn;
|
var groupId = btn.dataset.tabBtn;
|
||||||
var targetId = btn.dataset.tabTarget;
|
var targetId = btn.dataset.tabTarget;
|
||||||
document.querySelectorAll('[data-tab-btn="' + groupId + '"]').forEach(function (b) {
|
document.querySelectorAll('[data-tab-btn="' + groupId + '"]').forEach(function (b) {
|
||||||
b.classList.toggle('active', b === btn);
|
b.classList.toggle('is-active', b === btn);
|
||||||
});
|
});
|
||||||
document.querySelectorAll('[data-tab-group="' + groupId + '"]').forEach(function (p) {
|
document.querySelectorAll('[data-tab-group="' + groupId + '"]').forEach(function (p) {
|
||||||
p.style.display = (p.id === targetId) ? '' : 'none';
|
p.style.display = (p.id === targetId) ? '' : 'none';
|
||||||
|
|||||||
+8
-7
@@ -33,7 +33,7 @@ def test_gauge_width_reflects_field_value_without_extra_action(client, game):
|
|||||||
})
|
})
|
||||||
|
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert "width:50.0%" in html
|
assert 'value="50.0"' in html
|
||||||
|
|
||||||
|
|
||||||
def test_gauge_updates_after_data_change_with_no_extra_wiring(client, game):
|
def test_gauge_updates_after_data_change_with_no_extra_wiring(client, game):
|
||||||
@@ -52,7 +52,7 @@ def test_gauge_updates_after_data_change_with_no_extra_wiring(client, game):
|
|||||||
client.post(f"/game/{game}/objects/{def_id}/data/new", data={"reputation": "90"})
|
client.post(f"/game/{game}/objects/{def_id}/data/new", data={"reputation": "90"})
|
||||||
|
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert "width:90.0%" in html
|
assert 'value="90.0"' in html
|
||||||
|
|
||||||
|
|
||||||
def test_gauge_color_gradient_shifts_between_low_and_high(client, game):
|
def test_gauge_color_gradient_shifts_between_low_and_high(client, game):
|
||||||
@@ -67,7 +67,7 @@ def test_gauge_color_gradient_shifts_between_low_and_high(client, game):
|
|||||||
"ctrl_couleur_basse": "#ff0000", "ctrl_couleur_haute": "#00ff00",
|
"ctrl_couleur_basse": "#ff0000", "ctrl_couleur_haute": "#00ff00",
|
||||||
})
|
})
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert "background-color:#00ff00" in html # valeur au max -> couleur "haute" pure
|
assert "--bulma-progress-value-background-color:#00ff00" in html # valeur au max -> couleur "haute" pure
|
||||||
|
|
||||||
|
|
||||||
def test_gauge_value_clamped_visually_when_out_of_range(client, game):
|
def test_gauge_value_clamped_visually_when_out_of_range(client, game):
|
||||||
@@ -82,7 +82,7 @@ def test_gauge_value_clamped_visually_when_out_of_range(client, game):
|
|||||||
"ctrl_definition_id": str(def_id), "ctrl_champ": "reputation", "ctrl_min": "0", "ctrl_max": "100",
|
"ctrl_definition_id": str(def_id), "ctrl_champ": "reputation", "ctrl_min": "0", "ctrl_max": "100",
|
||||||
})
|
})
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert "width:100.0%" in html
|
assert 'value="100.0"' in html
|
||||||
|
|
||||||
|
|
||||||
# ---------- Choix de l'enregistrement (plusieurs jauges dans un même objet)
|
# ---------- Choix de l'enregistrement (plusieurs jauges dans un même objet)
|
||||||
@@ -121,7 +121,7 @@ def test_gauge_row_id_distinguishes_between_records_of_the_same_object(client, g
|
|||||||
"ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
|
"ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
|
||||||
})
|
})
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert "width:80.0%" in html
|
assert 'value="80.0"' in html
|
||||||
|
|
||||||
|
|
||||||
def test_gauge_without_row_id_falls_back_to_latest_row_no_regression(client, game):
|
def test_gauge_without_row_id_falls_back_to_latest_row_no_regression(client, game):
|
||||||
@@ -136,7 +136,7 @@ def test_gauge_without_row_id_falls_back_to_latest_row_no_regression(client, gam
|
|||||||
"ctrl_definition_id": str(def_id), "ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
|
"ctrl_definition_id": str(def_id), "ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
|
||||||
})
|
})
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert "width:80.0%" in html # "Confiance", ajoutée en dernier
|
assert 'value="80.0"' in html # "Confiance", ajoutée en dernier
|
||||||
|
|
||||||
|
|
||||||
def test_gauge_name_field_renders_above_the_bar(client, game):
|
def test_gauge_name_field_renders_above_the_bar(client, game):
|
||||||
@@ -186,7 +186,8 @@ def test_gauge_bar_stays_visible_when_nested_in_a_container_with_a_name(client,
|
|||||||
html = client.get(f"/game/{game}/play").data.decode()
|
html = client.get(f"/game/{game}/play").data.decode()
|
||||||
assert ">Confiance<" in html
|
assert ">Confiance<" in html
|
||||||
idx = html.find("jaugeBar")
|
idx = html.find("jaugeBar")
|
||||||
wrap_style = html[html.rfind('style="', 0, html.rfind('<div', 0, idx)):idx]
|
div_start = html.rfind('<div', 0, idx)
|
||||||
|
wrap_style = html[div_start:html.find('>', div_start)]
|
||||||
assert "min-height:14px" in wrap_style
|
assert "min-height:14px" in wrap_style
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def test_deleting_a_tab_removes_it_and_reselects_the_tabs_widget(client, game):
|
|||||||
assert f"selected={onglets_id}" in resp.headers["Location"]
|
assert f"selected={onglets_id}" in resp.headers["Location"]
|
||||||
|
|
||||||
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
|
||||||
assert html.count("forgeTabBtn") == 1
|
assert html.count("forgeShowTab(this)") == 1
|
||||||
|
|
||||||
|
|
||||||
def test_tab_content_holds_a_real_container_children_can_be_added_to(client, game):
|
def test_tab_content_holds_a_real_container_children_can_be_added_to(client, game):
|
||||||
|
|||||||
Reference in New Issue
Block a user