You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
BatchForm's per-recipient address and amount fields are the only Input usages in the app's financial forms with no label prop and no aria-label fallback, unlike every other form in the app.
Neither Input receives a label. Input's own implementation only renders a <label> element — and only generates an id/htmlFor pairing at all — when label is provided:
With no label and no id passed explicitly, inputId is undefined, no <label> renders, and the <input> gets no id either — so these two fields have no accessible name at all: no associated <label>, no aria-label, no aria-labelledby. A placeholder is not an accessible name (it disappears once text is entered, and most screen readers don't reliably announce it as a label in the first place).
Why it matters
This is the same defect category as the already-filed "Form inputs in AddContact and RecipientStep lack associated label elements" issue in this repo (#10), but in a file that issue doesn't mention: BatchForm.tsx is the only other place in the entire app's financial forms (SendForm, EscrowForm, SubscriptionForm, CreateRequestForm all correctly pass label="..." to every Input/Select) that has this specific gap, and arguably a higher-stakes one — it's per-recipient address and amount entry for a batch of up to 100 payments (MAX_BATCH_RECIPIENTS), sent as a single atomic transaction the user cannot partially undo. A screen-reader user filling out this form hears "edit text" (or similar generic control name) for every recipient row's address and amount fields, with no way to distinguish "recipient 3's address" from "recipient 3's amount" from "recipient 4's address" by name alone — they'd have to rely entirely on DOM/reading order and memory to keep track of which field is which while filling in multiple recipients.
Reproduction
Open the Batch Send form with a screen reader (or inspect the rendered DOM) and add a second recipient row. Neither the address nor amount <input> for any row has an id, and no <label> element referencing it exists anywhere in the row — confirmed by Input.tsx's own conditional rendering, since label is never passed at either call site.
Suggested fix
Pass a (visually hidden, if the current compact layout is intentional) label to both Inputs, e.g. label={Recipient ${index + 1} address} / label={Recipient ${index + 1} amount} using the app's existing VisuallyHidden component (src/components/common/VisuallyHidden.tsx, itself currently unused anywhere — see its own definition file) if a visible label would break the current row layout, or at minimum add explicit aria-label props with the same per-row-indexed text.
Additional Notes
src/components/BatchSend/BatchForm.tsx:89-105, src/components/ui/Input.tsx:13-29 (the conditional-label mechanism that silently produces no accessible name when label is omitted).
Testing strategy: extend BatchForm.test.tsx (which currently only asserts on validation/submission behavior) with an assertion that every rendered recipient row's address and amount inputs have an accessible name (e.g. via Testing Library's getByRole('textbox', { name: /recipient 1 address/i })), which would fail against the current markup.
Problem
BatchForm's per-recipient address and amount fields are the onlyInputusages in the app's financial forms with nolabelprop and noaria-labelfallback, unlike every other form in the app.Neither
Inputreceives alabel.Input's own implementation only renders a<label>element — and only generates anid/htmlForpairing at all — whenlabelis provided:With no
labeland noidpassed explicitly,inputIdisundefined, no<label>renders, and the<input>gets noideither — so these two fields have no accessible name at all: no associated<label>, noaria-label, noaria-labelledby. Aplaceholderis not an accessible name (it disappears once text is entered, and most screen readers don't reliably announce it as a label in the first place).Why it matters
This is the same defect category as the already-filed "Form inputs in AddContact and RecipientStep lack associated label elements" issue in this repo (#10), but in a file that issue doesn't mention:
BatchForm.tsxis the only other place in the entire app's financial forms (SendForm,EscrowForm,SubscriptionForm,CreateRequestFormall correctly passlabel="..."to everyInput/Select) that has this specific gap, and arguably a higher-stakes one — it's per-recipient address and amount entry for a batch of up to 100 payments (MAX_BATCH_RECIPIENTS), sent as a single atomic transaction the user cannot partially undo. A screen-reader user filling out this form hears "edit text" (or similar generic control name) for every recipient row's address and amount fields, with no way to distinguish "recipient 3's address" from "recipient 3's amount" from "recipient 4's address" by name alone — they'd have to rely entirely on DOM/reading order and memory to keep track of which field is which while filling in multiple recipients.Reproduction
<input>for any row has anid, and no<label>element referencing it exists anywhere in the row — confirmed byInput.tsx's own conditional rendering, sincelabelis never passed at either call site.Suggested fix
Inputs, e.g.label={Recipient ${index + 1} address}/label={Recipient ${index + 1} amount}using the app's existingVisuallyHiddencomponent (src/components/common/VisuallyHidden.tsx, itself currently unused anywhere — see its own definition file) if a visible label would break the current row layout, or at minimum add explicitaria-labelprops with the same per-row-indexed text.Additional Notes
src/components/BatchSend/BatchForm.tsx:89-105,src/components/ui/Input.tsx:13-29(the conditional-label mechanism that silently produces no accessible name whenlabelis omitted).AddContact/RecipientStep) — same underlying defect shape, different, previously-uncovered form.BatchForm.test.tsx(which currently only asserts on validation/submission behavior) with an assertion that every rendered recipient row's address and amount inputs have an accessible name (e.g. via Testing Library'sgetByRole('textbox', { name: /recipient 1 address/i })), which would fail against the current markup.