@elastic/eslint-plugin-eui 2.2.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +13 -1
  2. package/lib/cjs/index.js +11 -2
  3. package/lib/cjs/rules/a11y/callout_announce_on_mount.d.ts +3 -0
  4. package/lib/cjs/rules/a11y/callout_announce_on_mount.d.ts.map +1 -0
  5. package/lib/cjs/rules/a11y/callout_announce_on_mount.js +67 -0
  6. package/lib/cjs/rules/a11y/consistent_is_invalid_props.d.ts.map +1 -1
  7. package/lib/cjs/rules/a11y/consistent_is_invalid_props.js +2 -1
  8. package/lib/cjs/rules/a11y/no_unnamed_interactive_element.d.ts +3 -0
  9. package/lib/cjs/rules/a11y/no_unnamed_interactive_element.d.ts.map +1 -0
  10. package/lib/cjs/rules/a11y/no_unnamed_interactive_element.js +89 -0
  11. package/lib/cjs/rules/a11y/no_unnamed_radio_group.d.ts +3 -0
  12. package/lib/cjs/rules/a11y/no_unnamed_radio_group.d.ts.map +1 -0
  13. package/lib/cjs/rules/a11y/no_unnamed_radio_group.js +58 -0
  14. package/lib/cjs/rules/a11y/require_aria_label_for_modals.js +1 -1
  15. package/lib/cjs/rules/a11y/sr_output_disabled_tooltip.d.ts.map +1 -1
  16. package/lib/cjs/rules/a11y/sr_output_disabled_tooltip.js +2 -2
  17. package/lib/cjs/utils/are_attrs_equal.d.ts +2 -0
  18. package/lib/cjs/utils/are_attrs_equal.d.ts.map +1 -0
  19. package/lib/cjs/utils/are_attrs_equal.js +31 -0
  20. package/lib/cjs/utils/get_allowed_a11y_prop_names_for_component.d.ts +24 -0
  21. package/lib/cjs/utils/get_allowed_a11y_prop_names_for_component.d.ts.map +1 -0
  22. package/lib/cjs/utils/get_allowed_a11y_prop_names_for_component.js +50 -0
  23. package/lib/cjs/utils/has_a11y_prop_for_component.d.ts +20 -0
  24. package/lib/cjs/utils/has_a11y_prop_for_component.d.ts.map +1 -0
  25. package/lib/cjs/utils/has_a11y_prop_for_component.js +47 -0
  26. package/lib/cjs/utils/has_spread.d.ts +12 -0
  27. package/lib/cjs/utils/has_spread.d.ts.map +1 -0
  28. package/lib/cjs/utils/has_spread.js +38 -0
  29. package/lib/cjs/utils/is_in_conditional_rendering.d.ts +3 -0
  30. package/lib/cjs/utils/is_in_conditional_rendering.d.ts.map +1 -0
  31. package/lib/cjs/utils/is_in_conditional_rendering.js +35 -0
  32. package/lib/esm/index.js +9 -0
  33. package/lib/esm/index.js.map +1 -1
  34. package/lib/esm/rules/a11y/callout_announce_on_mount.d.ts +2 -0
  35. package/lib/esm/rules/a11y/callout_announce_on_mount.js +76 -0
  36. package/lib/esm/rules/a11y/callout_announce_on_mount.js.map +1 -0
  37. package/lib/esm/rules/a11y/consistent_is_invalid_props.js +2 -1
  38. package/lib/esm/rules/a11y/consistent_is_invalid_props.js.map +1 -1
  39. package/lib/esm/rules/a11y/no_unnamed_interactive_element.d.ts +2 -0
  40. package/lib/esm/rules/a11y/no_unnamed_interactive_element.js +106 -0
  41. package/lib/esm/rules/a11y/no_unnamed_interactive_element.js.map +1 -0
  42. package/lib/esm/rules/a11y/no_unnamed_radio_group.d.ts +2 -0
  43. package/lib/esm/rules/a11y/no_unnamed_radio_group.js +56 -0
  44. package/lib/esm/rules/a11y/no_unnamed_radio_group.js.map +1 -0
  45. package/lib/esm/rules/a11y/require_aria_label_for_modals.js +1 -1
  46. package/lib/esm/rules/a11y/require_aria_label_for_modals.js.map +1 -1
  47. package/lib/esm/rules/a11y/sr_output_disabled_tooltip.js +2 -2
  48. package/lib/esm/rules/a11y/sr_output_disabled_tooltip.js.map +1 -1
  49. package/lib/esm/utils/are_attrs_equal.d.ts +1 -0
  50. package/lib/esm/utils/are_attrs_equal.js +28 -0
  51. package/lib/esm/utils/are_attrs_equal.js.map +1 -0
  52. package/lib/esm/utils/get_allowed_a11y_prop_names_for_component.d.ts +23 -0
  53. package/lib/esm/utils/get_allowed_a11y_prop_names_for_component.js +45 -0
  54. package/lib/esm/utils/get_allowed_a11y_prop_names_for_component.js.map +1 -0
  55. package/lib/esm/utils/has_a11y_prop_for_component.d.ts +19 -0
  56. package/lib/esm/utils/has_a11y_prop_for_component.js +45 -0
  57. package/lib/esm/utils/has_a11y_prop_for_component.js.map +1 -0
  58. package/lib/esm/utils/has_spread.d.ts +11 -0
  59. package/lib/esm/utils/has_spread.js +34 -0
  60. package/lib/esm/utils/has_spread.js.map +1 -0
  61. package/lib/esm/utils/is_in_conditional_rendering.d.ts +2 -0
  62. package/lib/esm/utils/is_in_conditional_rendering.js +34 -0
  63. package/lib/esm/utils/is_in_conditional_rendering.js.map +1 -0
  64. package/package.json +1 -1
