Skip to content

Commit 40016c8

Browse files
authored
Add new rule a11y-no-title-usage (#316)
* Add new rule `a11y-no-title-usage` * Small clean up * Fix test, format * Add to index, recommended * Add alternative to docs * Add changeset * Update docs/rules/a11y-no-title-usage.md
1 parent 8441f8d commit 40016c8

File tree

6 files changed

+111
-0
lines changed

6 files changed

+111
-0
lines changed

.changeset/nine-mirrors-float.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-primer-react': major
3+
---
4+
5+
Add `a11y-no-title-usage` that warns against using `title` in some components

docs/rules/a11y-no-title-usage.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Rule Details
2+
3+
This rule aims to prevent the use of the `title` attribute with some components from `@primer/react`. The `title` attribute is not keyboard accessible, which results in accessibility issues. Instead, we should utilize alternatives that are accessible.
4+
5+
👎 Examples of **incorrect** code for this rule
6+
7+
```jsx
8+
import {RelativeTime} from '@primer/react'
9+
10+
const App = () => <RelativeTime date={new Date('2020-01-01T00:00:00Z')} noTitle={false} />
11+
```
12+
13+
👍 Examples of **correct** code for this rule:
14+
15+
```jsx
16+
import {RelativeTime} from '@primer/react'
17+
18+
const App = () => <RelativeTime date={new Date('2020-01-01T00:00:00Z')} />
19+
```
20+
21+
The noTitle attribute is true by default, so it can be omitted.
22+
23+
## With alternative tooltip
24+
25+
If you want to still utilize a tooltip in a similar way to how the `title` attribute works, you can use the [Primer `Tooltip`](https://primer.style/components/tooltip/react/beta). If you use the `Tooltip` component, you must use it with an interactive element, such as with a button or a link.
26+
27+
```jsx
28+
import {RelativeTime, Tooltip} from '@primer/react'
29+
30+
const App = () => {
31+
const date = new Date('2020-01-01T00:00:00Z')
32+
return (
33+
<Tooltip text={date.toString()}>
34+
<Link href="#">
35+
<RelativeTime date={date} noTitle={true} />
36+
</Link>
37+
</Tooltip>
38+
)
39+
}
40+
```

src/configs/recommended.js

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = {
1515
'primer-react/a11y-tooltip-interactive-trigger': 'error',
1616
'primer-react/new-color-css-vars': 'error',
1717
'primer-react/a11y-explicit-heading': 'error',
18+
'primer-react/a11y-no-title-usage': 'error',
1819
'primer-react/no-deprecated-props': 'warn',
1920
'primer-react/a11y-remove-disable-tooltip': 'error',
2021
'primer-react/a11y-use-accessible-tooltip': 'error',

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'a11y-link-in-text-block': require('./rules/a11y-link-in-text-block'),
1111
'a11y-remove-disable-tooltip': require('./rules/a11y-remove-disable-tooltip'),
1212
'a11y-use-accessible-tooltip': require('./rules/a11y-use-accessible-tooltip'),
13+
'a11y-no-title-usage': require('./rules/a11y-no-title-usage'),
1314
'use-deprecated-from-deprecated': require('./rules/use-deprecated-from-deprecated'),
1415
'no-wildcard-imports': require('./rules/no-wildcard-imports'),
1516
'no-unnecessary-components': require('./rules/no-unnecessary-components'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const rule = require('../a11y-no-title-usage')
2+
const {RuleTester} = require('eslint')
3+
4+
const ruleTester = new RuleTester({
5+
parserOptions: {
6+
ecmaVersion: 'latest',
7+
sourceType: 'module',
8+
ecmaFeatures: {
9+
jsx: true,
10+
},
11+
},
12+
})
13+
14+
ruleTester.run('a11y-no-title-usage', rule, {
15+
valid: [
16+
`<RelativeTime date={new Date('2020-01-01T00:00:00Z')} noTitle={true} />`,
17+
`<RelativeTime date={new Date('2020-01-01T00:00:00Z')} noTitle />`,
18+
`<RelativeTime date={new Date('2020-01-01T00:00:00Z')} />`,
19+
],
20+
invalid: [
21+
{
22+
code: `<RelativeTime date={new Date('2020-01-01T00:00:00Z')} noTitle={false} />`,
23+
output: `<RelativeTime date={new Date('2020-01-01T00:00:00Z')} />`,
24+
errors: [{messageId: 'noTitleOnRelativeTime'}],
25+
},
26+
],
27+
})

src/rules/a11y-no-title-usage.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const url = require('../url')
2+
const {getJSXOpeningElementAttribute} = require('../utils/get-jsx-opening-element-attribute')
3+
4+
module.exports = {
5+
meta: {
6+
type: 'error',
7+
docs: {
8+
description: 'Disallow usage of title attribute on some components',
9+
recommended: true,
10+
url: url(module),
11+
},
12+
messages: {
13+
noTitleOnRelativeTime: 'Avoid using the title attribute on RelativeTime.',
14+
},
15+
fixable: 'code',
16+
},
17+
18+
create(context) {
19+
return {
20+
JSXOpeningElement(jsxNode) {
21+
const title = getJSXOpeningElementAttribute(jsxNode, 'noTitle')
22+
23+
if (title && title.value && title.value.expression && title.value.expression.value !== true) {
24+
context.report({
25+
node: title,
26+
messageId: 'noTitleOnRelativeTime',
27+
fix(fixer) {
28+
const start = title.range[0] - 1
29+
const end = title.range[1]
30+
return fixer.removeRange([start, end])
31+
},
32+
})
33+
}
34+
},
35+
}
36+
},
37+
}

0 commit comments

Comments
 (0)