Skip to content

Commit b629330

Browse files
committed
test(no-aria-hidden-on-focusable): promote peer-parity cases, drop audit file
Promoted non-redundant cases: click-modifier valid (event handlers don't add focusability), and three DIVERGENCE invalids (button[tabindex=-1][aria-hidden], plus descendants with tabindex=-1 under aria-hidden ancestors). Both divergences are intentional — our rule treats tabindex="-1" as programmatically focusable regardless of value, unlike jsx-a11y and vue-a11y.
1 parent 5ce8e2c commit b629330

2 files changed

Lines changed: 26 additions & 281 deletions

File tree

tests/audit/no-aria-hidden-on-focusable/peer-parity.js

Lines changed: 0 additions & 281 deletions
This file was deleted.

tests/lib/rules/template-no-aria-hidden-on-focusable.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ ruleTester.run('template-no-aria-hidden-on-focusable', rule, {
6565
'<template><div aria-hidden="true"><img alt="static" /></div></template>',
6666
// <input type="hidden"> is non-focusable per isFocusable.
6767
'<template><div aria-hidden="true"><input type="hidden" /></div></template>',
68+
// Event modifiers (`{{on "click" ...}}`) do not make an element focusable —
69+
// only tabindex / inherent native focusability does.
70+
'<template><div {{on "click" this.handler}} aria-hidden="true"></div></template>',
71+
6872
// Dynamic mustache descendants are not inspected.
6973
'<template><div aria-hidden="true">{{this.label}}</div></template>',
7074
// `@arg`-prefixed tag is opaque.
@@ -112,6 +116,15 @@ ruleTester.run('template-no-aria-hidden-on-focusable', rule, {
112116
output: null,
113117
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
114118
},
119+
// DIVERGENCE from jsx-a11y + vue-a11y: both accept button[tabindex="-1"][aria-hidden="true"]
120+
// reasoning that tabindex="-1" removes the element from the tab order.
121+
// Our rule treats tabindex="-1" as still programmatically focusable (reachable via
122+
// .focus() and click), so aria-hidden on it still creates an AT-invisibility mismatch.
123+
{
124+
code: '<template><button aria-hidden="true" tabindex="-1"></button></template>',
125+
output: null,
126+
errors: [{ messageId: 'noAriaHiddenOnFocusable' }],
127+
},
115128

116129
// Mustache-boolean + case-variant aria-hidden = true — truthy per spec.
117130
{
@@ -169,6 +182,19 @@ ruleTester.run('template-no-aria-hidden-on-focusable', rule, {
169182
output: null,
170183
errors: [{ messageId: 'noAriaHiddenOnAncestorOfFocusable' }],
171184
},
185+
// DIVERGENCE from vue-a11y: it considers tabindex="-1" on a descendant as "escaped from
186+
// tab order = not focusable". Our isFocusable treats any tabindex (including "-1") as
187+
// programmatically focusable, so the ancestor aria-hidden still creates a trap.
188+
{
189+
code: '<template><div aria-hidden="true"><button tabindex="-1">Trapped</button></div></template>',
190+
output: null,
191+
errors: [{ messageId: 'noAriaHiddenOnAncestorOfFocusable' }],
192+
},
193+
{
194+
code: '<template><div aria-hidden="true"><a href="#" tabindex="-1">Link</a></div></template>',
195+
output: null,
196+
errors: [{ messageId: 'noAriaHiddenOnAncestorOfFocusable' }],
197+
},
172198

173199
// <audio controls> / <video controls> expose focusable UI; flag directly.
174200
{

0 commit comments

Comments
 (0)