Skip to content

fix(Alert): honour explicit role={undefined} to suppress role attribute#490

Open
jrlprost wants to merge 2 commits into
codegouvfr:mainfrom
jrlprost:fix/alert-role-undefined-rgaa
Open

fix(Alert): honour explicit role={undefined} to suppress role attribute#490
jrlprost wants to merge 2 commits into
codegouvfr:mainfrom
jrlprost:fix/alert-role-undefined-rgaa

Conversation

@jrlprost
Copy link
Copy Markdown

Fixes #478.

Problème

Passer role={undefined} sur <Alert /> rendait quand même role="alert" dans le DOM, ce qui est une non-conformité RGAA (critère 8.7 — changement de contexte non demandé par l'utilisateur·rice).

La cause : le default dans le destructuring (role = "alert") remplace silencieusement undefined par "alert", même lorsque undefined est explicitement fourni par l'appelant·e.

Correction

Au lieu d'un default dans le destructuring, on utilise "role" in props pour distinguer les deux cas :

  • role non fourni → comportement par défaut conservé : "alert" (compatibilité ascendante intacte)
  • role={undefined} explicitement passé → aucun attribut role dans le DOM rendu
// Avant
role = "alert",

// Après
role: roleFromProps,
// ...
const role = "role" in props ? roleFromProps : ("alert" as const);

Le rendu JSX passe de role ? à role !== undefined pour gérer correctement la valeur undefined :

// Avant
{...(role ? { "role": role } : {})}

// Après
{...(role !== undefined ? { "role": role } : {})}

La même correction est appliquée au spread conditionnel refShouldSetRole (ré-annonce par les lecteurs d'écran quand l'alerte se rouvre).

Tests

  • La pre-commit hook (ESLint + Prettier) passe sur le fichier modifié.
  • Aucun nouveau warning lint introduit (Notice.tsx avait une warning pre-existing).
  • Comportement attendu : <Alert role={undefined} ... /> → pas d'attribut role dans le DOM rendu.

When role={undefined} is passed, the component was still rendering
role="alert" because the destructuring default applies to undefined
values unconditionally.

Fix: remove the default from the destructuring and use "role" in props
to distinguish "caller did not pass role" (default to "alert") from
"caller explicitly passed undefined" (no role attribute rendered).

Fixes codegouvfr#478
Comment thread src/Alert.tsx Outdated
style={style}
{...(refShouldSetRole.current && { "role": role })}
{...(role ? { "role": role } : {})}
{...(refShouldSetRole.current && role !== undefined && { "role": role })}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

à quoi sert cette ligne, c'est toujours écrasé en-dessous, non ?

The first spread was always overridden by the second one: when
role !== undefined both spreads set the same value, and when role
is undefined neither fires. Drop the dead ref and simplify to a
single conditional spread.

Addresses review comment from revolunet.
@jrlprost
Copy link
Copy Markdown
Author

jrlprost commented May 20, 2026

Vous avuez raison. Le premier est redondant : quand role !== undefined les deux gèrent la même valeur, et quand role === undefined aucun des deux ne s'applique de toute façon. J'ai supprimé ce spread inutile ainsi que la ref refShouldSetRole et son assignation. Le diff est meilleur.

@revolunet revolunet requested a review from lsagetlethias May 20, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Régression] - Composant Alert et role par defaut même undefined => non conformité RGAA

2 participants