Skip to content

Commit 2bbcee0

Browse files
committed
fix(template-no-nested-interactive): guard canvas in isInteractiveOnlyFromTabindex; update docs
canvas is unconditionally interactive (added by PR #37 for drawing/game-UI parity with upstream ember-template-lint). isInteractiveOnlyFromTabindex was missing a canvas guard, so <canvas tabindex="0"> was incorrectly treated as tabindex-only and never pushed onto the interactive stack, meaning nested interactive content inside a canvas+tabindex was silently allowed. Also updates docs to list audio[controls], video[controls], and canvas as interactive, and expands the Special Cases section to document the ARIA composite-widget hierarchy exception introduced alongside this refactor.
1 parent aecd9ed commit 2bbcee0

3 files changed

Lines changed: 21 additions & 2 deletions

File tree

docs/rules/template-no-nested-interactive.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ This rule disallows nesting interactive elements inside other interactive elemen
1515
Interactive elements include:
1616

1717
- `<a>` (only when it has an `href` attribute)
18+
- `<audio>` (only when it has a `controls` attribute)
1819
- `<button>`
20+
- `<canvas>` (drawing/game-UI convention; not in the HTML spec's interactive-content category)
1921
- `<details>`
2022
- `<embed>`
2123
- `<iframe>`
@@ -24,6 +26,7 @@ Interactive elements include:
2426
- `<select>`
2527
- `<summary>`
2628
- `<textarea>`
29+
- `<video>` (only when it has a `controls` attribute)
2730
- Elements with interactive ARIA roles (e.g., `role="button"`, `role="link"`)
2831
- Elements with `tabindex` (unless `ignoreTabindex` is enabled)
2932
- Elements with `contenteditable` (except `contenteditable="false"`)
@@ -32,8 +35,9 @@ Interactive elements include:
3235
Special cases:
3336

3437
- `<label>` may contain **one** interactive child (e.g., `<label><input /></label>` is fine, but `<label><input /><button>x</button></label>` is not)
35-
- `<summary>` as the first child of `<details>` is allowed
36-
- Nested `role="menuitem"` elements are allowed (menu/sub-menu pattern)
38+
- `<summary>` as the first child of `<details>` is allowed; other interactive content after `<summary>` (in the disclosed panel) is also allowed
39+
- Canonical ARIA composite-widget hierarchies are allowed (e.g., `role="option"` inside `role="listbox"`, `role="tab"` inside `role="tablist"`, `role="row"` inside `role="grid"`, `role="radio"` inside `role="radiogroup"`). Derived from the ARIA `requiredOwnedElements` relationship.
40+
- Nested `role="menuitem"` / `role="menuitemcheckbox"` / `role="menuitemradio"` elements are allowed (menu/sub-menu pattern)
3741

3842
## Examples
3943

lib/rules/template-no-nested-interactive.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ module.exports = {
187187
if (additionalInteractiveTags.has(tag)) {
188188
return false;
189189
}
190+
if (tag === 'canvas') {
191+
return false;
192+
}
190193
if (isHtmlInteractiveContent(node, getTextAttr, { ignoreUsemap })) {
191194
return false;
192195
}

tests/lib/rules/template-no-nested-interactive.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ ruleTester.run('template-no-nested-interactive', rule, {
268268
output: null,
269269
errors: [{ messageId: 'nested' }],
270270
},
271+
// <canvas> is interactive (drawing/game-UI convention); nesting fires even with tabindex.
272+
{
273+
code: '<template><canvas tabindex="0"><button>Click</button></canvas></template>',
274+
output: null,
275+
errors: [{ messageId: 'nested' }],
276+
},
271277
],
272278
});
273279

@@ -454,5 +460,11 @@ hbsRuleTester.run('template-no-nested-interactive', rule, {
454460
output: null,
455461
errors: [{ message: 'Do not nest interactive element <video> inside <button>.' }],
456462
},
463+
// <canvas> is interactive (drawing/game-UI convention); nesting fires even with tabindex.
464+
{
465+
code: '<canvas tabindex="0"><button>Click</button></canvas>',
466+
output: null,
467+
errors: [{ message: 'Do not nest interactive element <button> inside <canvas>.' }],
468+
},
457469
],
458470
});

0 commit comments

Comments
 (0)