Skip to content

Commit fea642d

Browse files
Add a rule that warn against removing unsafeDisableTooltip prop from IconButton (#185)
* add a rule to remove unsafeDisableTooltip prop * add changeset * Update src/rules/a11y-remove-disable-tooltip.js Co-authored-by: Kate Higa <[email protected]> * unsafeDisableTooltip={false} is also invalid - the prop should be removed. * export the rule - duh --------- Co-authored-by: Kate Higa <[email protected]>
1 parent 2ff10af commit fea642d

File tree

7 files changed

+130
-0
lines changed

7 files changed

+130
-0
lines changed

.changeset/slow-numbers-invite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-primer-react": minor
3+
---
4+
5+
Add a rule that warns against removing `unsafeDisableTooltip` prop

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ ESLint rules for Primer React
3838
- [a11y-tooltip-interactive-trigger](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-tooltip-interactive-trigger.md)
3939
- [a11y-explicit-heading](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-explicit-heading.md)
4040
- [a11y-link-in-text-block](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-link-in-text-block.md)
41+
- [a11y-remove-disable-tooltip](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-remove-disable-tooltip.md)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
## Rule Details
2+
3+
This rule enforces to remove the `unsafeDisableTooltip` from `IconButton` component so that they have a tooltip by default. `unsafeDisableTooltip` prop is created for an incremental migration and should be removed once all icon buttons have a tooltip.
4+
5+
👎 Examples of **incorrect** code for this rule:
6+
7+
```jsx
8+
import {IconButton} from '@primer/react'
9+
10+
const App = () => (
11+
<IconButton icon={SearchIcon} aria-label="Search" unsafeDisableTooltip />
12+
// OR
13+
<IconButton icon={SearchIcon} aria-label="Search" unsafeDisableTooltip={true} />
14+
// OR
15+
<IconButton icon={SearchIcon} aria-label="Search" unsafeDisableTooltip={false} /> // This is incorrect because it should be removed
16+
)
17+
```
18+
19+
👍 Examples of **correct** code for this rule:
20+
21+
```jsx
22+
import {IconButton} from '@primer/react'
23+
24+
const App = () => <IconButton icon={SearchIcon} aria-label="Search" />
25+
```

src/configs/recommended.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
'primer-react/new-color-css-vars': 'error',
1717
'primer-react/a11y-explicit-heading': 'error',
1818
'primer-react/no-deprecated-props': 'warn',
19+
'primer-react/a11y-remove-disable-tooltip': 'error',
1920
},
2021
settings: {
2122
github: {

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = {
88
'a11y-explicit-heading': require('./rules/a11y-explicit-heading'),
99
'no-deprecated-props': require('./rules/no-deprecated-props'),
1010
'a11y-link-in-text-block': require('./rules/a11y-link-in-text-block'),
11+
'a11y-remove-disable-tooltip': require('./rules/a11y-remove-disable-tooltip'),
1112
},
1213
configs: {
1314
recommended: require('./configs/recommended'),
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict'
2+
3+
const {RuleTester} = require('eslint')
4+
const rule = require('../a11y-remove-disable-tooltip')
5+
6+
const ruleTester = new RuleTester({
7+
parserOptions: {
8+
ecmaVersion: 'latest',
9+
sourceType: 'module',
10+
ecmaFeatures: {
11+
jsx: true,
12+
},
13+
},
14+
})
15+
16+
ruleTester.run('a11y-remove-disable-tooltip', rule, {
17+
valid: [
18+
`import {IconButton} from '@primer/react';
19+
<IconButton icon={SearchIcon} aria-label="Search" />`,
20+
],
21+
invalid: [
22+
{
23+
code: `<IconButton icon={SearchIcon} aria-label="Search" unsafeDisableTooltip />`,
24+
output: `<IconButton icon={SearchIcon} aria-label="Search" />`,
25+
errors: [
26+
{
27+
messageId: 'removeDisableTooltipProp',
28+
},
29+
],
30+
},
31+
{
32+
code: `<IconButton icon={SearchIcon} aria-label="Search" unsafeDisableTooltip={true} />`,
33+
output: `<IconButton icon={SearchIcon} aria-label="Search" />`,
34+
errors: [
35+
{
36+
messageId: 'removeDisableTooltipProp',
37+
},
38+
],
39+
},
40+
{
41+
code: `<IconButton icon={SearchIcon} aria-label="Search" unsafeDisableTooltip={false} />`,
42+
output: `<IconButton icon={SearchIcon} aria-label="Search" />`,
43+
errors: [
44+
{
45+
messageId: 'removeDisableTooltipProp',
46+
},
47+
],
48+
},
49+
],
50+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict'
2+
const {getJSXOpeningElementAttribute} = require('../utils/get-jsx-opening-element-attribute')
3+
const {getJSXOpeningElementName} = require('../utils/get-jsx-opening-element-name')
4+
5+
/**
6+
* @type {import('eslint').Rule.RuleModule}
7+
*/
8+
module.exports = {
9+
meta: {
10+
type: 'error',
11+
docs: {
12+
description:
13+
'Icon buttons should have tooltip by default. Please remove `unsafeDisableTooltip` prop from `IconButton` component to enable the tooltip and help making icon button more accessible.',
14+
recommended: true,
15+
},
16+
fixable: 'code',
17+
schema: [],
18+
messages: {
19+
removeDisableTooltipProp:
20+
'Please remove `unsafeDisableTooltip` prop from `IconButton` component to enable the tooltip and help make icon button more accessible.',
21+
},
22+
},
23+
create(context) {
24+
return {
25+
JSXOpeningElement(node) {
26+
const openingElName = getJSXOpeningElementName(node)
27+
if (openingElName !== 'IconButton') {
28+
return
29+
}
30+
const unsafeDisableTooltip = getJSXOpeningElementAttribute(node, 'unsafeDisableTooltip')
31+
if (unsafeDisableTooltip !== undefined) {
32+
context.report({
33+
node,
34+
messageId: 'removeDisableTooltipProp',
35+
fix(fixer) {
36+
const start = unsafeDisableTooltip.range[0]
37+
const end = unsafeDisableTooltip.range[1]
38+
return [
39+
fixer.removeRange([start - 1, end]), // remove the space before unsafeDisableTooltip as well
40+
]
41+
},
42+
})
43+
}
44+
},
45+
}
46+
},
47+
}

0 commit comments

Comments
 (0)