package/README.md CHANGED
@@ -129,7 +129,7 @@ It's worth pointing out that although the examples provided are specific to EUI
129
129
 
130
130
  ### `@elastic/eui/require-aria-label-for-modals`
131
131
 
132
- Ensures that EUI modal components (`EuiModal`, `EuiFlyout`, `EuiConfirmModal`) have either an `aria-label` or `aria-labelledby` prop for accessibility. This helps screen reader users understand the purpose and content of modal dialogs.
132
+ Ensures that EUI modal components (`EuiModal`, `EuiFlyout`, `EuiFlyoutResizable` ,`EuiConfirmModal`) have either an `aria-label` or `aria-labelledby` prop for accessibility. This helps screen reader users understand the purpose and content of modal dialogs.
133
133
 
134
134
  ### `@elastic/eui/consistent-is-invalid-props`
135
135
 
@@ -143,6 +143,18 @@ Ensures `disableScreenReaderOutput` is set when `EuiToolTip` content matches `Eu
143
143
 
144
144
  Ensure `EuiIconTip` is used rather than `<EuiToolTip><EuiIcon/></EuiToolTip>`, as it provides better accessibility and improved support for assistive technologies.
145
145
 
146
+ ### `@elastic/eui/no-unnamed-radio-group`
147
+
148
+ Ensure that all radio input components (`EuiRadio`, `EuiRadioGroup`) have a `name` attribute. The `name` attribute is required for radio inputs to be grouped correctly, allowing users to select only one option from a set. Without a `name`, radios may not behave as expected and can cause accessibility issues for assistive technologies.
149
+
150
+ ### `@elastic/eui/callout-announce-on-mount`
151
+
152
+ Ensures that `EuiCallOut` components rendered conditionally have the `announceOnMount` prop for better accessibility. When callouts appear dynamically (e.g., after user interactions, form validation errors, or status changes), screen readers may not announce their content to users. The `announceOnMount` prop ensures these messages are properly announced to users with assistive technologies.
153
+
154
+ ### `@elastic/eui/no-unnamed-interactive-element`
155
+
156
+ Ensure that appropriate aria-attributes are set for `EuiBetaBadge`, `EuiButtonIcon`, `EuiComboBox`, `EuiSelect`, `EuiSelectWithWidth`,`EuiSuperSelect`,`EuiPagination`, `EuiTreeView`, `EuiBreadcrumbs`. Without this rule, screen reader users lose context, keyboard navigation can be confusing.
157
+
146
158
  ## Testing
147
159
 
148
160
  ### Running unit tests
package/lib/cjs/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ var _callout_announce_on_mount = require("./rules/a11y/callout_announce_on_mount");
3
4
  var _href_or_on_click = require("./rules/href_or_on_click");
4
5
  var _no_restricted_eui_imports = require("./rules/no_restricted_eui_imports");
5
6
  var _no_css_color = require("./rules/no_css_color");
@@ -7,6 +8,8 @@ var _require_aria_label_for_modals = require("./rules/a11y/require_aria_label_fo
7
8
  var _consistent_is_invalid_props = require("./rules/a11y/consistent_is_invalid_props");
8
9
  var _sr_output_disabled_tooltip = require("./rules/a11y/sr_output_disabled_tooltip");
9
10
  var _prefer_eui_icon_tip = require("./rules/a11y/prefer_eui_icon_tip");
11
+ var _no_unnamed_radio_group = require("./rules/a11y/no_unnamed_radio_group");
12
+ var _no_unnamed_interactive_element = require("./rules/a11y/no_unnamed_interactive_element");
10
13
  /*
11
14
  * Licensed to Elasticsearch B.V. under one or more contributor
12
15
  * license agreements. See the NOTICE file distributed with
@@ -34,7 +37,10 @@ const config = {
34
37
  'require-aria-label-for-modals': _require_aria_label_for_modals.RequireAriaLabelForModals,
35
38
  'consistent-is-invalid-props': _consistent_is_invalid_props.ConsistentIsInvalidProps,
36
39
  'sr-output-disabled-tooltip': _sr_output_disabled_tooltip.ScreenReaderOutputDisabledTooltip,
37
- 'prefer-eui-icon-tip': _prefer_eui_icon_tip.PreferEuiIconTip
40
+ 'prefer-eui-icon-tip': _prefer_eui_icon_tip.PreferEuiIconTip,
41
+ 'no-unnamed-radio-group': _no_unnamed_radio_group.NoUnnamedRadioGroup,
42
+ 'callout-announce-on-mount': _callout_announce_on_mount.CallOutAnnounceOnMount,
43
+ 'no-unnamed-interactive-element': _no_unnamed_interactive_element.NoUnnamedInteractiveElement
38
44
  },
39
45
  configs: {
40
46
  recommended: {
@@ -46,7 +52,10 @@ const config = {
46
52
  '@elastic/eui/require-aria-label-for-modals': 'warn',
47
53
  '@elastic/eui/consistent-is-invalid-props': 'warn',
48
54
  '@elastic/eui/sr-output-disabled-tooltip': 'warn',
49
- '@elastic/eui/prefer-eui-icon-tip': 'warn'
55
+ '@elastic/eui/prefer-eui-icon-tip': 'warn',
56
+ '@elastic/eui/no-unnamed-radio-group': 'warn',
57
+ '@elastic/eui/callout-announce-on-mount': 'warn',
58
+ '@elastic/eui/no-unnamed-interactive-element': 'warn'
50
59
  }
51
60
  }
52
61
  }
@@ -0,0 +1,3 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const CallOutAnnounceOnMount: ESLintUtils.RuleModule<"missingAnnounceOnMount", [], unknown, ESLintUtils.RuleListener>;
3
+ //# sourceMappingURL=callout_announce_on_mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callout_announce_on_mount.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/callout_announce_on_mount.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAG,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAMxD,eAAO,MAAM,sBAAsB,yFAsDjC,CAAC"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.CallOutAnnounceOnMount = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _is_in_conditional_rendering = require("../../utils/is_in_conditional_rendering");
9
+ var _has_spread = require("../../utils/has_spread");
10
+ /*
11
+ * Licensed to Elasticsearch B.V. under one or more contributor
12
+ * license agreements. See the NOTICE file distributed with
13
+ * this work for additional information regarding copyright
14
+ * ownership. Elasticsearch B.V. licenses this file to you under
15
+ * the Apache License, Version 2.0 (the "License"); you may
16
+ * not use this file except in compliance with the License.
17
+ * You may obtain a copy of the License at
18
+ *
19
+ * http://www.apache.org/licenses/LICENSE-2.0
20
+ *
21
+ * Unless required by applicable law or agreed to in writing,
22
+ * software distributed under the License is distributed on an
23
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
24
+ * KIND, either express or implied. See the License for the
25
+ * specific language governing permissions and limitations
26
+ * under the License.
27
+ */
28
+
29
+ const CALLOUT_COMPONENT = 'EuiCallOut';
30
+ const CallOutAnnounceOnMount = exports.CallOutAnnounceOnMount = _utils.ESLintUtils.RuleCreator.withoutDocs({
31
+ create(context) {
32
+ return {
33
+ JSXElement(node) {
34
+ const {
35
+ openingElement
36
+ } = node;
37
+ if (openingElement.name.type !== 'JSXIdentifier' || openingElement.name.name !== CALLOUT_COMPONENT) {
38
+ return;
39
+ }
40
+ if (openingElement.attributes.some(attr => attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && attr.name.name === 'announceOnMount')) {
41
+ return;
42
+ }
43
+ if ((0, _is_in_conditional_rendering.isInConditionalRendering)(node)) {
44
+ context.report({
45
+ node: openingElement,
46
+ messageId: 'missingAnnounceOnMount',
47
+ fix: (0, _has_spread.hasSpread)(openingElement.attributes) ? undefined : fixer => {
48
+ return fixer.insertTextAfterRange([openingElement.name.range[1], openingElement.name.range[1]], ' announceOnMount');
49
+ }
50
+ });
51
+ }
52
+ }
53
+ };
54
+ },
55
+ meta: {
56
+ type: 'problem',
57
+ docs: {
58
+ description: `Ensure ${CALLOUT_COMPONENT} components that are conditionally rendered have announceOnMount prop for better accessibility`
59
+ },
60
+ fixable: 'code',
61
+ schema: [],
62
+ messages: {
63
+ missingAnnounceOnMount: [`${CALLOUT_COMPONENT} should have \`announceOnMount\` prop when conditionally rendered for better accessibility.`, '\n', `When ${CALLOUT_COMPONENT} appears dynamically (e.g., after user interaction, form validation, etc.),`, 'screen readers may not announce its content. Adding `announceOnMount` ensures the callout', 'is properly announced to users with assistive technologies.', '\n', 'Example:', ` <${CALLOUT_COMPONENT} announceOnMount title="Error" color="danger">`, ' This message will be announced when it appears', ` </${CALLOUT_COMPONENT}>`].join('\n')
64
+ }
65
+ },
66
+ defaultOptions: []
67
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"consistent_is_invalid_props.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/consistent_is_invalid_props.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAetE,eAAO,MAAM,wBAAwB,wFA6FnC,CAAC"}
1
+ {"version":3,"file":"consistent_is_invalid_props.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/consistent_is_invalid_props.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAiBtE,eAAO,MAAM,wBAAwB,wFA6FnC,CAAC"}
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.ConsistentIsInvalidProps = void 0;
7
7
  var _utils = require("@typescript-eslint/utils");
