Aligne la hauteur du bouton de recherche sur l'input (débordement en bas)

Le bouton utilisait height:auto, dépendant du padding/line-height par
défaut de .btn (~38px), plus haut que l'input fixé à 34px. Fixe la
hauteur du bouton à 34px pour qu'il s'aligne avec l'input.

Inclut aussi les changements en cours sur recherche.py (tagline
dynamique avec stats, home_intro, layout en fonction).
This commit is contained in:
Colin Maudry
2026-07-15 16:08:56 +02:00
parent 85b0dd0b39
commit 6252b65671
2 changed files with 109 additions and 61 deletions
+96 -61
View File
@@ -1,15 +1,47 @@
import math
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
from dash import Input, Output, State, callback, dcc, html, register_page from dash import Input, Output, State, callback, dcc, html, register_page
from src.db import count_unique_marches
from src.figures import DataTable from src.figures import DataTable
from src.pages.a_propos.abonnement import abonnement_features from src.pages.a_propos.abonnement import abonnement_features
from src.utils.cache import cache
from src.utils.data import DF_ACHETEURS, DF_TITULAIRES from src.utils.data import DF_ACHETEURS, DF_TITULAIRES
from src.utils.search import search_org from src.utils.search import search_org
from src.utils.seo import META_CONTENT from src.utils.seo import META_CONTENT
from src.utils.table import setup_table_columns from src.utils.table import format_number, setup_table_columns
NAME = "Recherche" NAME = "Recherche"
_STAT_STYLE = {"fontWeight": "bold", "color": "var(--primary-color-text)"}
@cache.memoize(timeout=12 * 60 * 60)
def _tagline_stats() -> tuple[str, str, str]:
nb_acheteurs = format_number(round(DF_ACHETEURS.height, -3))
nb_titulaires = format_number(round(DF_TITULAIRES.height, -3))
nb_marches_millions = math.floor(count_unique_marches() / 100_000) / 10
unite_marches = "million" if nb_marches_millions < 2 else "millions"
nb_marches = f"{nb_marches_millions:.1f}".replace(".", ",") + " " + unite_marches
return nb_acheteurs, nb_titulaires, nb_marches
def _tagline() -> html.P:
nb_acheteurs, nb_titulaires, nb_marches = _tagline_stats()
return html.P(
[
"Recherchez parmi ",
html.Span(nb_acheteurs, style=_STAT_STYLE),
" acheteurs et ",
html.Span(nb_titulaires, style=_STAT_STYLE),
" titulaires dans une base de données de plus de ",
html.Span(nb_marches, style=_STAT_STYLE),
" de marchés publics",
]
)
_features_gratuites = dcc.Markdown(""" _features_gratuites = dcc.Markdown("""
- Recherche d'acheteurs et de titulaires - Recherche d'acheteurs et de titulaires
- [Tableau](/tableau) filtrable et personnalisable sur des dizaines de colonnes et exports Excel - [Tableau](/tableau) filtrable et personnalisable sur des dizaines de colonnes et exports Excel
@@ -52,6 +84,7 @@ home_intro = html.Div(
dcc.Link( dcc.Link(
"En savoir plus et s'abonner", "En savoir plus et s'abonner",
href="/a-propos/abonnement", href="/a-propos/abonnement",
style={"marginLeft": "20px"},
), ),
], ],
md=6, md=6,
@@ -73,66 +106,68 @@ register_page(
order=0, order=0,
) )
layout = html.Div(
className="container", def layout(**_):
children=[ return html.Div(
html.Div( className="container",
className="tagline", children=[
children=html.P("Recherchez un acheteur ou un titulaire de marché public"), html.Div(
), className="tagline",
html.Div( children=_tagline(),
style={ ),
"display": "flex", html.Div(
"justifyContent": "center", style={
"marginTop": "30px", "display": "flex",
"marginBottom": "30px", "justifyContent": "center",
}, "marginTop": "30px",
children=[ "marginBottom": "30px",
dcc.Input( },
id="search", children=[
type="text", dcc.Input(
placeholder="Nom d'acheteur/entreprise, SIREN/SIRET, code département", id="search",
autoFocus=True, type="text",
style={ placeholder="Nom d'acheteur/entreprise, SIREN/SIRET, code département",
"margin": "0", autoFocus=True,
"width": "500px", style={
"border": "1px solid #ccc", "margin": "0",
"borderRight": "none", "width": "500px",
"borderRadius": "3px 0 0 3px", "border": "1px solid #ccc",
"padding": "5px 10px", "borderRight": "none",
"outline": "none", "borderRadius": "3px 0 0 3px",
"height": "34px", "padding": "5px 10px",
}, "outline": "none",
), "height": "34px",
html.Button( },
"=>", ),
id="search-button", html.Button(
className="btn btn-secondary", "=>",
style={ id="search-button",
"border": "1px solid #ccc", className="btn btn-secondary",
"borderRadius": "0 3px 3px 0", style={
"marginLeft": "0", "border": "1px solid #ccc",
"height": "auto", # Ensure it matches input height if necessary, often relying on padding/line-height "borderRadius": "0 3px 3px 0",
}, "marginLeft": "0",
), "height": "34px",
], },
), ),
html.P( ],
[ ),
"...ou bien filtrez les marchés publics dans la vue ", html.P(
dcc.Link("Tableau", href="/tableau"), [
], "...ou bien filtrez les marchés publics dans la vue ",
style={"textAlign": "center"}, dcc.Link("Tableau", href="/tableau"),
id="mention_tableau", ],
), style={"textAlign": "center"},
home_intro, id="mention_tableau",
# html.Div( ),
# className="search_options", home_intro,
# children=[dcc.RadioItems(options=["Acheteur(s)"])], # html.Div(
# ), # className="search_options",
dbc.Row(id="search_results"), # children=[dcc.RadioItems(options=["Acheteur(s)"])],
], # ),
) dbc.Row(id="search_results"),
],
)
@callback( @callback(
+13
View File
@@ -402,3 +402,16 @@ def test_015_org_pages_filter_date(dash_duo: DashComposite):
# la mise à jour de la grille plutôt que de lire les lignes immédiatement # la mise à jour de la grille plutôt que de lire les lignes immédiatement
# (sinon on lit l'état pré-filtre). # (sinon on lit l'état pré-filtre).
dash_duo.wait_for_no_elements(filter_cell_result, timeout=4) dash_duo.wait_for_no_elements(filter_cell_result, timeout=4)
def test_016_search_button_matches_input_height():
# Import app first to initialize Dash
from src.app import app # noqa: F401
from src.pages.recherche import layout
search_input, search_button = layout().children[1].children
assert search_input.style["height"] == search_button.style["height"], (
"Le bouton de recherche doit avoir la même hauteur que le champ de "
"recherche pour ne pas déborder en bas"
)