République
française
dsfr-data
Documentation — Web Components DSFR pour la dataviz
Filtres transverses multi-sources pour les dashboards a filtre commun.
Le contexte ecoute des elements d'UI natifs (select, input...)
et diffuse les filtres a N sources nommees — le « fan-out » declaratif qui evitait
jusqu'ici d'ecrire du JavaScript d'orchestration a la main.
Opt-in et additif (ADR-031) : sans contexte, chaque source reste
autonome — rien ne change pour les pages mono-graphique. Le contexte n'effectue
aucun fetch et ne transforme aucune donnee : il emet des commandes where
avec un whereKey stable par filtre, combinees en AND par
le merge multi-emetteurs des sources (jamais « le dernier gagne »).
<!-- UI native libre (formulaire DSFR) -->
<select id="ui-catégorie" multiple>...</select>
<input id="ui-mois" type="month">
<!-- Le contexte orchestre -->
<dsfr-data-context sources="src-a src-b src-c" url-sync>
<dsfr-data-context-filter field="categorie_produit" label="Catégorie"
operator="in" ui="ui-catégorie"></dsfr-data-context-filter>
<dsfr-data-context-filter field="date_rappel" label="Mois"
operator="month-of" ui="ui-mois"></dsfr-data-context-filter>
</dsfr-data-context>
<!-- Recap supprimable des filtres actifs -->
<dsfr-data-context-tags for="ctx"></dsfr-data-context-tags>
| Attribut | Type | Valeurs | Description |
|---|---|---|---|
sources | String | requis | Ids des sources cibles, séparés par des espaces |
url-sync | Boolean | défaut : off | Sérialisation URL des filtres (#231, ADR-031) — OPT-IN, défaut OFF (collision possible avec le routing query-string du site hôte). Lecture au chargement (pré-remplit les UI, qui repassent par le même chemin qu'un clic — aucune injection directe dans un where) ; écriture en history.replaceState à chaque changement. Un paramètre par champ, pour les filtres classiques comme pour les facettes et la recherche enregistrées par context="id" (#678) : l'URL-sync est unique. DEUX PIÈGES À DEUX CONTEXTES, tous deux signalés en console (#922, #923). 1. Deux contextes à url-sync qui filtrent le MÊME champ écrivent le MÊME paramètre : le dernier écrase les autres, et au rechargement ils relisent tous la même valeur — un comparateur se compare alors à lui-même. Un seul contexte dans l'URL, ou url-param-map pour séparer les paramètres. 2. Le pré-remplissage depuis l'URL écrit el.value SANS émettre d'événement : un filtre d'un AUTRE contexte déjà lié au même contrôle reste sur la valeur d'avant. Déclarer le contexte à url-sync EN PREMIER dans le document. |
url-param-map | String | "param:field | p2:f2" | Renommage des paramètres : "param:field | param2:field2" (#231) |
| Attribut | Type | Valeurs | Description |
|---|---|---|---|
field | String | requis | Colonne filtrée — une colonne des SOURCES ciblées, telle que l'API la connaît. Le filtre y est délégué et s'applique avant tout regroupement (ordre ODSQL : where puis group_by) : ni un alias d'agrégat (montant__sum), ni une colonne calculée en aval (compute d'un dsfr-data-normalize) n'existent à ce stade. Quand le contexte sait que la colonne manque (source sans select ni group-by, déjà chargée, #805) : absente de toutes les sources visées, erreur de configuration nommée et rien n'est diffusé ; absente de certaines seulement, ces sources sont exclues du filtre, avec un message console. Sinon le filtre part, et l'API répond (HTTP 400 si la colonne n'existe pas). TYPE DE LA COLONNE (#924) : un contrôle de formulaire rend toujours du TEXTE, et le filtre émet reg = "01". Sur une colonne que le jeu publie en ENTIER, le portail compare en texte et ne trouve rien, là où son refine trouvait — « — » à l'écran, sans erreur. Les codes métropolitains (« 75 ») passent, ce qui cache le défaut : seuls les codes à zéro de tête (Guadeloupe 01, départements 01 à 09) sont muets. Quand les lignes déjà rendues par une source montrent que la colonne y est numérique, la console le dit, une fois par colonne et par source. Quand elles ne la portent pas — source agrégée côté serveur (select="sum(...)"), filtre délégué au portail (#980) — c'est le type DÉCLARÉ par le jeu Opendatasoft qui décide, lu une fois par jeu. Le geste : alimenter le filtre avec la valeur sans zéro de tête, ou réserver ce filtre aux sources qui publient le code en texte avec apply-to. |
ui | String | requis | Id(s) de l'élément d'UI écouté — deux ids (min max) pour between |
operator | ContextOperator | eq (défaut), in, lt, gte, between, month-of, year-of, lt-day-after, last-n-days, current-year, current-month | Opérateur : eq, in, lt, gte, between, contains (sous-chaîne, #678) — et dates (#230, clauses en plages [debut, fin)) : month-of, year-of, lt-day-after, last-n-days, current-year, current-month (#682 — case à cocher, mois en cours, borne dynamique). year-of et month-of acceptent une date plus precise que l'opérateur et la tronquent (#646) : "2026-09-09" -> annee 2026 / mois 2026-09, ce qui permet de les nourrir d'un <input type="date"> (il n'existe pas de type="year"). Une valeur qui reste inexploitable (ni date, ni mois, ni annee) retire le filtre et le signale par un avertissement console, emis une seule fois par filtre. |
apply-to | String | * | Cibles : "*" (défaut, toutes les sources du contexte) ou ids ciblés |
label | String | défaut : field | Libellé naturel pour l'affichage (tags #232) — défaut : field |
default | String | today, first-of-month, first-of-year ou littéral | Valeur initiale du filtre (#682), appliquée au montage APRÈS l'URL — un paramètre d'URL présent gagne toujours (ADR-031). Mots-clés dynamiques résolus dans le fuseau local : today (date du jour), first-of-month (1er du mois en cours), first-of-year (1er janvier de l'année en cours) ; toute autre valeur est un littéral. La date est adaptée au contrôle (input type="month" → AAAA-MM, year-of → AAAA) puis écrite dans l'UI et émise par le chemin normal, jamais injectée dans un where. Pour between et in, plusieurs valeurs séparées par une virgule (ex. first-of-year,today). |
context | String | context="ctx" | Id du dsfr-data-context cible (#678). Vide = le contexte parent le plus proche (closest), comportement historique. Le contexte peut être déclaré après ce filtre dans le DOM : l'enregistrement se fait alors à sa connexion. |
| Operateur | UI typique | Clause générée |
|---|---|---|
eq | input, select | field = valeur |
in | select multiple | field IN (v1, v2) — valeurs separees par | ou , |
lt / gte | input | comparaison stricte / large |
between | deux inputs (min, max) | gte min + lt max |
month-of | input type="month" | plage [1er du mois, 1er du mois suivant) |
year-of | input annee | plage annuelle |
lt-day-after | input type="date" | inclusif jusqu'au jour choisi |
last-n-days | input N | borne dynamique « N derniers jours », recalculee a chaque diffusion |
current-year | checkbox | plage de l'annee en cours (dynamique) |
current-month | checkbox | plage du mois en cours (dynamique, #682) |
Une checkbox cochée transmet la valeur 'on' — c'est ce qui active les
opérateurs sans valeur comme current-year. Une valeur vide (champ effacé,
checkbox décochée) retire le filtre. Les valeurs sont percent-encodées dans la clause colon.
year-start-month
year-of et current-year découpent l'année civile. Une bonne partie des
données publiques ne s'y range pas : année scolaire, exercice comptable, saison.
year-start-month déplace le mois de début de l'année sur le filtre —
1 (défaut) pour l'année civile, 9 pour l'année scolaire, 4
pour l'exercice comptable britannique, 7 pour l'exercice australien, 10
pour une saison.
<dsfr-data-context-filter field="date_inscription" label="Année scolaire" operator="year-of" year-start-month="9" ui="ui-annee"> </dsfr-data-context-filter>
La clause reste une plage gte + lt : elle se délègue au serveur comme
n'importe quelle autre, aucun adaptateur n'est concerné. Avec year-start-month="9",
la valeur « 2024 » filtre [2024-09-01, 2025-09-01) et s'affiche « 2024-2025 » dans
les tags. Une valeur plus précise (« 2025-03-10 ») désigne l'année qui la CONTIENT — 2024-2025
ici — ce qui permet de nourrir l'opérateur d'un <input type="date">. Côté
client seul, une colonne d'année scolaire se dérive aussi avec le compute de
dsfr-data-normalize ; l'attribut existe pour les jeux
qu'on ne veut pas rapatrier.
Dialectes : la clause est construite en colon (pivot) puis traduite
au whereFormat de chaque adapter cible (ODSQL pour OpenDataSoft, colon
pour Tabular/Grist). Les bornes dynamiques ne figent jamais de date dans le DOM, et
l'URL serialise l'intention (« 30 »), pas les dates resolues : un lien
partage ne gele pas de vieilles données.
| Attribut | Type | Valeurs | Description |
|---|---|---|---|
for | String | requis | Id du dsfr-data-context observé |
clear-all | Boolean | présent / absent | Bouton « Tout effacer » (#679) — un seul geste pour vider tous les filtres du contexte |
dsfr-data-context-tags liste les filtres actifs sous forme de tags
supprimables ; il ne s'insère pas dans une phrase.
<dsfr-data-context-value> (#742) rend la valeur courante d'un filtre comme du
texte, seule ou interpolée dans un gabarit — pour qu'un titre de page dise « Résultats pour la
Gironde » plutôt que « Résultats ».
| Attribut | Type | Valeurs | Description |
|---|---|---|---|
for | String | requis | Id du dsfr-data-context observe |
field | String | field="departement" | Champ dont la valeur est rendue — raccourci de template="{{champ}}". Ignore quand template est pose. |
template | String | "Résultats pour {{champ}}" | Gabarit texte : chaque {{champ}} est remplace par la valeur courante du filtre de ce champ (« Résultats pour {{departement}} »). |
fallback | String | vide = ne rend rien | Texte rendu tant qu'un champ cite n'a aucune valeur — le repli declare de #742. Vide : le composant ne rend rien du tout. |
live | Boolean | présent / absent | Région live polie : le titre annonce le changement de contenu aux lecteurs d'écran. À poser sur UN seul élément de la page. |
<h2>
<dsfr-data-context-value for="ctx"
template="Résultats pour {{departement}}"
fallback="Résultats pour toute la France" live>
</dsfr-data-context-value>
</h2>
Le gabarit accepte plusieurs marqueurs dans la même phrase
(template="{{departement}} en {{annee}}") ; field="departement" est le
raccourci de template="{{departement}}", ignoré si template est posé.
Un seul champ cité sans valeur suffit à basculer sur fallback : « Résultats pour »
serait pire qu'une phrase de repli. Sans fallback, le composant ne rend rien du tout.
La valeur rendue est celle qu'affichent les tags, y compris pour une facette ou une recherche
enregistrée sur le contexte par context="id".
Accessibilité : live fait du composant une région live discrète
(aria-live="polite", role="status"), pour qu'un titre annonce le
changement de contexte. À poser sur un seul élément de la page — trois libellés
qui parlent en même temps sont un bruit, pas une aide. Le texte rendu est du texte : la valeur
d'un filtre traverse le rendu Lit, jamais l'analyseur HTML. Le composant vit en light DOM et
hérite donc des styles de la page (titre, paragraphe).
| Événement | detail | Description |
|---|---|---|
dsfr-data-context-change | — | Émis sur l'élément <dsfr-data-context> à chaque application ou retrait d'un filtre. C'est cet événement qu'écoute dsfr-data-context-tags pour se rafraîchir. |
dsfr-data-context-filter, dsfr-data-context-tags et
dsfr-data-context-value n'émettent aucun événement custom propre : tout passe par le
contexte et les commandes du data-bridge. dsfr-data-context-value se contente
d'écouter dsfr-data-context-change, comme les tags.