8
8
  var _get_attr_value = require("../../utils/get_attr_value");
9
+ var _are_attrs_equal = require("../../utils/are_attrs_equal");
9
10
  /*
10
11
  * Licensed to Elasticsearch B.V. under one or more contributor
11
12
  * license agreements. See the NOTICE file distributed with
@@ -44,7 +45,7 @@ const ConsistentIsInvalidProps = exports.ConsistentIsInvalidProps = _utils.ESLin
44
45
  return;
45
46
  }
46
47
  const childIsInvalid = (0, _get_attr_value.getAttrValue)(context, childElement.openingElement.attributes, 'isInvalid');
47
- if (childIsInvalid !== formRowIsInvalid) {
48
+ if (!(0, _are_attrs_equal.areAttrsEqual)(childIsInvalid, formRowIsInvalid)) {
48
49
  const componentName = childElement.openingElement.name.name;
49
50
  context.report({
50
51
  node: childElement,
@@ -0,0 +1,3 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const NoUnnamedInteractiveElement: ESLintUtils.RuleModule<"missingA11y", [], unknown, ESLintUtils.RuleListener>;
3
+ //# sourceMappingURL=no_unnamed_interactive_element.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no_unnamed_interactive_element.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/no_unnamed_interactive_element.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,WAAW,EAAiB,MAAM,0BAA0B,CAAC;AAgCtE,eAAO,MAAM,2BAA2B,8EAyEtC,CAAC"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NoUnnamedInteractiveElement = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _has_spread = require("../../utils/has_spread");
9
+ var _get_allowed_a11y_prop_names_for_component = require("../../utils/get_allowed_a11y_prop_names_for_component");
10
+ var _has_a11y_prop_for_component = require("../../utils/has_a11y_prop_for_component");
11
+ /*
12
+ * Licensed to Elasticsearch B.V. under one or more contributor
13
+ * license agreements. See the NOTICE file distributed with
14
+ * this work for additional information regarding copyright
15
+ * ownership. Elasticsearch B.V. licenses this file to you under
16
+ * the Apache License, Version 2.0 (the "License"); you may
17
+ * not use this file except in compliance with the License.
18
+ * You may obtain a copy of the License at
19
+ *
20
+ * http://www.apache.org/licenses/LICENSE-2.0
21
+ *
22
+ * Unless required by applicable law or agreed to in writing,
23
+ * software distributed under the License is distributed on an
24
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25
+ * KIND, either express or implied. See the License for the
26
+ * specific language governing permissions and limitations
27
+ * under the License.
28
+ */
29
+
30
+ const interactiveComponents = ['EuiBetaBadge', 'EuiButtonEmpty', 'EuiButtonIcon', 'EuiComboBox', 'EuiSelect', 'EuiSelectWithWidth', 'EuiSuperSelect', 'EuiPagination', 'EuiTreeView', 'EuiBreadcrumbs'];
31
+ const wrappingComponents = ['EuiFormRow'];
32
+ const interactiveComponentsWithLabel = ['EuiBetaBadge'];
33
+ const baseA11yProps = ['aria-label', 'aria-labelledby'];
34
+
35
+ // Single source of truth for the utils (keeps them reusable)
36
+ const a11yConfig = {
37
+ interactiveComponentsWithLabel: [...interactiveComponentsWithLabel],
38
+ wrappingComponents: [...wrappingComponents],
39
+ baseA11yProps: [...baseA11yProps]
40
+ };
41
+ const NoUnnamedInteractiveElement = exports.NoUnnamedInteractiveElement = _utils.ESLintUtils.RuleCreator.withoutDocs({
42
+ meta: {
43
+ type: 'problem',
44
+ hasSuggestions: false,
45
+ schema: [],
46
+ messages: {
47
+ missingA11y: '{{component}} must include an accessible label. Use one of: {{a11yProps}}'
48
+ }
49
+ },
50
+ defaultOptions: [],
51
+ create(context) {
52
+ const sourceCode = context.sourceCode;
53
+ function report(opening) {
54
+ if (opening.name.type !== 'JSXIdentifier') return;
55
+ const component = opening.name.name;
56
+ const allowed = (0, _get_allowed_a11y_prop_names_for_component.getAllowedA11yPropNamesForComponent)(component, a11yConfig).join(', ');
57
+ context.report({
58
+ node: opening,
59
+ messageId: 'missingA11y',
60
+ data: {
61
+ component,
62
+ a11yProps: allowed
63
+ }
64
+ });
65
+ }
66
+ return {
67
+ JSXOpeningElement(node) {
68
+ if (node.name.type !== 'JSXIdentifier') return;
69
+ const componentName = node.name.name;
70
+ const isInteractive = interactiveComponents.includes(componentName);
71
+ if (!isInteractive) return;
72
+ if ((0, _has_spread.hasSpread)(node.attributes) || (0, _has_a11y_prop_for_component.hasA11yPropForComponent)(componentName, node.attributes, a11yConfig)) {
73
+ return;
74
+ }
75
+ const ancestors = sourceCode.getAncestors(node);
76
+ const wrapper = [...ancestors].reverse().find(a => a.type === 'JSXElement' && a.openingElement.name.type === 'JSXIdentifier' && wrappingComponents.includes(a.openingElement.name.name));
77
+ if (wrapper) {
78
+ const open = wrapper.openingElement;
79
+ const wrapperName = open.name.type === 'JSXIdentifier' ? open.name.name : '';
80
+ if (!(0, _has_spread.hasSpread)(open.attributes) && !(0, _has_a11y_prop_for_component.hasA11yPropForComponent)(wrapperName, open.attributes, a11yConfig)) {
81
+ report(open);
82
+ }
83
+ } else {
84
+ report(node);
85
+ }
86
+ }
87
+ };
88
+ }
89
+ });
@@ -0,0 +1,3 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const NoUnnamedRadioGroup: ESLintUtils.RuleModule<"missingRadioName", [], unknown, ESLintUtils.RuleListener>;
3
+ //# sourceMappingURL=no_unnamed_radio_group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no_unnamed_radio_group.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/no_unnamed_radio_group.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIvD,eAAO,MAAM,mBAAmB,mFAuC9B,CAAC"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NoUnnamedRadioGroup = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ /*
9
+ * Licensed to Elasticsearch B.V. under one or more contributor
10
+ * license agreements. See the NOTICE file distributed with
11
+ * this work for additional information regarding copyright
12
+ * ownership. Elasticsearch B.V. licenses this file to you under
13
+ * the Apache License, Version 2.0 (the "License"); you may
14
+ * not use this file except in compliance with the License.
15
+ * You may obtain a copy of the License at
16
+ *
17
+ * http://www.apache.org/licenses/LICENSE-2.0
18
+ *
19
+ * Unless required by applicable law or agreed to in writing,
20
+ * software distributed under the License is distributed on an
21
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22
+ * KIND, either express or implied. See the License for the
23
+ * specific language governing permissions and limitations
24
+ * under the License.
25
+ */
26
+
27
+ const radioComponents = ['EuiRadio', 'EuiRadioGroup'];
28
+ const NoUnnamedRadioGroup = exports.NoUnnamedRadioGroup = _utils.ESLintUtils.RuleCreator.withoutDocs({
29
+ create(context) {
30
+ return {
31
+ JSXOpeningElement(node) {
32
+ if (node.name.type === 'JSXIdentifier' && radioComponents.includes(node.name.name)) {
33
+ const hasNameAttr = node.attributes.some(attr => attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && attr.name.name === 'name');
34
+ if (!hasNameAttr) {
35
+ context.report({
36
+ node,
37
+ messageId: 'missingRadioName',
38
+ data: {
39
+ component: node.name.name
40
+ }
41
+ });
42
+ }
43
+ }
44
+ }
45
+ };
46
+ },
47
+ meta: {
48
+ type: 'problem',
49
+ docs: {
50
+ description: 'Ensure that all radio input components have a `name` attribute. The `name` attribute is required for radio inputs to be grouped correctly, allowing users to select only one option from a set. Without a `name`, radios may not behave as expected and can cause accessibility issues for assistive technologies.'
51
+ },
52
+ schema: [],
53
+ messages: {
54
+ missingRadioName: '{{ component }} must have a `name` attribute. The `name` attribute is required for radio inputs to be grouped correctly, ensuring only one option can be selected and improving accessibility for all users.'
55
+ }
56
+ },
57
+ defaultOptions: []
58
+ });
@@ -24,7 +24,7 @@ var _utils = require("@typescript-eslint/utils");
24
24
  * under the License.
