**This fixes a regression I introduced in #2965, found by re-running the full dashboard lane on `main` rather than trusting the targeted runs I did at the time.** `AddNodeModal` and `ConnectNodeModal` are red on main: ``` → Found multiple elements with the text of: Add Node → Found multiple elements with the text of: Connect to Node ``` ### Cause #2965 dropped the redundant `" dialog"` suffix from each `FloatingWindow`'s `ariaLabel`. That was correct — `role="dialog"` already conveys it. What I missed is that six of those modals **also** put an `aria-label` with the *same* text on their own inner `<div>`: ```jsx <FloatingWindow ariaLabel={t("nodes.addNode", "Add Node")} …> <div className="modal modal-md add-node-modal" aria-label={t("nodes.addNode", "Add Node")}> ``` Before #2965 the two differed (`"Add Node dialog"` vs `"Add Node"`), so `getByLabelText("Add Node")` matched exactly one element. Now both match. ### Why the inner one goes, not the dialog's Those inner labels sit on **role-less `<div>`s**, where assistive technology ignores `aria-label` entirely — it was never conveying anything to anyone. Removing it restores a single accessible name per dialog and needs no test changes. ### Surface enumeration — four of the six were latent Only two surfaced as failures; the other four have no test querying by that name, so they would have shipped a duplicate accessible name silently. Found by scanning every component for an inner `aria-label` whose expression matches its own `ariaLabel` prop: | modal | was it red? | |---|---| | `AddNodeModal` | red on main | | `ConnectNodeModal` | red on main | | `GroupTaskModal` | latent | | `NodeDetailModal` | latent | | `ScriptsModal` | latent | | `WorkflowAddStepModal` | latent | ### Five more, deliberately untouched `AgentDetailView`, `PlanningModeModal`, `SettingsModal` (`role="region"`), `ScheduledTasksModal` (`role="listbox"`) and `NewTaskModal` (`role="dialog"`) also carry their dialog's name on an inner element — but those elements **have a role**, so the label is meaningful rather than dead markup. A listbox named "Automations" inside a dialog named "Automations" is redundant, not broken, and renaming it is a UX decision rather than a cleanup. Left alone and recorded here. **Verified:** 93/93 across `AddNodeModal`, `ConnectNodeModal`, `NodesView`, `GroupTaskModal`, `ScriptsModal` and the #2965 aria guard; `tsc -p tsconfig.app.json` 0 errors; lint clean; FNXC gate exit 0. Product-code change to a11y markup, so this is user-visible but needs no operator-facing note — say the word if you want a changeset. **Measured dashboard-lane state on main before this PR:** `3 failed | 11173 passed`. Two are these; the third is `MainContent.planning-project-remount`, which belongs to #2420 and is detailed there. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
20 KiB
20 KiB