Skip to content

Commit 8e4d319

Browse files
committed
refactor: migrate to is-native-element util + normalize input[type] + doc grammar (Copilot review)
1 parent 992209d commit 8e4d319

6 files changed

Lines changed: 19 additions & 97 deletions

docs/rules/template-click-events-have-key-events.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<!-- end auto-generated rule header -->
44

5-
Enforce a clickable non-interactive element has at least one keyboard event listener.
5+
Enforce that a clickable non-interactive element has at least one keyboard event listener.
66

77
When a `{{on "click" …}}` modifier is attached to a non-interactive DOM element (e.g. `<div>`, `<span>`, `<a>` without `href`), keyboard-only users can't reach the handler through the keyboard path alone. Adding `{{on "keydown" …}}`, `{{on "keyup" …}}`, or `{{on "keypress" …}}` — along with appropriate `role`/`tabindex` to make the element focusable — restores keyboard parity.
88

lib/rules/template-click-events-have-key-events.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { dom } = require('aria-query');
2-
const { isComponentInvocation } = require('../utils/is-component-invocation');
2+
const { isNativeElement } = require('../utils/is-native-element');
33
const { isHtmlInteractiveContent } = require('../utils/html-interactive-content');
44

55
const KEYBOARD_EVENT_NAMES = new Set(['keydown', 'keyup', 'keypress']);
@@ -113,14 +113,16 @@ module.exports = {
113113
},
114114

115115
create(context) {
116+
const sourceCode = context.sourceCode ?? context.getSourceCode();
116117
return {
117118
GlimmerElementNode(node) {
118119
if (!node.tag) {
119120
return;
120121
}
121122

122-
// Skip component invocations (PascalCase, named-arg, this-path, dot-path, named-block).
123-
if (isComponentInvocation(node)) {
123+
// Skip component invocations (PascalCase, named-arg, this-path, dot-path, named-block)
124+
// and scope-shadowed tag names.
125+
if (!isNativeElement(node, sourceCode)) {
124126
return;
125127
}
126128

lib/utils/html-interactive-content.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function isHtmlInteractiveContent(node, getTextAttrValue, options = {}) {
6363
// input — interactive unless type="hidden"
6464
if (tag === 'input') {
6565
const type = getTextAttrValue(node, 'type');
66-
return type !== 'hidden';
66+
return type === undefined || type === null || type.trim().toLowerCase() !== 'hidden';
6767
}
6868

6969
// a — interactive only when href is present

lib/utils/is-component-invocation.js

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

tests/lib/utils/html-interactive-content-test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ describe('isHtmlInteractiveContent', () => {
5353
isHtmlInteractiveContent(makeNode('input', { type: 'hidden' }), getTextAttrValue)
5454
).toBe(false);
5555
});
56+
57+
it('is NOT interactive when type="HIDDEN" (case-insensitive)', () => {
58+
expect(
59+
isHtmlInteractiveContent(makeNode('input', { type: 'HIDDEN' }), getTextAttrValue)
60+
).toBe(false);
61+
});
62+
63+
it('is NOT interactive when type=" hidden " (whitespace-trimmed)', () => {
64+
expect(
65+
isHtmlInteractiveContent(makeNode('input', { type: ' hidden ' }), getTextAttrValue)
66+
).toBe(false);
67+
});
5668
});
5769

5870
describe('<a>', () => {

tests/lib/utils/is-component-invocation-test.js

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

0 commit comments

Comments
 (0)