Skip to content

BUGFIX: template-no-redundant-role — case-insensitive match + <select>→combobox#2727

Draft
johanrd wants to merge 5 commits intoember-cli:masterfrom
johanrd:fix/no-redundant-role-case-and-select
Draft

BUGFIX: template-no-redundant-role — case-insensitive match + <select>→combobox#2727
johanrd wants to merge 5 commits intoember-cli:masterfrom
johanrd:fix/no-redundant-role-case-and-select

Conversation

@johanrd
Copy link
Copy Markdown
Contributor

@johanrd johanrd commented Apr 21, 2026

Two fixes in one PR (shared rule, no semantic overlap).

1. Case-insensitive role comparison

Fix: lowercase the role value before the ROLE_TO_ELEMENTS / LANDMARK_ROLES lookups.

2. <select> maps to combobox (conditional)

  • Premise: Per HTML-AAM §4 main conformance table, <select> has an implicit role of combobox only when neither multiple nor size > 1 is set; otherwise the implicit role is listbox. Both mappings are documented in the spec.
  • Problem: ROLE_TO_ELEMENTS only listed listbox: ['select']. <select role="combobox"> was silently accepted.

Fix: add combobox: ['select'], and gate the combobox redundancy check on the multiple / size attributes so <select role="combobox" multiple> and <select role="combobox" size="5"> are not falsely flagged. The implicit-role check mirrors jsx-a11y's src/util/implicitRoles/select.js.

Tests cover both fixes, including the uppercase + conditional-combobox interaction.

Prior art

Verified each peer in source:

Plugin Rule Verified behavior
jsx-a11y no-redundant-roles Calls getExplicitRole (lowercases via role.toLowerCase() at src/util/getExplicitRole.js:18–23); src/util/implicitRoles/select.js:9–17 honors multiple and size > 1.
vuejs-accessibility no-redundant-roles Exact-match comparison at line 78 (implicitRoleSet.includes(explicitRole)); no lowercasing in the rule or its utils. <body role="DOCUMENT"> is not flagged there either.
@angular-eslint/template no equivalent rule angular-eslint ships valid-aria, role-has-required-aria, and related ARIA-validity rules, but no redundant-role check. Role-vs-implicit-role redundancy is not audited.
lit-a11y no-redundant-role Same getExplicitRole-style pattern as jsx-a11y (case-insensitive via its own lib/utils/getExplicitRole.js:11–12).

Audit fixture

Translated peer-plugin test fixture at tests/audit/no-redundant-roles/peer-parity.js. Not wired into the default test run.

johanrd added 3 commits April 21, 2026 07:41
…x mapping

Two changes.

1. Compare role tokens case-insensitively. ARIA role values are
   ASCII-case-insensitive (HTML-AAM inherits HTML's attribute-comparison
   semantics). Before: <body role="DOCUMENT"> was not flagged because
   "DOCUMENT" didn't match "document" in ROLE_TO_ELEMENTS.

2. Add 'combobox' → ['select'] mapping. <select> without `multiple` or
   `size > 1` has an implicit role of "combobox" per HTML-AAM §4.1.
   Before: <select role="combobox"> was silently accepted as a redundant
   role.

Two new invalid tests cover the fixes.
Per HTML-AAM, <select>'s implicit role is "combobox" only when neither
`multiple` nor `size > 1` is present; otherwise it is "listbox". The
previous commit added `combobox: ['select']` unconditionally, which
caused false positives for <select role="combobox" multiple> and
<select role="combobox" size="5"> (where combobox disagrees with the
implicit listbox role and therefore is not redundant).

Add a selectHasComboboxImplicitRole helper mirroring jsx-a11y's
src/util/implicitRoles/select.js, and short-circuit the redundancy
check for <select role="combobox"> when the implicit role is actually
listbox.

Update the code comment on the `combobox: ['select']` entry to reflect
this (and drop the inaccurate "§4.1" reference — the <select> mapping
lives in the HTML-AAM main conformance table, section 4, without a
numbered subsection).

Tests:
- valid: <select role="combobox" multiple></select>
- valid: <select role="combobox" size="5"></select>
- invalid: <select role="combobox" size="1"></select>  (size=1 → combobox)
- invalid: <select role="COMBOBOX"></select>           (case + implicit)
…ases

Translates 35 cases from peer-plugin rules:
  - jsx-a11y no-redundant-roles
  - vuejs-accessibility no-redundant-roles
  - lit-a11y no-redundant-role

Fixture documents parity after this fix:
  - Case-insensitive role comparison (body role="DOCUMENT" now flagged).
  - Default <select role="combobox"> flagged; <select multiple>/size>1 still
    valid (implicit listbox, not combobox).

Remaining divergences (ul/ol role="list", <a role="link"> without href)
are annotated inline. Not wired into the default vitest run.
johanrd added 2 commits April 22, 2026 14:03
- Normalize role via trim() + first-token split before lookup so values
  like 'COMBOBOX' or 'combobox listbox' are handled consistently
  (ARIA role attribute is a space-separated fallback list; only the
  first supported token is effective).
- getSelectImplicitRole() now returns 'combobox' | 'listbox' | 'unknown'.
  A dynamic <select size={{...}}> previously fell through as 'combobox'
  and produced false positives on role="combobox"; we now bail on
  'unknown' instead of flagging.
@johanrd johanrd force-pushed the fix/no-redundant-role-case-and-select branch from df79aaf to 00c823d Compare April 22, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant