From 39c70e3868d1f5a433e219ba3bc8678f99df99d6 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 17 Jul 2025 14:08:01 +0300 Subject: [PATCH 1/5] feat(eslint-plugin): add new `@elastic/eui/prefer_eui_icon_tip` rule --- packages/eslint-plugin/README.md | 4 + packages/eslint-plugin/src/index.ts | 4 + .../rules/a11y/prefer_eui_icon_tip.test.ts | 84 +++++++++++++++++++ .../src/rules/a11y/prefer_eui_icon_tip.ts | 49 +++++++++++ 4 files changed, 141 insertions(+) create mode 100644 packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts create mode 100644 packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index c01521666e3e..45fb09cde2af 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -139,6 +139,10 @@ Ensures that form control components within `EuiFormRow` components have matchin Ensures `disableScreenReaderOutput` is set when `EuiToolTip` content matches `EuiButtonIcon` "aria-label". +### `@elastic/eui/prefer_eui_icon_tip` + +Ensure `EuiIconTip` is used rather than ``, as it provides better accessibility and improved support for assistive technologies. + ## Testing ### Running unit tests diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 60e792fc4d9e..4ac1f8b1b964 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -24,6 +24,7 @@ import { NoCssColor } from './rules/no_css_color'; import { RequireAriaLabelForModals } from './rules/a11y/require_aria_label_for_modals'; import { ConsistentIsInvalidProps } from './rules/a11y/consistent_is_invalid_props'; import { ScreenReaderOutputDisabledTooltip } from './rules/a11y/sr_output_disabled_tooltip'; +import { PreferEuiIconTip } from './rules/a11y/prefer_eui_icon_tip'; const config = { rules: { @@ -33,6 +34,7 @@ const config = { 'require-aria-label-for-modals': RequireAriaLabelForModals, 'consistent-is-invalid-props': ConsistentIsInvalidProps, 'sr_output_disabled_tooltip': ScreenReaderOutputDisabledTooltip, + 'prefer_eui_icon_tip': PreferEuiIconTip, }, configs: { recommended: { @@ -44,6 +46,8 @@ const config = { '@elastic/eui/require-aria-label-for-modals': 'warn', '@elastic/eui/consistent-is-invalid-props': 'warn', '@elastic/eui/sr_output_disabled_tooltip': 'warn', + '@elastic/eui/no-css_color': 'warn', + '@elastic/eui/prefer_eui_icon_tip': 'warn', }, }, }, diff --git a/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts new file mode 100644 index 000000000000..547f851b0d71 --- /dev/null +++ b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts @@ -0,0 +1,84 @@ +import dedent from 'dedent'; +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { PreferEuiIconTip } from './prefer_eui_icon_tip'; + +const languageOptions = { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, +}; + +const ruleTester = new RuleTester(); + +ruleTester.run('prefer-eui-icon-tip', PreferEuiIconTip, { + valid: [ + { + code: dedent` + const MyComponent = () => ( + + + + ) + `, + languageOptions, + }, + { + code: dedent` + const MyComponent = () => ( + +
+ Some text + +
+
+ ) + `, + languageOptions, + }, + { + code: dedent` + const MyComponent = () => ( +
+ +
+ ) + `, + languageOptions, + }, + ], + invalid: [ + { + code: dedent` + const MyComponent = () => ( + + + + ) + `, + languageOptions, + errors: [ + { + messageId: 'preferEuiIconTip', + }, + ], + }, + { + code: dedent` + const MyComponent = () => ( + + + Other content + + ) + `, + languageOptions, + errors: [ + { + messageId: 'preferEuiIconTip', + }, + ], + }, + ], +}); diff --git a/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts new file mode 100644 index 000000000000..984280fcee54 --- /dev/null +++ b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts @@ -0,0 +1,49 @@ +import { type TSESTree, ESLintUtils } from '@typescript-eslint/utils'; + +const TOOLTIP_COMPONENT = 'EuiToolTip'; +const ICON_COMPONENT = 'EuiIcon'; +const ICON_TIP_COMPONENT = 'EuiIconTip'; + +export const PreferEuiIconTip = ESLintUtils.RuleCreator.withoutDocs({ + create(context) { + return { + JSXElement(node) { + const openingElement = node.openingElement; + if ( + openingElement.name.type !== 'JSXIdentifier' || + openingElement.name.name !== TOOLTIP_COMPONENT + ) { + return; + } + + // Find first JSX child + const firstChild = node.children.find( + (child): child is TSESTree.JSXElement => + child.type === 'JSXElement' + ); + + if ( + firstChild && + firstChild.openingElement.name.type === 'JSXIdentifier' && + firstChild.openingElement.name.name === ICON_COMPONENT + ) { + context.report({ + node: firstChild, + messageId: 'preferEuiIconTip' + }); + } + }, + }; + }, + meta: { + type: 'suggestion', + docs: { + description: `Prefer using ${ICON_TIP_COMPONENT} over <${TOOLTIP_COMPONENT}><${ICON_COMPONENT}/>, as it delivers better accessibility and improved support for assistive technologies.` + }, + schema: [], + messages: { + preferEuiIconTip: `Ensure ${ICON_TIP_COMPONENT} is used rather than <${TOOLTIP_COMPONENT}><${ICON_COMPONENT}/>, as it delivers better accessibility and improved support for assistive technologies.`, + }, + }, + defaultOptions: [], +}); From d8c93542ae650e051ea6c6024bcd8b53bf5d2691 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 17 Jul 2025 14:17:35 +0300 Subject: [PATCH 2/5] changelog --- packages/eslint-plugin/changelogs/upcoming/8877.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/eslint-plugin/changelogs/upcoming/8877.md diff --git a/packages/eslint-plugin/changelogs/upcoming/8877.md b/packages/eslint-plugin/changelogs/upcoming/8877.md new file mode 100644 index 000000000000..b686529f2d45 --- /dev/null +++ b/packages/eslint-plugin/changelogs/upcoming/8877.md @@ -0,0 +1 @@ +- Added new `prefer_eui_icon_tip` rule. From c90cf817bbab159b28f348ec78086bbacb5654b6 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 17 Jul 2025 14:19:38 +0300 Subject: [PATCH 3/5] typo --- packages/eslint-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 45fb09cde2af..0406186af90d 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -141,7 +141,7 @@ Ensures `disableScreenReaderOutput` is set when `EuiToolTip` content matches `Eu ### `@elastic/eui/prefer_eui_icon_tip` -Ensure `EuiIconTip` is used rather than ``, as it provides better accessibility and improved support for assistive technologies. +Ensure `EuiIconTip` is used rather than ``, as it provides better accessibility and improved support for assistive technologies. ## Testing From 864429255c80d00695e750eecf7436b53e79a735 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 18 Jul 2025 10:15:48 +0300 Subject: [PATCH 4/5] Update packages/eslint-plugin/src/index.ts Co-authored-by: Weronika Olejniczak <32842468+weronikaolejniczak@users.noreply.github.com> --- packages/eslint-plugin/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 4ac1f8b1b964..0af6baed361f 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -47,7 +47,7 @@ const config = { '@elastic/eui/consistent-is-invalid-props': 'warn', '@elastic/eui/sr_output_disabled_tooltip': 'warn', '@elastic/eui/no-css_color': 'warn', - '@elastic/eui/prefer_eui_icon_tip': 'warn', + '@elastic/eui/prefer-eui-icon-tip': 'warn', }, }, }, From cea65340bda607fc709bf72ef0c062269acca76b Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 21 Jul 2025 16:27:16 +0300 Subject: [PATCH 5/5] fix typo --- packages/eslint-plugin/README.md | 4 ++-- packages/eslint-plugin/changelogs/CHANGELOG_2025.md | 2 +- packages/eslint-plugin/changelogs/upcoming/8877.md | 2 +- packages/eslint-plugin/src/index.ts | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 0406186af90d..172393b5f2a4 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -135,11 +135,11 @@ Ensures that EUI modal components (`EuiModal`, `EuiFlyout`, `EuiConfirmModal`) h Ensures that form control components within `EuiFormRow` components have matching `isInvalid` prop values. This maintains consistent validation state between parent form rows and their child form controls, leading to a more predictable and accessible user experience. -### `@elastic/eui/sr_output_disabled_tooltip` +### `@elastic/eui/sr-output-disabled-tooltip` Ensures `disableScreenReaderOutput` is set when `EuiToolTip` content matches `EuiButtonIcon` "aria-label". -### `@elastic/eui/prefer_eui_icon_tip` +### `@elastic/eui/prefer-eui-icon-tip` Ensure `EuiIconTip` is used rather than ``, as it provides better accessibility and improved support for assistive technologies. diff --git a/packages/eslint-plugin/changelogs/CHANGELOG_2025.md b/packages/eslint-plugin/changelogs/CHANGELOG_2025.md index 598223f66182..b9e03d889aa9 100644 --- a/packages/eslint-plugin/changelogs/CHANGELOG_2025.md +++ b/packages/eslint-plugin/changelogs/CHANGELOG_2025.md @@ -1,6 +1,6 @@ ## [`v2.1.0`](https://github.com/elastic/eui/releases/v2.1.0) -- Added new `sr_output_disabled_tooltip` rule. ([#8848](https://github.com/elastic/eui/pull/8848)) +- Added new `sr-output-disabled-tooltip` rule. ([#8848](https://github.com/elastic/eui/pull/8848)) - Added new `consistent-is-invalid-props` rule. ([#8843](https://github.com/elastic/eui/pull/8843)) - Added new `require-aria-label-for-modals` rule. ([#8811](https://github.com/elastic/eui/pull/8811)) diff --git a/packages/eslint-plugin/changelogs/upcoming/8877.md b/packages/eslint-plugin/changelogs/upcoming/8877.md index b686529f2d45..eb55b0e16fbb 100644 --- a/packages/eslint-plugin/changelogs/upcoming/8877.md +++ b/packages/eslint-plugin/changelogs/upcoming/8877.md @@ -1 +1 @@ -- Added new `prefer_eui_icon_tip` rule. +- Added new `prefer-eui-icon-tip` rule. diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index 0af6baed361f..fda6edd2d3cb 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -33,8 +33,8 @@ const config = { 'no-css-color': NoCssColor, 'require-aria-label-for-modals': RequireAriaLabelForModals, 'consistent-is-invalid-props': ConsistentIsInvalidProps, - 'sr_output_disabled_tooltip': ScreenReaderOutputDisabledTooltip, - 'prefer_eui_icon_tip': PreferEuiIconTip, + 'sr-output-disabled-tooltip': ScreenReaderOutputDisabledTooltip, + 'prefer-eui-icon-tip': PreferEuiIconTip, }, configs: { recommended: { @@ -45,7 +45,7 @@ const config = { '@elastic/eui/no-css-color': 'warn', '@elastic/eui/require-aria-label-for-modals': 'warn', '@elastic/eui/consistent-is-invalid-props': 'warn', - '@elastic/eui/sr_output_disabled_tooltip': 'warn', + '@elastic/eui/sr-output-disabled-tooltip': 'warn', '@elastic/eui/no-css_color': 'warn', '@elastic/eui/prefer-eui-icon-tip': 'warn', },