Skip to content

fix(a11y): drop role=textbox / aria-multiline from innerdocbody (#7778)#7782

Merged
JohnMcLear merged 1 commit into
developfrom
fix/a11y-7778-drop-textbox-role
May 16, 2026
Merged

fix(a11y): drop role=textbox / aria-multiline from innerdocbody (#7778)#7782
JohnMcLear merged 1 commit into
developfrom
fix/a11y-7778-drop-textbox-role

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

Summary

  • Removes role="textbox" and aria-multiline="true" from innerdocbody
  • Keeps aria-label="Pad content" and aria-describedby="editor-keyboard-hint"
  • Adds Playwright regression test asserting the attrs stay absent

Lighter alternative to the AT-only read mirror originally sketched in #7778 — pure ARIA strip, no DOM restructuring, no plugin impact.

Why

Murphy's 2026-05-16 re-test of #7255 said "you still can't cycle through the text properly line by line to press links and such". The narrower fixes in #7777 don't address that — it's the textbox role doing the damage:

  • role="textbox" + aria-multiline="true" pin NVDA/JAWS into focus mode for the entire pad.
  • In focus mode: arrow keys move the caret one character at a time, P/H/K rotor shortcuts are suppressed, links don't appear in the rotor's links list.
  • That matches the reported symptoms exactly.

contenteditable="true" alone is enough to tell AT this is editable. Without the textbox role, NVDA/JAWS browse the content as document-mode HTML, restoring line-by-line arrow nav and rotor access to headings + links.

Acceptance criteria from #7778 — partial

This is the cheap experiment first. If the ARIA strip alone unblocks line / link / heading navigation in real AT, the heavier mirror approach in #7778 isn't needed. If headings still feel flat, we layer role="paragraph" onto each <div class="ace-line"> next.

Test plan

  • CI: new innerdocbody does not advertise role=textbox / aria-multiline (#7778) Playwright test passes
  • CI: existing Inaccessibility to screenreaders #7255 regression tests (skip-to-content, line-number sidediv hidden, keyboard-hint text) still pass
  • Local manual: arrow-down through a multi-line pad in NVDA/JAWS/VoiceOver and confirm caret moves line-by-line in browse mode
  • Local manual: open the rotor / elements list and confirm headings (h1-h4 from ep_headings2) + links surface
  • @StrangeGirlMurph real-screen-reader retest of Inaccessibility to screenreaders #7255 once this is on a build

Refs #7255 #7777

🤖 Generated with Claude Code

Murphy's 2026-05-16 re-test of #7255 reported "you still can't cycle
through the text properly line by line to press links and such". The
narrower toolbar/measurement fixes in #7777 don't address this — it's
caused by the editor body advertising textbox semantics.

role="textbox" + aria-multiline="true" pin NVDA/JAWS into focus mode for
the whole pad. In focus mode arrow keys move the caret one character at
a time, the P/H/K rotor shortcuts are suppressed, and links don't
surface in the links list. That matches Murphy's symptoms exactly.

contenteditable="true" by itself is enough to tell AT this is editable.
Without the textbox role, NVDA/JAWS browse the content as document-mode
HTML — line-by-line arrow nav, headings rotor, links list all return.
aria-label / aria-describedby stay so the pad is still announced as
"Pad content" with the keyboard hint on focus.

This is the lighter alternative to the AT-only read mirror originally
sketched in #7778 — ARIA-only, no DOM restructuring, no plugin impact.

Refs #7255 #7777
@JohnMcLear JohnMcLear marked this pull request as ready for review May 16, 2026 12:59
@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Remove textbox role from editor body for AT navigation

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Removes role="textbox" and aria-multiline="true" from innerdocbody element
  - These attributes forced NVDA/JAWS into focus mode, hiding links/headings from rotor
  - Prevented line-by-line arrow navigation through pad content
• Retains aria-label and aria-describedby for accessibility announcements
• Adds Playwright regression test to ensure attributes remain absent
Diagram
flowchart LR
  A["innerdocbody element"] -->|remove| B["role=textbox"]
  A -->|remove| C["aria-multiline=true"]
  A -->|keep| D["aria-label=Pad content"]
  A -->|keep| E["aria-describedby=editor-keyboard-hint"]
  B -->|result| F["AT browses as document"]
  C -->|result| F
  F -->|enables| G["Line-by-line navigation"]
  F -->|enables| H["Rotor access to links/headings"]
Loading

Grey Divider

File Changes

1. src/static/js/ace.ts 🐞 Bug fix +6/-2

Remove textbox role from editor body

• Removes setAttribute('role', 'textbox') from innerdocbody body element
• Removes setAttribute('aria-multiline', 'true') from innerdocbody body element
• Adds detailed comment explaining why textbox role and aria-multiline are omitted
• Retains aria-label and aria-describedby attributes for accessibility

src/static/js/ace.ts


2. src/tests/frontend-new/specs/a11y_dialogs.spec.ts 🧪 Tests +16/-0

Add regression test for innerdocbody ARIA attributes

• Adds new Playwright test innerdocbody does not advertise role=textbox / aria-multiline (#7778)
• Tests that innerdocbody element has no role attribute
• Tests that innerdocbody element has no aria-multiline attribute
• Verifies aria-label and aria-describedby attributes are still present

src/tests/frontend-new/specs/a11y_dialogs.spec.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 16, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)

Context used

Grey Divider


Remediation recommended

1. Non-retrying attr assertions 🐞 Bug ☼ Reliability
Description
The new Playwright regression test uses getAttribute()+toBeNull() for role/aria-multiline, which
does not auto-retry and can be flaky if those attributes are set/cleared asynchronously during init
or by plugins. Other assertions in the same spec use Playwright’s auto-waiting matchers, so this
inconsistency increases intermittent CI failure risk.
Code

src/tests/frontend-new/specs/a11y_dialogs.spec.ts[R293-294]

+  expect(await body.getAttribute('role')).toBeNull();
+  expect(await body.getAttribute('aria-multiline')).toBeNull();
Evidence
The new test uses one-shot getAttribute() checks for absence, while the same file uses
Playwright’s auto-waiting toHaveAttribute() matcher elsewhere, indicating the reliability gap and
inconsistency.

src/tests/frontend-new/specs/a11y_dialogs.spec.ts[283-297]
src/tests/frontend-new/specs/a11y_dialogs.spec.ts[24-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The test asserts missing attributes via `getAttribute()` + `toBeNull()`, which is a one-shot read and does not benefit from Playwright’s built-in polling/retry. This can cause flakes if the DOM is still settling or if any async code temporarily modifies attributes.

### Issue Context
The same spec file already uses `await expect(locator).toHaveAttribute(...)` for other ARIA checks, which is the preferred pattern because it auto-waits until the condition is satisfied.

### Fix Focus Areas
- src/tests/frontend-new/specs/a11y_dialogs.spec.ts[283-297]

### Suggested change
Replace the one-shot assertions with auto-waiting expectations, e.g.:

```ts
await expect(body).not.toHaveAttribute('role', /.*/);
await expect(body).not.toHaveAttribute('aria-multiline', /.*/);
// or more specific:
// await expect(body).not.toHaveAttribute('role', 'textbox');
// await expect(body).not.toHaveAttribute('aria-multiline', 'true');
```

This keeps the intent (attributes must be absent) but makes the test resilient to timing variance.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@JohnMcLear
Copy link
Copy Markdown
Member Author

Waiting user testing.

@JohnMcLear JohnMcLear merged commit a4261c2 into develop May 16, 2026
31 checks passed
@JohnMcLear JohnMcLear deleted the fix/a11y-7778-drop-textbox-role branch May 16, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant