Duplicate JSX attributes in BountyCard and Avatar break the build and silently revert the #654 and #653 accessibility fixes

Aperta Adatta ai principianti
#835 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
78/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
next.js, react, typescript

Direzione di ricerca

Leggi src/components/bounty/BountyCard.tsx e src/components/ui/Avatar.tsx nelle posizioni indicate, quindi esamina BountyCard.test.tsx e Avatar.test.tsx per individuare le asserzioni esistenti. Esegui npx tsc --noEmit per confermare gli errori dovuti agli attributi duplicati. Il lavoro è completato quando i duplicati sono risolti preservando l’etichetta accessibile descritta, la sorgente sicura dell’avatar e il testo alternativo fornito, e quando entrambe le asserzioni di regressione passano.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

accessibility bug

Location

  • src/components/bounty/BountyCard.tsx:37-40 — BountyCard
  • src/components/ui/Avatar.tsx:57-61 — Avatar

Problem

Both components carry the same attribute twice on one JSX element, left behind by an unreconciled merge. React keeps the last occurrence and discards the earlier one, so in each case the newer accessibility fix is the one that gets thrown away.

BountyCard
<Link
  href={`/issues/${bounty.id}`}
  prefetch={false}
  aria-label={accessibleLabel}                      // line 37  (#654 fix)
  className={`... before:left-0 ...`}               // line 38
  aria-label={`${bounty.title}, ${formatCurrency(bounty.reward, bounty.asset)}`}  // line 39 (old)
  className={`... before:start-0 ...`}              // line 40
>

aria-label and className are each specified twice. The surviving aria-label is the old short one, so accessibleLabel — the full "title, from org/repo, issue N, reward X, status: funded, difficulty: advanced, due in 5 days, claimed by Y" string built at lines 20-31 for closed issue #654 — is computed on every render and then dropped. The card is back to announcing only its title and reward, which is precisely what #654 reported. The variable also becomes unused, so no-unused-vars style lint rules flag it.

The two className values differ in one class: before:left-0 versus the logical before:start-0. The logical property is the one that survives, which is the correct outcome, but it is surviving by accident.

Avatar
<Image
  src={src ?? seedToUrl(seed)}   // line 58 — bypasses isSafeAvatarUrl
  alt={alt ?? seed}              // line 59 — honours the `alt` prop
  src={resolved}                 // line 60 — safety-checked + error fallback
  alt={seed}                     // line 61 — ignores the `alt` prop
  ...

Here the survivors are src={resolved} (good — the host-allowlist and onError fallback logic is the one that applies) and alt={seed} (bad — the alt prop is accepted by the component's signature, documented at lines 40-41 as "Use empty string when the avatar is decorative (next to a visible name)", and then silently ignored).

Every call site that passes alt="" therefore still gets alt={seed}. That reintroduces closed issue #653: src/components/dashboard/ActivityList.tsx:42 renders <Avatar seed={event.handle} /> immediately before a visible {event.handle}, so a screen reader reads the handle twice per activity row.

Both files fail to compile

Duplicate JSX attributes are a TypeScript error, not a warning: TS17001: JSX elements cannot have multiple attributes with the same name. tsc --noEmit and next build both fail on these two files, and react/jsx-no-duplicate-props would have caught them too.

Why it matters

  • The app does not build, so no route is reachable.
  • Once it does build (by resolving these duplicates the wrong way, i.e. keeping what React keeps today), two closed accessibility issues are quietly back in place: bounty cards on /issues lose their status/deadline/claimant context for screen-reader users (#654), and decorative avatars double-announce every name in the activity feed (#653). Both regressions are invisible in code review because the "fix" code is still sitting right there in the file.

Reproduction

  1. Check out the current default branch, run npm ci, then npx tsc --noEmit. Four TS17001 errors are reported across the two files.
  2. Delete lines 39-40 of BountyCard.tsx and lines 58-59 of Avatar.tsx — i.e. resolve each duplicate the way React resolves it at runtime — and build again.
  3. Open /issues with VoiceOver or NVDA and move through the links list. Each bounty card announces only "<title>, 500 USDC"; status, difficulty, deadline and claimant are not announced.
  4. Open any dashboard with the activity feed and move through it. Each row announces the contributor handle twice: once as the avatar's alt text, once as the adjacent visible text.

Suggested fix

  1. BountyCard.tsx: delete the old aria-label on line 39 and the before:left-0 className on line 38, keeping aria-label={accessibleLabel} (line 37) and the before:start-0 className (line 40).
  2. Avatar.tsx: delete the raw src={src ?? seedToUrl(seed)} (line 58) and alt={seed} (line 61), keeping src={resolved} (line 60) and alt={alt ?? seed} (line 59).
  3. Enable react/jsx-no-duplicate-props as an error in eslint.config.mjs and run tsc --noEmit in CI, so this class of merge damage is caught mechanically. Two other components on this branch have the same kind of unresolved merge.
  4. Re-add a regression assertion to each test file: BountyCard.test.tsx should assert the link's accessible name contains the status and deadline, and Avatar.test.tsx should assert alt="" is honoured when passed.
Lingua principale
TypeScript
Stelle
1
Fork
94
Merge medio
1h 2m
PR unite (30g)
25

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di MergeFi/frontend

Tutte le issue di MergeFi/frontend

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.