Skip to content

Commit de1838b

Browse files
committed
fix(template-require-context-role): broaden hasAriaHiddenTrue via classifyAttribute (rows h3, h7, h12, h14)
Before: - `hasAriaHiddenTrue` matched only `aria-hidden="true"` as a `GlimmerTextNode`. - Mustache forms that render the same `aria-hidden="true"` at runtime were silently treated as visible: - `aria-hidden={{"true"}}` (h7) → renders `aria-hidden="true"` - `aria-hidden="{{true}}"` (h12) → renders `aria-hidden="true"` - `aria-hidden="{{'true'}}"` (h14) → renders `aria-hidden="true"` - Each was a false positive: a child with role="treeitem" inside a hidden ancestor was reported as missing-context, even though the entire subtree is hidden from assistive tech and "context" is moot. After: - `hasAriaHiddenTrue` flows through `classifyAttribute` from the new util. The check is `presence === 'present' && value === 'true'`, which matches exactly h3 / h7 / h12 / h14 — every source form whose IDL `ariaHidden` is `"true"`. - Note: h5 (bare `{{true}}`) renders `aria-hidden=""` (contested per ARIA spec), so `classifyAttribute` returns `value=""` for that form and the equality check correctly excludes it. The rule deliberately does not treat the contested empty-value case as hidden. Test additions in both GJS and HBS suites: - Valid: `<div aria-hidden={{"true"}} role="tablist"><div role="treeitem">…` - Valid: `<div aria-hidden="{{true}}" role="tablist"><div role="treeitem">…` - Valid: `<div aria-hidden="{{'true'}}" role="tablist"><div role="treeitem">…` 111 tests pass. Doc rows cited: h3 (existing), h7, h12, h14.
1 parent fcd1fdd commit de1838b

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

lib/rules/template-require-context-role.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
'use strict';
2+
3+
const { classifyAttribute } = require('../utils/glimmer-attr-presence');
4+
15
const ROLES_REQUIRING_CONTEXT = {
26
cell: ['row'],
37
listitem: ['group', 'list'],
@@ -89,9 +93,18 @@ function getRoleFromNode(node) {
8993
return null;
9094
}
9195

96+
// Recognize every source form that renders `aria-hidden="true"` at runtime
97+
// per the verified model (doc rows h3, h7, h12, h14). The previous
98+
// `GlimmerTextNode + chars === 'true'` check missed bare `{{"true"}}` and
99+
// concat forms `"{{true}}"` / `"{{'true'}}"` — all three render identical
100+
// HTML and are interpreted as "hidden" by every assistive tech. Notably,
101+
// bare `{{true}}` is *not* hidden (h5: renders `aria-hidden=""`, contested
102+
// per ARIA spec) — `classifyAttribute` returns value="" for that case so
103+
// the equality check correctly excludes it.
92104
function hasAriaHiddenTrue(node) {
93105
const attr = node.attributes?.find((a) => a.name === 'aria-hidden');
94-
return attr?.value?.type === 'GlimmerTextNode' && attr.value.chars === 'true';
106+
const { presence, value } = classifyAttribute(attr);
107+
return presence === 'present' && value === 'true';
95108
}
96109

97110
/**

tests/lib/rules/template-require-context-role.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ ruleTester.run('template-require-context-role', rule, {
5656
'<template><div role="rowgroup"><div role="row">Item One</div></div></template>',
5757
'<template><div role="treegrid"><div role="row">Item One</div></div></template>',
5858
'<template><div aria-hidden="true" role="tablist"><div role="treeitem">Item One</div></div></template>',
59+
// Mustache forms that render `aria-hidden="true"` at runtime (doc rows
60+
// h7, h12, h14) — were previously false-positive flagged because the
61+
// rule only matched the static text node. classifyAttribute resolves
62+
// each form to value="true" and the parent is correctly recognized as
63+
// hidden, suppressing the missing-context check.
64+
'<template><div aria-hidden={{"true"}} role="tablist"><div role="treeitem">Item</div></div></template>',
65+
'<template><div aria-hidden="{{true}}" role="tablist"><div role="treeitem">Item</div></div></template>',
66+
'<template><div aria-hidden="{{\'true\'}}" role="tablist"><div role="treeitem">Item</div></div></template>',
5967
'<template><div role="grid"><div role="rowgroup">Item One</div></div></template>',
6068
'<template><div role="row"><div role="rowheader">Item One</div></div></template>',
6169
'<template><div role="tablist"><div role="tab">Item One</div></div></template>',
@@ -321,6 +329,10 @@ hbsRuleTester.run('template-require-context-role', rule, {
321329
'<div role="rowgroup"><div role="row">Item One</div></div>',
322330
'<div role="treegrid"><div role="row">Item One</div></div>',
323331
'<div aria-hidden="true" role="tablist"><div role="treeitem">Item One</div></div>',
332+
// Mustache forms that render `aria-hidden="true"` at runtime (doc rows h7, h12, h14)
333+
'<div aria-hidden={{"true"}} role="tablist"><div role="treeitem">Item</div></div>',
334+
'<div aria-hidden="{{true}}" role="tablist"><div role="treeitem">Item</div></div>',
335+
'<div aria-hidden="{{\'true\'}}" role="tablist"><div role="treeitem">Item</div></div>',
324336
'<div role="grid"><div role="rowgroup">Item One</div></div>',
325337
'<div role="row"><div role="rowheader">Item One</div></div>',
326338
'<div role="tablist"><div role="tab">Item One</div></div>',

0 commit comments

Comments
 (0)