fix(browser): read a native label for any labelable element, not just form fields - #1169
Conversation
… form fields el.labels was only read inside the isInput/isTextArea/isSelect guard in getAccessibleName, so a <button role="combobox"> (or <meter>/<output>/ <progress>) with a native <label for> never picked up that label. It fell through to its own text content (NAME_FROM_CONTENT) or returned '' for roles outside that set, so by: role + name lookups found nothing. Hoist the labels read out of the guard so it runs for any element with a non-empty .labels, before the input-specific value/placeholder fallbacks and before name-from-content. Closes reticlehq#1145 Signed-off-by: drakeo338 <paranoyouz@gmail.com>
|
Thanks for your first pull request to Reticle! A maintainer aims to review within two days. Before then, |
|
| const labels = (el as Partial<HTMLInputElement>).labels; | ||
| if (labels !== null && labels !== undefined && labels.length > 0) { | ||
| const text = [...labels] |
There was a problem hiding this comment.
Unrestricted labels read can throw. Snapshots call
getAccessibleName for non-labelable elements too. If a custom element has a labels property with a positive length that is not iterable, it passes this check and [...labels] throws, aborting the snapshot. Restrict the read to native labelable elements or validate the collection before spreading it.
| } | ||
| }); | ||
|
|
||
| it('names a plain button from its label', () => { |
There was a problem hiding this comment.
Other labelable elements remain untested. Both new cases create buttons, but the change and release note also cover
meter, output, and progress. Add cases for those elements so their native-label behavior is protected against regressions.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
getAccessibleName read el.labels unconditionally, for every element a snapshot walks - not only input/textarea/select/button/meter/output/ progress. A custom element defining its own labels property for unrelated reasons (a tag list, a chart's category labels) could throw on that read or return something that isn't a NodeList, breaking naming for elements this function has no business reading .labels from. Add isMeter/isOutput/isProgress alongside the existing isInput/ isTextArea/isSelect/isButton cross-realm checks and gate the read on that full labelable set, and add tests for meter/output/progress labelled via a native <label for>, matching the release note's claim. Signed-off-by: drakeo338 <paranoyouz@gmail.com>
|
Fix is in the right place: |
isLabelable already narrows el to the union of labelable elements, so the Partial<HTMLInputElement> cast and the accompanying undefined check were redundant. Signed-off-by: drakeo338 <paranoyouz@gmail.com>
|
Dropped the cast, since |
divshekhar
left a comment
There was a problem hiding this comment.
Cast is gone, thanks. Good to go once CI is green.
What & why
Closes #1145
A
<button role="combobox">(or<meter>/<output>/<progress>) with a native<label for>was reported as unnamed:getAccessibleNameonly readel.labelsinside the input/textarea/select guard, so anything outside that set fell through to its own text content or returned''.{ role: "combobox", name: "…" }lookups on a correctly labelled control found nothing. This hoists thelabelsread out of the guard so it runs for any labelable element, before the input-specific fallbacks and before name-from-content.How it was verified
Added a unit test with the button/label markup from the issue. Reverted the fix locally and ran the added test: it failed (2 failing) without the change, and passes (0 failing) with it — confirming the test exercises the fix, per the AI-assisted contribution guideline.
Gates run
pnpm lint && pnpm typecheck && pnpm test:unit(~2 min — always)pnpm test:e2e(~8 min) — touched the tool surface,core, an observer, or telemetrypnpm gate:install(~15 min) — touchedreticle init,vite-plugin,next, orbabel-pluginpnpm test:e2e:desktop(~3 min) — touchedadapters/realm/electron,adapters/realm/tauri, or desktop captureChecklist
git commit -s) — CI's DCO check fails the PR without it. Already pushed?git rebase --signoff origin/main && git push --force-with-leaseany, no free strings (wire strings live in@reticlehq/core), no non-null!console.logor internal tracking codes left in the diff.changes/(never editCHANGELOG.md— that file is assembled at release time, and editing it is what makes PRs conflict; format in.changes/README.md)docs/telemetry.md) and are covered by a test