25
25
  */
26
26
 
27
- const modalComponents = ['EuiModal', 'EuiFlyout'];
27
+ const modalComponents = ['EuiModal', 'EuiFlyout', 'EuiFlyoutResizable'];
28
28
  const confirmModalComponents = ['EuiConfirmModal'];
29
29
  const RequireAriaLabelForModals = exports.RequireAriaLabelForModals = _utils.ESLintUtils.RuleCreator.withoutDocs({
30
30
  create(context) {
@@ -1 +1 @@
1
- {"version":3,"file":"sr_output_disabled_tooltip.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/sr_output_disabled_tooltip.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAStE,eAAO,MAAM,iCAAiC,6FA6F1C,CAAC"}
1
+ {"version":3,"file":"sr_output_disabled_tooltip.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/sr_output_disabled_tooltip.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAQtE,eAAO,MAAM,iCAAiC,6FA6F1C,CAAC"}
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.ScreenReaderOutputDisabledTooltip = void 0;
7
7
  var _utils = require("@typescript-eslint/utils");
8
8
  var _get_attr_value = require("../../utils/get_attr_value");
9
+ var _are_attrs_equal = require("../../utils/are_attrs_equal");
9
10
  /*
10
11
  * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
11
12
  * or more contributor license agreements. Licensed under the Elastic License
@@ -17,7 +18,6 @@ var _get_attr_value = require("../../utils/get_attr_value");
17
18
  const tooltipComponent = 'EuiToolTip';
18
19
  const disabledTooltipComponentProp = 'disableScreenReaderOutput';
19
20
  const buttonComponents = ['EuiButtonIcon'];
20
- const normalizeAttrString = str => str?.trim().replace(/\s+/g, ' ');
21
21
  const ScreenReaderOutputDisabledTooltip = exports.ScreenReaderOutputDisabledTooltip = _utils.ESLintUtils.RuleCreator.withoutDocs({
22
22
  create(context) {
23
23
  return {
@@ -36,7 +36,7 @@ const ScreenReaderOutputDisabledTooltip = exports.ScreenReaderOutputDisabledTool
36
36
  return;
37
37
  }
38
38
  const ariaLabel = (0, _get_attr_value.getAttrValue)(context, buttonElement.openingElement.attributes, 'aria-label');
39
- if (tooltipContent && ariaLabel && normalizeAttrString(tooltipContent) === normalizeAttrString(ariaLabel)) {
39
+ if (tooltipContent && ariaLabel && (0, _are_attrs_equal.areAttrsEqual)(tooltipContent, ariaLabel)) {
40
40
  const buttonElementName = buttonElement.openingElement.name.name;
41
41
  context.report({
42
42
  node: openingElement,
@@ -0,0 +1,2 @@
1
+ export declare const areAttrsEqual: (...strings: Array<string | undefined>) => boolean;
2
+ //# sourceMappingURL=are_attrs_equal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"are_attrs_equal.d.ts","sourceRoot":"","sources":["../../../src/utils/are_attrs_equal.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,aAAa,GAAI,GAAG,SAAS,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,KAAG,OAGrE,CAAC"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.areAttrsEqual = void 0;
7
+ /*
8
+ * Licensed to Elasticsearch B.V. under one or more contributor
9
+ * license agreements. See the NOTICE file distributed with
10
+ * this work for additional information regarding copyright
11
+ * ownership. Elasticsearch B.V. licenses this file to you under
12
+ * the Apache License, Version 2.0 (the "License"); you may
13
+ * not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing,
19
+ * software distributed under the License is distributed on an
20
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ * KIND, either express or implied. See the License for the
22
+ * specific language governing permissions and limitations
23
+ * under the License.
24
+ */
25
+
26
+ const normalizeAttrString = str => str?.trim().replace(/\s+/g, ' ');
27
+ const areAttrsEqual = (...strings) => {
28
+ const [first, ...rest] = strings.map(normalizeAttrString);
29
+ return rest.every(s => s === first);
30
+ };
31
+ exports.areAttrsEqual = areAttrsEqual;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Configuration describing which components accept a `label` prop
3
+ * and the baseline set of accessibility prop names allowed across components.
4
+ */
5
+ export type A11yConfig = {
6
+ interactiveComponentsWithLabel: ReadonlyArray<string>;
7
+ wrappingComponents: ReadonlyArray<string>;
8
+ baseA11yProps: ReadonlyArray<string>;
9
+ };
10
+ /**
11
+ * Compute the set of allowed accessibility prop names for a given component.
12
+ *
13
+ * - Always includes the provided `baseA11yProps`.
14
+ * - Conditionally includes `label` if the component is listed in either
15
+ * `interactiveComponentsWithLabel` or `wrappingComponents`.
16
+ * - Does **not** mutate the provided configuration; a new array is returned.
17
+ *
18
+ * @param componentName - The EUI component name (e.g., `'EuiButtonIcon'`).
19
+ * @param cfg - The accessibility configuration to use when resolving allowed props.
20
+ * @returns A new array of allowed prop names for `componentName`.
21
+ *
22
+ */
23
+ export declare function getAllowedA11yPropNamesForComponent(componentName: string, cfg: A11yConfig): string[];
24
+ //# sourceMappingURL=get_allowed_a11y_prop_names_for_component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_allowed_a11y_prop_names_for_component.d.ts","sourceRoot":"","sources":["../../../src/utils/get_allowed_a11y_prop_names_for_component.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,8BAA8B,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtD,kBAAkB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,mCAAmC,CACjD,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,UAAU,GACd,MAAM,EAAE,CAUV"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getAllowedA11yPropNamesForComponent = getAllowedA11yPropNamesForComponent;
7
+ /*
8
+ * Licensed to Elasticsearch B.V. under one or more contributor
9
+ * license agreements. See the NOTICE file distributed with
10
+ * this work for additional information regarding copyright
11
+ * ownership. Elasticsearch B.V. licenses this file to you under
12
+ * the Apache License, Version 2.0 (the "License"); you may
13
+ * not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing,
19
+ * software distributed under the License is distributed on an
20
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ * KIND, either express or implied. See the License for the
22
+ * specific language governing permissions and limitations
23
+ * under the License.
24
+ */
25
+
26
+ /**
27
+ * Configuration describing which components accept a `label` prop
28
+ * and the baseline set of accessibility prop names allowed across components.
29
+ */
30
+
31
+ /**
32
+ * Compute the set of allowed accessibility prop names for a given component.
33
+ *
34
+ * - Always includes the provided `baseA11yProps`.
35
+ * - Conditionally includes `label` if the component is listed in either
36
+ * `interactiveComponentsWithLabel` or `wrappingComponents`.
37
+ * - Does **not** mutate the provided configuration; a new array is returned.
38
+ *
39
+ * @param componentName - The EUI component name (e.g., `'EuiButtonIcon'`).
40
+ * @param cfg - The accessibility configuration to use when resolving allowed props.
41
+ * @returns A new array of allowed prop names for `componentName`.
42
+ *
43
+ */
44
+ function getAllowedA11yPropNamesForComponent(componentName, cfg) {
45
+ const componentsWithLabel = new Set([...cfg.interactiveComponentsWithLabel, ...cfg.wrappingComponents]);
46
+ if (componentsWithLabel.has(componentName)) {
47
+ return [...cfg.baseA11yProps, 'label'];
48
+ }
49
+ return [...cfg.baseA11yProps];
50
+ }
@@ -0,0 +1,20 @@
1
+ import type { TSESTree } from '@typescript-eslint/utils';
2
+ import { type A11yConfig } from './get_allowed_a11y_prop_names_for_component';
3
+ /**
4
+ * Determines whether a JSX element declares at least one **allowed**
5
+ * accessibility-related prop for a given component.
6
+ *
7
+ * Allowed prop names are resolved via {@link getAllowedA11yPropNamesForComponent},
8
+ * which combines baseline a11y props (e.g. `aria-*`) and conditionally adds
9
+ * `label` for components that support it per the provided configuration.
10
+ *
11
+ * Only plain `JSXAttribute` nodes are considered—spread attributes are ignored here.
12
+ *
13
+ * @param componentName - The component name being checked (e.g., `"EuiButtonIcon"`).
14
+ * @param attrs - The attributes array from a `JSXOpeningElement` (ESTree).
15
+ * @param cfg - Accessibility configuration that defines base props and which
16
+ * components may accept a `label` prop.
17
+ * @returns `true` if any attribute name on the element is in the allowed set; otherwise `false`.
18
+ */
19
+ export declare function hasA11yPropForComponent(componentName: string, attrs: TSESTree.JSXOpeningElement['attributes'], cfg: A11yConfig): boolean;
20
+ //# sourceMappingURL=has_a11y_prop_for_component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has_a11y_prop_for_component.d.ts","sourceRoot":"","sources":["../../../src/utils/has_a11y_prop_for_component.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAEL,KAAK,UAAU,EAChB,MAAM,6CAA6C,CAAC;AAErD;;;;;;;;;;;;;;;GAeG;AAEH,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,YAAY,CAAC,EAC/C,GAAG,EAAE,UAAU,GACd,OAAO,CAUT"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.hasA11yPropForComponent = hasA11yPropForComponent;
7
+ var _get_allowed_a11y_prop_names_for_component = require("./get_allowed_a11y_prop_names_for_component");
8
+ /*
9
+ * Licensed to Elasticsearch B.V. under one or more contributor
10
+ * license agreements. See the NOTICE file distributed with
11
+ * this work for additional information regarding copyright
12
+ * ownership. Elasticsearch B.V. licenses this file to you under
13
+ * the Apache License, Version 2.0 (the "License"); you may
14
+ * not use this file except in compliance with the License.
15
+ * You may obtain a copy of the License at
16
+ *
17
+ * http://www.apache.org/licenses/LICENSE-2.0
18
+ *
19
+ * Unless required by applicable law or agreed to in writing,
20
+ * software distributed under the License is distributed on an
21
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22
+ * KIND, either express or implied. See the License for the
23
+ * specific language governing permissions and limitations
24
+ * under the License.
25
+ */
26
+
27
+ /**
28
+ * Determines whether a JSX element declares at least one **allowed**
29
+ * accessibility-related prop for a given component.
30
+ *
31
+ * Allowed prop names are resolved via {@link getAllowedA11yPropNamesForComponent},
32
+ * which combines baseline a11y props (e.g. `aria-*`) and conditionally adds
33
+ * `label` for components that support it per the provided configuration.
34
+ *
35
+ * Only plain `JSXAttribute` nodes are considered—spread attributes are ignored here.
36
+ *
37
+ * @param componentName - The component name being checked (e.g., `"EuiButtonIcon"`).
38
+ * @param attrs - The attributes array from a `JSXOpeningElement` (ESTree).
39
+ * @param cfg - Accessibility configuration that defines base props and which
40
+ * components may accept a `label` prop.
41
+ * @returns `true` if any attribute name on the element is in the allowed set; otherwise `false`.
42
+ */
43
+
44
+ function hasA11yPropForComponent(componentName, attrs, cfg) {
45
+ const allowed = new Set((0, _get_allowed_a11y_prop_names_for_component.getAllowedA11yPropNamesForComponent)(componentName, cfg));
46
+ return attrs.some(attr => attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && allowed.has(attr.name.name));
47
+ }
@@ -0,0 +1,12 @@
1
+ import type { TSESTree } from '@typescript-eslint/utils';
2
+ /**
3
+ * Checks whether a JSX opening element contains a spread attribute
4
+ * (e.g., `...props`). Spreads make it impossible to statically know
5
+ * all props present on an element, so ESLint rules often use this as
6
+ * a quick bail-out to avoid false positives.
7
+ *
8
+ * @param attrs - The attributes array from a `JSXOpeningElement` node (ESTree).
9
+ * @returns `true` if any attribute is a `JSXSpreadAttribute`; otherwise `false`.
10
+ */
11
+ export declare function hasSpread(attrs: TSESTree.JSXOpeningElement['attributes']): boolean;
12
+ //# sourceMappingURL=has_spread.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has_spread.d.ts","sourceRoot":"","sources":["../../../src/utils/has_spread.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;GAQG;AAEH,wBAAgB,SAAS,CACvB,KAAK,EAAE,QAAQ,CAAC,iBAAiB,CAAC,YAAY,CAAC,GAC9C,OAAO,CAET"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.hasSpread = hasSpread;
7
+ /*
8
+ * Licensed to Elasticsearch B.V. under one or more contributor
9
+ * license agreements. See the NOTICE file distributed with
10
+ * this work for additional information regarding copyright
11
+ * ownership. Elasticsearch B.V. licenses this file to you under
12
+ * the Apache License, Version 2.0 (the "License"); you may
13
+ * not use this file except in compliance with the License.
14
+ * You may obtain a copy of the License at
15
+ *
16
+ * http://www.apache.org/licenses/LICENSE-2.0
17
+ *
18
+ * Unless required by applicable law or agreed to in writing,
19
+ * software distributed under the License is distributed on an
20
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ * KIND, either express or implied. See the License for the
22
+ * specific language governing permissions and limitations
23
+ * under the License.
24
+ */
25
+
26
+ /**
27
+ * Checks whether a JSX opening element contains a spread attribute
28
+ * (e.g., `...props`). Spreads make it impossible to statically know
29
+ * all props present on an element, so ESLint rules often use this as
30
+ * a quick bail-out to avoid false positives.
31
+ *
32
+ * @param attrs - The attributes array from a `JSXOpeningElement` node (ESTree).
33
+ * @returns `true` if any attribute is a `JSXSpreadAttribute`; otherwise `false`.
34
+ */
35
+
36
+ function hasSpread(attrs) {
37
+ return attrs.some(a => a.type === 'JSXSpreadAttribute');
38
+ }
@@ -0,0 +1,3 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ export declare function isInConditionalRendering(node: TSESTree.JSXElement): boolean;
3
+ //# sourceMappingURL=is_in_conditional_rendering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is_in_conditional_rendering.d.ts","sourceRoot":"","sources":["../../../src/utils/is_in_conditional_rendering.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,GAAG,OAAO,CAW3E"}