Skip to content

Commit b10067d

Browse files
committed
Extract rule: template-link-href-attributes
1 parent 5d2ad94 commit b10067d

3 files changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# ember/template-link-href-attributes
2+
3+
💼 This rule is enabled in the following [configs](https://github.com/ember-cli/eslint-plugin-ember#-configurations): `strict-gjs`, `strict-gts`.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
Requires `href` attribute on `<a>` elements.
8+
9+
Anchor elements should have an `href` attribute to be properly recognized as links by browsers and assistive technologies. If an element is meant to be interactive but not navigate, use a `<button>` instead.
10+
11+
## Rule Details
12+
13+
This rule ensures that all `<a>` elements have an `href` attribute.
14+
15+
## Examples
16+
17+
Examples of **incorrect** code for this rule:
18+
19+
```gjs
20+
<template>
21+
<a>Link</a>
22+
</template>
23+
```
24+
25+
```gjs
26+
<template>
27+
<a onclick={{this.handleClick}}>Click me</a>
28+
</template>
29+
```
30+
31+
```gjs
32+
<template>
33+
<a role="button">Action</a>
34+
</template>
35+
```
36+
37+
Examples of **correct** code for this rule:
38+
39+
```gjs
40+
<template>
41+
<a href="/about">About Us</a>
42+
</template>
43+
```
44+
45+
```gjs
46+
<template>
47+
<a href="https://example.com">External Link</a>
48+
</template>
49+
```
50+
51+
```gjs
52+
<template>
53+
<button {{on "click" this.handleClick}}>Click me</button>
54+
</template>
55+
```
56+
57+
## References
58+
59+
- [MDN: The Anchor element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
60+
- [WebAIM: Links and Hypertext](https://webaim.org/techniques/hypertext/)
61+
- [ember-template-lint link-href-attributes](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/link-href-attributes.md)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/** @type {import('eslint').Rule.RuleModule} */
2+
module.exports = {
3+
meta: {
4+
type: 'problem',
5+
docs: {
6+
description: 'require href attribute on link elements',
7+
category: 'Accessibility',
8+
strictGjs: true,
9+
strictGts: true,
10+
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-link-href-attributes.md',
11+
},
12+
fixable: null,
13+
schema: [],
14+
messages: {
15+
missingHref:
16+
'<a> elements must have an href attribute. Use <button> for clickable elements that are not links.',
17+
},
18+
},
19+
20+
create(context) {
21+
return {
22+
GlimmerElementNode(node) {
23+
if (node.tag !== 'a') {
24+
return;
25+
}
26+
27+
const hasHref = node.attributes?.some((attr) => attr.name === 'href');
28+
29+
if (!hasHref) {
30+
context.report({
31+
node,
32+
messageId: 'missingHref',
33+
});
34+
}
35+
},
36+
};
37+
},
38+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const rule = require('../../../lib/rules/template-link-href-attributes');
2+
const RuleTester = require('eslint').RuleTester;
3+
4+
const ruleTester = new RuleTester({
5+
parser: require.resolve('ember-eslint-parser'),
6+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
7+
});
8+
9+
ruleTester.run('template-link-href-attributes', rule, {
10+
valid: [
11+
'<template><a href="/about">About</a></template>',
12+
'<template><a href="https://example.com">External</a></template>',
13+
'<template><button>Click me</button></template>',
14+
],
15+
16+
invalid: [
17+
{
18+
code: '<template><a>Link</a></template>',
19+
output: null,
20+
errors: [{ messageId: 'missingHref' }],
21+
},
22+
{
23+
code: '<template><a onclick="doSomething()">Click</a></template>',
24+
output: null,
25+
errors: [{ messageId: 'missingHref' }],
26+
},
27+
{
28+
code: '<template><a role="button">Action</a></template>',
29+
output: null,
30+
errors: [{ messageId: 'missingHref' }],
31+
},
32+
],
33+
});

0 commit comments

Comments
 (0)