Skip to content

Commit 54955c2

Browse files
committed
fix(template-require-iframe-title): flag title={{null|undefined|number}}
Before: the rule only rejected title={{false}} among mustache literals. title={{null}}, title={{undefined}}, and title={{42}} (any non-string literal) were silently accepted even though none produces a useful accessible name for an <iframe>. Fix: extract isInvalidTitleLiteral(); treat boolean, null, undefined, and numeric literals as invalid title values in both GlimmerMustacheStatement and single-part GlimmerConcatStatement positions. Six new invalid tests (3 literal types × 2 syntax forms). Note: whitespace-only title values (e.g. title=" ") remain flagged — the audit suggested upstream jsx-a11y accepts these, but an all-whitespace title provides no accessible name, so our stricter behavior is kept.
1 parent 24882a3 commit 54955c2

2 files changed

Lines changed: 57 additions & 4 deletions

File tree

lib/rules/template-require-iframe-title.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
// Mustache path nodes that produce no accessible name. Booleans, null, undefined
2+
// all coerce to empty-ish strings; numeric literals ("42") are accepted by HTML
3+
// but provide no useful title for assistive tech.
4+
function isInvalidTitleLiteral(path) {
5+
if (!path) {
6+
return false;
7+
}
8+
if (path.type === 'GlimmerBooleanLiteral') {
9+
return true;
10+
}
11+
if (path.type === 'GlimmerNullLiteral' || path.type === 'GlimmerUndefinedLiteral') {
12+
return true;
13+
}
14+
if (path.type === 'GlimmerNumberLiteral') {
15+
return true;
16+
}
17+
return false;
18+
}
19+
120
/** @type {import('eslint').Rule.RuleModule} */
221
module.exports = {
322
meta: {
@@ -93,19 +112,21 @@ module.exports = {
93112
break;
94113
}
95114
case 'GlimmerMustacheStatement': {
96-
// title={{false}} → BooleanLiteral false is invalid
97-
if (titleAttr.value.path?.type === 'GlimmerBooleanLiteral') {
115+
// title={{false}} / title={{null}} / title={{undefined}} / title={{42}}
116+
// — any literal that doesn't produce a meaningful accessible name.
117+
if (isInvalidTitleLiteral(titleAttr.value.path)) {
98118
context.report({ node, messageId: 'dynamicFalseTitle' });
99119
}
100120
break;
101121
}
102122
case 'GlimmerConcatStatement': {
103-
// title="{{false}}" → ConcatStatement with single BooleanLiteral part
123+
// title="{{false}}" / "{{undefined}}" / etc. — ConcatStatement
124+
// with a single literal part that doesn't produce a name.
104125
const parts = titleAttr.value.parts || [];
105126
if (
106127
parts.length === 1 &&
107128
parts[0].type === 'GlimmerMustacheStatement' &&
108-
parts[0].path?.type === 'GlimmerBooleanLiteral'
129+
isInvalidTitleLiteral(parts[0].path)
109130
) {
110131
context.report({ node, messageId: 'dynamicFalseTitle' });
111132
}

tests/lib/rules/template-require-iframe-title.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,38 @@ ruleTester.run('template-require-iframe-title', rule, {
104104
output: null,
105105
errors: [{ messageId: 'emptyTitle' }],
106106
},
107+
108+
// Mustache literals that don't coerce to a useful accessible name.
109+
{
110+
code: '<template><iframe title={{null}} /></template>',
111+
output: null,
112+
errors: [{ messageId: 'dynamicFalseTitle' }],
113+
},
114+
{
115+
code: '<template><iframe title={{undefined}} /></template>',
116+
output: null,
117+
errors: [{ messageId: 'dynamicFalseTitle' }],
118+
},
119+
{
120+
code: '<template><iframe title={{42}} /></template>',
121+
output: null,
122+
errors: [{ messageId: 'dynamicFalseTitle' }],
123+
},
124+
{
125+
code: '<template><iframe title="{{null}}" /></template>',
126+
output: null,
127+
errors: [{ messageId: 'dynamicFalseTitle' }],
128+
},
129+
{
130+
code: '<template><iframe title="{{undefined}}" /></template>',
131+
output: null,
132+
errors: [{ messageId: 'dynamicFalseTitle' }],
133+
},
134+
{
135+
code: '<template><iframe title="{{42}}" /></template>',
136+
output: null,
137+
errors: [{ messageId: 'dynamicFalseTitle' }],
138+
},
107139
],
108140
});
109141

0 commit comments

Comments
 (0)