feat(vues): bouton « Copier le lien » + lien de partage en texte stylé (#112)
- Bouton de copie explicite (icône copy.svg + « Copier le lien » + retour « ✓ Copié ») sur /tableau et /compte/vues, via dcc.Clipboard (children / copied_children), à la place de l'icône seule. - Le lien direct s'affiche en texte (span#share-url-text) plutôt qu'un <input> : évite la troncature d'affichage du champ Bootstrap form-control en flex (zone de texte bloquée ~218px), montre tout le lien avec retour à la ligne, et prend la largeur du lien. Stylé comme un champ (bordure, coins arrondis, texte 90%). - Instructions /compte/vues simplifiées ; E2E adapté (#share-url-text).
This commit is contained in:
@@ -1095,3 +1095,13 @@ input[type="number"] {
|
|||||||
background-color: var(--bs-primary-bg-subtle);
|
background-color: var(--bs-primary-bg-subtle);
|
||||||
border-color: var(--bs-primary);
|
border-color: var(--bs-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Lien de partage affiché en texte (span) mais stylé comme un champ :
|
||||||
|
bordure gris clair, coins arrondis, texte légèrement plus petit. */
|
||||||
|
.share-url-text {
|
||||||
|
font-size: 90%;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: var(--bs-border-radius);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,8 +31,14 @@ def _content():
|
|||||||
[
|
[
|
||||||
html.H2("Mes vues"),
|
html.H2("Mes vues"),
|
||||||
html.P(
|
html.P(
|
||||||
"Les vues que vous enregistrez depuis le Tableau apparaissent ici. "
|
[
|
||||||
"Cliquez sur « Ouvrir » pour appliquer une vue."
|
"Les vues que vous enregistrez depuis le Tableau apparaissent "
|
||||||
|
"ici. Cliquez sur ",
|
||||||
|
html.B("Ouvrir"),
|
||||||
|
" pour appliquer une vue, ou sur ",
|
||||||
|
html.B("Copier le lien"),
|
||||||
|
" pour copier son adresse Web et la partager par mail ou par message, y compris avec une personne qui n'a pas de compte colibre.",
|
||||||
|
]
|
||||||
),
|
),
|
||||||
html.Div(saved_views_ui.views_table(views), id="vues-list"),
|
html.Div(saved_views_ui.views_table(views), id="vues-list"),
|
||||||
dbc.Modal(
|
dbc.Modal(
|
||||||
|
|||||||
+25
-11
@@ -411,21 +411,35 @@ layout = [
|
|||||||
className="share-url-box d-none",
|
className="share-url-box d-none",
|
||||||
children=[
|
children=[
|
||||||
dbc.Label(
|
dbc.Label(
|
||||||
"URL directe vers cette vue :",
|
"Lien direct vers cette vue :",
|
||||||
html_for="share-url-input",
|
|
||||||
className="mb-0",
|
className="mb-0",
|
||||||
),
|
),
|
||||||
dcc.Input(
|
# URL affichée comme texte sélectionnable : prend exactement
|
||||||
id="share-url-input",
|
# sa largeur (pas de champ pleine largeur qui encombre) et
|
||||||
type="text",
|
# passe à la ligne si le lien est long (pas de troncature).
|
||||||
readOnly=True,
|
html.Span(
|
||||||
className="form-control form-control-sm",
|
id="share-url-text",
|
||||||
style={"maxWidth": "420px"},
|
className="share-url-text",
|
||||||
|
style={"wordBreak": "break-all", "minWidth": 0},
|
||||||
),
|
),
|
||||||
dcc.Clipboard(
|
dcc.Clipboard(
|
||||||
target_id="share-url-input",
|
target_id="share-url-text",
|
||||||
title="Copier le lien vers cette vue",
|
title="Copier le lien vers cette vue",
|
||||||
style={"cursor": "pointer", "fontSize": "1.1rem"},
|
className="btn btn-outline-secondary btn-sm "
|
||||||
|
"d-inline-flex align-items-center",
|
||||||
|
children=[
|
||||||
|
html.Img(
|
||||||
|
src="/assets/copy.svg",
|
||||||
|
alt="",
|
||||||
|
style={
|
||||||
|
"height": "1em",
|
||||||
|
"verticalAlign": "-0.15em",
|
||||||
|
"marginRight": "0.35em",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
"Copier le lien",
|
||||||
|
],
|
||||||
|
copied_children="✓ Copié",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -714,7 +728,7 @@ _SHARE_BOX_HIDDEN = "share-url-box d-none"
|
|||||||
|
|
||||||
@callback(
|
@callback(
|
||||||
Output("share-url-box", "className"),
|
Output("share-url-box", "className"),
|
||||||
Output("share-url-input", "value"),
|
Output("share-url-text", "children"),
|
||||||
Input("active-view", "data"),
|
Input("active-view", "data"),
|
||||||
)
|
)
|
||||||
def render_share_box(active_view):
|
def render_share_box(active_view):
|
||||||
|
|||||||
+17
-3
@@ -79,7 +79,21 @@ def _view_row(view) -> html.Div:
|
|||||||
dcc.Clipboard(
|
dcc.Clipboard(
|
||||||
content=share_url,
|
content=share_url,
|
||||||
title="Copier le lien vers cette vue",
|
title="Copier le lien vers cette vue",
|
||||||
style={"cursor": "pointer", "fontSize": "1.1rem"},
|
className="btn btn-outline-secondary btn-sm "
|
||||||
|
"d-inline-flex align-items-center",
|
||||||
|
children=[
|
||||||
|
html.Img(
|
||||||
|
src="/assets/copy.svg",
|
||||||
|
alt="",
|
||||||
|
style={
|
||||||
|
"height": "1em",
|
||||||
|
"verticalAlign": "-0.15em",
|
||||||
|
"marginRight": "0.35em",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
"Copier le lien",
|
||||||
|
],
|
||||||
|
copied_children="✓ Copié",
|
||||||
),
|
),
|
||||||
dbc.Button(
|
dbc.Button(
|
||||||
"Renommer",
|
"Renommer",
|
||||||
@@ -102,9 +116,9 @@ def _view_row(view) -> html.Div:
|
|||||||
def views_table(views) -> html.Div:
|
def views_table(views) -> html.Div:
|
||||||
if not views:
|
if not views:
|
||||||
return html.Div(
|
return html.Div(
|
||||||
html.P(
|
dcc.Markdown(
|
||||||
"Vous n'avez pas encore de vue enregistrée. "
|
"Vous n'avez pas encore de vue enregistrée. "
|
||||||
"Créez-en une depuis le Tableau, bouton « Sauvegarder la vue »."
|
"Créez-en une depuis le Tableau, bouton **Sauvegarder la vue**."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return html.Div([_view_row(v) for v in views])
|
return html.Div([_view_row(v) for v in views])
|
||||||
|
|||||||
@@ -168,16 +168,18 @@ def test_open_shared_view_applies_and_shows_box(dash_duo, users_db_path):
|
|||||||
dash_duo.wait_for_text_to_equal(".logo > h1", "colibre", timeout=6)
|
dash_duo.wait_for_text_to_equal(".logo > h1", "colibre", timeout=6)
|
||||||
dash_duo.wait_for_page(dash_duo.server_url + f"/tableau?vue=ma-vue_{token}")
|
dash_duo.wait_for_page(dash_duo.server_url + f"/tableau?vue=ma-vue_{token}")
|
||||||
|
|
||||||
# Le bloc de partage est visible et contient l'URL courte (jeton). La valeur
|
# Le bloc de partage est visible et affiche l'URL courte (jeton). Le texte est
|
||||||
# de l'input est renseignée par le même callback que l'affichage : la lire
|
# renseigné par le même callback que l'affichage : le lire non vide prouve que
|
||||||
# non vide prouve que l'écho de l'application n'a PAS masqué le bloc.
|
# l'écho de l'application n'a PAS masqué le bloc.
|
||||||
dash_duo.wait_for_style_to_equal("#share-url-box", "display", "flex", timeout=10)
|
dash_duo.wait_for_style_to_equal("#share-url-box", "display", "flex", timeout=10)
|
||||||
dash_duo.wait_for_element("#share-url-input", timeout=6)
|
dash_duo.wait_for_element("#share-url-text", timeout=6)
|
||||||
WebDriverWait(dash_duo.driver, 10).until(
|
WebDriverWait(dash_duo.driver, 10).until(
|
||||||
lambda _d: token
|
lambda _d: token in (dash_duo.find_element("#share-url-text").text or "")
|
||||||
in (dash_duo.find_element("#share-url-input").get_attribute("value") or "")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Le bouton de copie porte un libellé explicite (UX : pas d'icône seule).
|
||||||
|
assert "Copier le lien" in dash_duo.find_element("#share-url-box").text
|
||||||
|
|
||||||
# Une action utilisateur (filtre) masque la box. On re-tente la saisie tant
|
# Une action utilisateur (filtre) masque la box. On re-tente la saisie tant
|
||||||
# que le bloc n'est pas masqué : la fenêtre de re-render post-application
|
# que le bloc n'est pas masqué : la fenêtre de re-render post-application
|
||||||
# peut invalider l'input entre le find et le send_keys.
|
# peut invalider l'input entre le find et le send_keys.
|
||||||
|
|||||||
Reference in New Issue
Block a user