diff --git a/.changeset/slow-numbers-invite.md b/.changeset/slow-numbers-invite.md new file mode 100644 index 00000000..366499a4 --- /dev/null +++ b/.changeset/slow-numbers-invite.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-primer-react": minor +--- + +Add a rule that warns against removing `unsafeDisableTooltip` prop diff --git a/README.md b/README.md index aa008488..b2a22db4 100644 --- a/README.md +++ b/README.md @@ -38,3 +38,4 @@ ESLint rules for Primer React - [a11y-tooltip-interactive-trigger](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-tooltip-interactive-trigger.md) - [a11y-explicit-heading](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-explicit-heading.md) - [a11y-link-in-text-block](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-link-in-text-block.md) +- [a11y-remove-disable-tooltip](https://github.com/primer/eslint-plugin-primer-react/blob/main/docs/rules/a11y-remove-disable-tooltip.md) diff --git a/docs/rules/a11y-remove-disable-tooltip.md b/docs/rules/a11y-remove-disable-tooltip.md new file mode 100644 index 00000000..8b41f449 --- /dev/null +++ b/docs/rules/a11y-remove-disable-tooltip.md @@ -0,0 +1,25 @@ +## Rule Details + +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. + +👎 Examples of **incorrect** code for this rule: + +```jsx +import {IconButton} from '@primer/react' + +const App = () => ( + + // OR + + // OR + // This is incorrect because it should be removed +) +``` + +👍 Examples of **correct** code for this rule: + +```jsx +import {IconButton} from '@primer/react' + +const App = () => +``` diff --git a/src/configs/recommended.js b/src/configs/recommended.js index 25b878ca..7b4aa489 100644 --- a/src/configs/recommended.js +++ b/src/configs/recommended.js @@ -16,6 +16,7 @@ module.exports = { 'primer-react/new-color-css-vars': 'error', 'primer-react/a11y-explicit-heading': 'error', 'primer-react/no-deprecated-props': 'warn', + 'primer-react/a11y-remove-disable-tooltip': 'error', }, settings: { github: { diff --git a/src/index.js b/src/index.js index 3a21b191..5ec93def 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,7 @@ module.exports = { 'a11y-explicit-heading': require('./rules/a11y-explicit-heading'), 'no-deprecated-props': require('./rules/no-deprecated-props'), 'a11y-link-in-text-block': require('./rules/a11y-link-in-text-block'), + 'a11y-remove-disable-tooltip': require('./rules/a11y-remove-disable-tooltip'), }, configs: { recommended: require('./configs/recommended'), diff --git a/src/rules/__tests__/a11y-remove-disable-tooltip.test.js b/src/rules/__tests__/a11y-remove-disable-tooltip.test.js new file mode 100644 index 00000000..ebf6fb9a --- /dev/null +++ b/src/rules/__tests__/a11y-remove-disable-tooltip.test.js @@ -0,0 +1,50 @@ +'use strict' + +const {RuleTester} = require('eslint') +const rule = require('../a11y-remove-disable-tooltip') + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, +}) + +ruleTester.run('a11y-remove-disable-tooltip', rule, { + valid: [ + `import {IconButton} from '@primer/react'; + `, + ], + invalid: [ + { + code: ``, + output: ``, + errors: [ + { + messageId: 'removeDisableTooltipProp', + }, + ], + }, + { + code: ``, + output: ``, + errors: [ + { + messageId: 'removeDisableTooltipProp', + }, + ], + }, + { + code: ``, + output: ``, + errors: [ + { + messageId: 'removeDisableTooltipProp', + }, + ], + }, + ], +}) diff --git a/src/rules/a11y-remove-disable-tooltip.js b/src/rules/a11y-remove-disable-tooltip.js new file mode 100644 index 00000000..b2896aaa --- /dev/null +++ b/src/rules/a11y-remove-disable-tooltip.js @@ -0,0 +1,47 @@ +'use strict' +const {getJSXOpeningElementAttribute} = require('../utils/get-jsx-opening-element-attribute') +const {getJSXOpeningElementName} = require('../utils/get-jsx-opening-element-name') + +/** + * @type {import('eslint').Rule.RuleModule} + */ +module.exports = { + meta: { + type: 'error', + docs: { + description: + '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.', + recommended: true, + }, + fixable: 'code', + schema: [], + messages: { + removeDisableTooltipProp: + 'Please remove `unsafeDisableTooltip` prop from `IconButton` component to enable the tooltip and help make icon button more accessible.', + }, + }, + create(context) { + return { + JSXOpeningElement(node) { + const openingElName = getJSXOpeningElementName(node) + if (openingElName !== 'IconButton') { + return + } + const unsafeDisableTooltip = getJSXOpeningElementAttribute(node, 'unsafeDisableTooltip') + if (unsafeDisableTooltip !== undefined) { + context.report({ + node, + messageId: 'removeDisableTooltipProp', + fix(fixer) { + const start = unsafeDisableTooltip.range[0] + const end = unsafeDisableTooltip.range[1] + return [ + fixer.removeRange([start - 1, end]), // remove the space before unsafeDisableTooltip as well + ] + }, + }) + } + }, + } + }, +}