@elastic/eslint-plugin-eui 2.11.1 → 2.13.0-snapshot.1779110362015

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 (38) hide show
  1. package/README.md +95 -0
  2. package/lib/cjs/index.js +14 -2
  3. package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.d.ts +3 -0
  4. package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.d.ts.map +1 -0
  5. package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.js +104 -0
  6. package/lib/cjs/rules/a11y/tooltip_no_interactive_content.d.ts +3 -0
  7. package/lib/cjs/rules/a11y/tooltip_no_interactive_content.d.ts.map +1 -0
  8. package/lib/cjs/rules/a11y/tooltip_no_interactive_content.js +113 -0
  9. package/lib/cjs/rules/href_or_on_click.js +1 -1
  10. package/lib/cjs/rules/prefer_tooltip_trigger_focus_test_utility.d.ts +3 -0
  11. package/lib/cjs/rules/prefer_tooltip_trigger_focus_test_utility.d.ts.map +1 -0
  12. package/lib/cjs/rules/prefer_tooltip_trigger_focus_test_utility.js +80 -0
  13. package/lib/cjs/rules/require_href_for_link.d.ts +3 -0
  14. package/lib/cjs/rules/require_href_for_link.d.ts.map +1 -0
  15. package/lib/cjs/rules/require_href_for_link.js +59 -0
  16. package/lib/cjs/utils/constants.d.ts +7 -0
  17. package/lib/cjs/utils/constants.d.ts.map +1 -1
  18. package/lib/cjs/utils/constants.js +10 -2
  19. package/lib/esm/index.js +13 -1
  20. package/lib/esm/index.js.map +1 -1
  21. package/lib/esm/rules/a11y/tooltip_button_icon_wrap.d.ts +2 -0
  22. package/lib/esm/rules/a11y/tooltip_button_icon_wrap.js +123 -0
  23. package/lib/esm/rules/a11y/tooltip_button_icon_wrap.js.map +1 -0
  24. package/lib/esm/rules/a11y/tooltip_no_interactive_content.d.ts +2 -0
  25. package/lib/esm/rules/a11y/tooltip_no_interactive_content.js +115 -0
  26. package/lib/esm/rules/a11y/tooltip_no_interactive_content.js.map +1 -0
  27. package/lib/esm/rules/href_or_on_click.js +1 -1
  28. package/lib/esm/rules/href_or_on_click.js.map +1 -1
  29. package/lib/esm/rules/prefer_tooltip_trigger_focus_test_utility.d.ts +2 -0
  30. package/lib/esm/rules/prefer_tooltip_trigger_focus_test_utility.js +93 -0
  31. package/lib/esm/rules/prefer_tooltip_trigger_focus_test_utility.js.map +1 -0
  32. package/lib/esm/rules/require_href_for_link.d.ts +2 -0
  33. package/lib/esm/rules/require_href_for_link.js +54 -0
  34. package/lib/esm/rules/require_href_for_link.js.map +1 -0
  35. package/lib/esm/utils/constants.d.ts +7 -0
  36. package/lib/esm/utils/constants.js +45 -29
  37. package/lib/esm/utils/constants.js.map +1 -1
  38. package/package.json +3 -2
package/lib/esm/index.js CHANGED
@@ -11,6 +11,7 @@ const accessible_interactive_element_1 = require("./rules/a11y/accessible_intera
11
11
  const callout_announce_on_mount_1 = require("./rules/a11y/callout_announce_on_mount");
12
12
  const consistent_is_invalid_props_1 = require("./rules/a11y/consistent_is_invalid_props");
13
13
  const href_or_on_click_1 = require("./rules/href_or_on_click");
14
+ const require_href_for_link_1 = require("./rules/require_href_for_link");
14
15
  const no_css_color_1 = require("./rules/no_css_color");
15
16
  const no_restricted_eui_imports_1 = require("./rules/no_restricted_eui_imports");
16
17
  const no_static_z_index_1 = require("./rules/no_static_z_index");
@@ -21,8 +22,11 @@ const require_aria_label_for_modals_1 = require("./rules/a11y/require_aria_label
21
22
  const require_table_caption_1 = require("./rules/a11y/require_table_caption");
22
23
  const sr_output_disabled_tooltip_1 = require("./rules/a11y/sr_output_disabled_tooltip");
23
24
  const tooltip_focusable_anchor_1 = require("./rules/a11y/tooltip_focusable_anchor");
25
+ const prefer_tooltip_trigger_focus_test_utility_1 = require("./rules/prefer_tooltip_trigger_focus_test_utility");
24
26
  const badge_accessibility_rules_1 = require("./rules/a11y/badge_accessibility_rules");
25
27
  const icon_accessibility_rules_1 = require("./rules/a11y/icon_accessibility_rules");
28
+ const tooltip_no_interactive_content_1 = require("./rules/a11y/tooltip_no_interactive_content");
29
+ const tooltip_button_icon_wrap_1 = require("./rules/a11y/tooltip_button_icon_wrap");
26
30
  const config = {
27
31
  rules: {
28
32
  'accessible-interactive-element': accessible_interactive_element_1.AccessibleInteractiveElements,
@@ -39,8 +43,12 @@ const config = {
39
43
  'require-table-caption': require_table_caption_1.RequireTableCaption,
40
44
  'sr-output-disabled-tooltip': sr_output_disabled_tooltip_1.ScreenReaderOutputDisabledTooltip,
41
45
  'tooltip-focusable-anchor': tooltip_focusable_anchor_1.TooltipFocusableAnchor,
46
+ 'prefer-tooltip-trigger-focus-test-utility': prefer_tooltip_trigger_focus_test_utility_1.PreferTooltipTriggerFocusTestUtility,
42
47
  'badge-accessibility-rules': badge_accessibility_rules_1.EuiBadgeAccessibilityRules,
43
- 'icon-accessibility-rules': icon_accessibility_rules_1.EuiIconAccessibilityRules
48
+ 'icon-accessibility-rules': icon_accessibility_rules_1.EuiIconAccessibilityRules,
49
+ 'require-href-for-link': require_href_for_link_1.RequireHrefForLink,
50
+ 'tooltip-no-interactive-content': tooltip_no_interactive_content_1.TooltipNoInteractiveContent,
51
+ 'tooltip-button-icon-wrap': tooltip_button_icon_wrap_1.TooltipButtonIconWrap,
44
52
  },
45
53
  configs: {
46
54
  recommended: {
@@ -60,8 +68,12 @@ const config = {
60
68
  '@elastic/eui/require-table-caption': 'warn',
61
69
  '@elastic/eui/sr-output-disabled-tooltip': 'warn',
62
70
  '@elastic/eui/tooltip-focusable-anchor': 'warn',
71
+ '@elastic/eui/prefer-tooltip-trigger-focus-test-utility': 'warn',
63
72
  '@elastic/eui/badge-accessibility-rules': 'warn',
64
73
  '@elastic/eui/icon-accessibility-rules': 'warn',
74
+ '@elastic/eui/require-href-for-link': 'warn',
75
+ '@elastic/eui/tooltip-no-interactive-content': 'warn',
76
+ '@elastic/eui/tooltip-button-icon-wrap': 'warn',
65
77
  },
66
78
  },
67
79
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAGH,gGAA4F;AAC5F,sFAAgF;AAChF,0FAAoF;AACpF,+DAAuD;AACvD,uDAAkD;AAClD,iFAA2E;AAC3E,iEAA2D;AAC3D,gGAA0F;AAC1F,gFAA0E;AAC1E,0EAAoE;AACpE,8FAAuF;AACvF,8EAAyE;AACzE,wFAA4F;AAC5F,oFAA+E;AAC/E,sFAAoF;AACpF,oFAAkF;AAElF,MAAM,MAAM,GAAG;IACb,KAAK,EAAE;QACL,gCAAgC,EAAE,8DAA6B;QAC/D,2BAA2B,EAAE,kDAAsB;QACnD,6BAA6B,EAAE,sDAAwB;QACvD,kBAAkB,EAAE,8BAAW;QAC/B,cAAc,EAAE,yBAAU;QAC1B,2BAA2B,EAAE,kDAAsB;QACnD,mBAAmB,EAAE,kCAAc;QACnC,gCAAgC,EAAE,4DAA2B;QAC7D,wBAAwB,EAAG,4CAAmB;QAC9C,qBAAqB,EAAE,sCAAgB;QACvC,+BAA+B,EAAE,yDAAyB;QAC1D,uBAAuB,EAAE,2CAAmB;QAC5C,4BAA4B,EAAE,8DAAiC;QAC/D,0BAA0B,EAAE,iDAAsB;QAClD,2BAA2B,EAAE,sDAA0B;QACvD,0BAA0B,EAAE,oDAAyB;KACtD;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,4BAA4B,CAAC;YACvC,KAAK,EAAE;gBACL,6CAA6C,EAAE,MAAM;gBACrD,wCAAwC,EAAE,MAAM;gBAChD,0CAA0C,EAAE,MAAM;gBAClD,+BAA+B,EAAE,MAAM;gBACvC,2BAA2B,EAAE,MAAM;gBACnC,wCAAwC,EAAE,MAAM;gBAChD,gCAAgC,EAAE,MAAM;gBACxC,6CAA6C,EAAE,MAAM;gBACrD,qCAAqC,EAAE,MAAM;gBAC7C,kCAAkC,EAAE,MAAM;gBAC1C,4CAA4C,EAAE,MAAM;gBACpD,oCAAoC,EAAE,MAAM;gBAC5C,yCAAyC,EAAE,MAAM;gBACjD,uCAAuC,EAAE,MAAM;gBAC/C,wCAAwC,EAAE,MAAM;gBAChD,uCAAuC,EAAE,MAAM;aAChD;SACF;KACF;CACF,CAAC;AAEF,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAEH,gGAA4F;AAC5F,sFAAgF;AAChF,0FAAoF;AACpF,+DAAuD;AACvD,yEAAmE;AACnE,uDAAkD;AAClD,iFAA2E;AAC3E,iEAA2D;AAC3D,gGAA0F;AAC1F,gFAA0E;AAC1E,0EAAoE;AACpE,8FAAuF;AACvF,8EAAyE;AACzE,wFAA4F;AAC5F,oFAA+E;AAC/E,iHAAyG;AACzG,sFAAoF;AACpF,oFAAkF;AAClF,gGAA0F;AAC1F,oFAA8E;AAE9E,MAAM,MAAM,GAAG;IACb,KAAK,EAAE;QACL,gCAAgC,EAAE,8DAA6B;QAC/D,2BAA2B,EAAE,kDAAsB;QACnD,6BAA6B,EAAE,sDAAwB;QACvD,kBAAkB,EAAE,8BAAW;QAC/B,cAAc,EAAE,yBAAU;QAC1B,2BAA2B,EAAE,kDAAsB;QACnD,mBAAmB,EAAE,kCAAc;QACnC,gCAAgC,EAAE,4DAA2B;QAC7D,wBAAwB,EAAE,4CAAmB;QAC7C,qBAAqB,EAAE,sCAAgB;QACvC,+BAA+B,EAAE,yDAAyB;QAC1D,uBAAuB,EAAE,2CAAmB;QAC5C,4BAA4B,EAAE,8DAAiC;QAC/D,0BAA0B,EAAE,iDAAsB;QAClD,2CAA2C,EACzC,gFAAoC;QACtC,2BAA2B,EAAE,sDAA0B;QACvD,0BAA0B,EAAE,oDAAyB;QACrD,uBAAuB,EAAE,0CAAkB;QAC3C,gCAAgC,EAAE,4DAA2B;QAC7D,0BAA0B,EAAE,gDAAqB;KAClD;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,4BAA4B,CAAC;YACvC,KAAK,EAAE;gBACL,6CAA6C,EAAE,MAAM;gBACrD,wCAAwC,EAAE,MAAM;gBAChD,0CAA0C,EAAE,MAAM;gBAClD,+BAA+B,EAAE,MAAM;gBACvC,2BAA2B,EAAE,MAAM;gBACnC,wCAAwC,EAAE,MAAM;gBAChD,gCAAgC,EAAE,MAAM;gBACxC,6CAA6C,EAAE,MAAM;gBACrD,qCAAqC,EAAE,MAAM;gBAC7C,kCAAkC,EAAE,MAAM;gBAC1C,4CAA4C,EAAE,MAAM;gBACpD,oCAAoC,EAAE,MAAM;gBAC5C,yCAAyC,EAAE,MAAM;gBACjD,uCAAuC,EAAE,MAAM;gBAC/C,wDAAwD,EAAE,MAAM;gBAChE,wCAAwC,EAAE,MAAM;gBAChD,uCAAuC,EAAE,MAAM;gBAC/C,oCAAoC,EAAE,MAAM;gBAC5C,6CAA6C,EAAE,MAAM;gBACrD,uCAAuC,EAAE,MAAM;aAChD;SACF;KACF;CACF,CAAC;AAEF,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const TooltipButtonIconWrap: ESLintUtils.RuleModule<"useEuiToolTipInsteadOfTitle" | "wrapWithEuiToolTip", [], unknown, ESLintUtils.RuleListener>;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
4
+ * or more contributor license agreements. Licensed under the Elastic License
5
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
6
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
7
+ * Side Public License, v 1.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.TooltipButtonIconWrap = void 0;
11
+ const utils_1 = require("@typescript-eslint/utils");
12
+ const remove_attr_1 = require("../../utils/remove_attr");
13
+ const has_spread_1 = require("../../utils/has_spread");
14
+ const BUTTON_ICON = 'EuiButtonIcon';
15
+ const TOOLTIP = 'EuiToolTip';
16
+ function isWrappedByTooltip(node) {
17
+ let current = node.parent;
18
+ while (current) {
19
+ switch (current.type) {
20
+ case 'JSXElement': {
21
+ const el = current;
22
+ if (el.openingElement.name.type === 'JSXIdentifier' &&
23
+ el.openingElement.name.name === TOOLTIP) {
24
+ return true;
25
+ }
26
+ current = current.parent;
27
+ break;
28
+ }
29
+ case 'JSXFragment':
30
+ case 'JSXExpressionContainer':
31
+ case 'LogicalExpression':
32
+ case 'ConditionalExpression':
33
+ current = current.parent;
34
+ break;
35
+ default:
36
+ return false;
37
+ }
38
+ }
39
+ return false;
40
+ }
41
+ exports.TooltipButtonIconWrap = utils_1.ESLintUtils.RuleCreator.withoutDocs({
42
+ create(context) {
43
+ return {
44
+ JSXElement(node) {
45
+ const { openingElement } = node;
46
+ if (openingElement.name.type !== 'JSXIdentifier' ||
47
+ openingElement.name.name !== BUTTON_ICON) {
48
+ return;
49
+ }
50
+ let titleAttr;
51
+ let ariaLabelAttr;
52
+ for (const attr of openingElement.attributes) {
53
+ if (attr.type !== 'JSXAttribute' ||
54
+ attr.name.type !== 'JSXIdentifier')
55
+ continue;
56
+ if (attr.name.name === 'title')
57
+ titleAttr = attr;
58
+ if (attr.name.name === 'aria-label')
59
+ ariaLabelAttr = attr;
60
+ }
61
+ if (titleAttr) {
62
+ const alreadyWrapped = isWrappedByTooltip(node);
63
+ context.report({
64
+ node: openingElement,
65
+ messageId: 'useEuiToolTipInsteadOfTitle',
66
+ fix: titleAttr.value
67
+ ? (fixer) => {
68
+ const titleValue = context.sourceCode.getText(titleAttr.value);
69
+ const [removeStart, removeEnd] = (0, remove_attr_1.removeAttribute)(context, titleAttr);
70
+ if (alreadyWrapped) {
71
+ return fixer.removeRange([removeStart, removeEnd]);
72
+ }
73
+ return [
74
+ fixer.removeRange([removeStart, removeEnd]),
75
+ fixer.insertTextBefore(node, `<${TOOLTIP} content=${titleValue}>\n `),
76
+ fixer.insertTextAfter(node, `\n</${TOOLTIP}>`),
77
+ ];
78
+ }
79
+ : undefined,
80
+ });
81
+ return;
82
+ }
83
+ if (!isWrappedByTooltip(node) &&
84
+ !(0, has_spread_1.hasSpread)(openingElement.attributes)) {
85
+ context.report({
86
+ node: openingElement,
87
+ messageId: 'wrapWithEuiToolTip',
88
+ fix: ariaLabelAttr?.value
89
+ ? (fixer) => {
90
+ const ariaLabelValue = context.sourceCode.getText(ariaLabelAttr.value);
91
+ return [
92
+ fixer.insertTextBefore(node, `<${TOOLTIP} content=${ariaLabelValue}>\n `),
93
+ fixer.insertTextAfter(node, `\n</${TOOLTIP}>`),
94
+ ];
95
+ }
96
+ : undefined,
97
+ });
98
+ }
99
+ },
100
+ };
101
+ },
102
+ meta: {
103
+ type: 'suggestion',
104
+ docs: {
105
+ description: `Ensure ${BUTTON_ICON} is wrapped with ${TOOLTIP} for sighted users`,
106
+ },
107
+ fixable: 'code',
108
+ schema: [],
109
+ messages: {
110
+ useEuiToolTipInsteadOfTitle: [
111
+ `Remove the \`title\` prop from ${BUTTON_ICON} and use ${TOOLTIP} instead.`,
112
+ 'Browser-native tooltips are unstyled, have no delay control and are not keyboard-accessible.',
113
+ `Wrap with <${TOOLTIP} content={label}> and use \`aria-label\` for screen readers.`,
114
+ ].join(' '),
115
+ wrapWithEuiToolTip: [
116
+ `${BUTTON_ICON} has no visible tooltip for sighted users.`,
117
+ `Wrap with <${TOOLTIP} content={ariaLabel}> using the button's \`aria-label\` as content.`,
118
+ ].join(' '),
119
+ },
120
+ },
121
+ defaultOptions: [],
122
+ });
123
+ //# sourceMappingURL=tooltip_button_icon_wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip_button_icon_wrap.js","sourceRoot":"","sources":["../../../../src/rules/a11y/tooltip_button_icon_wrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const TooltipNoInteractiveContent: ESLintUtils.RuleModule<"noInteractiveContent", [], unknown, ESLintUtils.RuleListener>;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
4
+ * or more contributor license agreements. Licensed under the Elastic License
5
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
6
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
7
+ * Side Public License, v 1.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.TooltipNoInteractiveContent = void 0;
11
+ const utils_1 = require("@typescript-eslint/utils");
12
+ const constants_1 = require("../../utils/constants");
13
+ const TOOLTIP_COMPONENTS = ['EuiToolTip', 'EuiIconTip'];
14
+ const TOOLTIP_CONTENT_PROPS = ['content', 'title'];
15
+ const INTERACTIVE_HTML_ELEMENTS = [
16
+ 'a',
17
+ 'button',
18
+ 'input',
19
+ 'select',
20
+ 'textarea',
21
+ ];
22
+ const CONDITIONALLY_INTERACTIVE_SET = new Set(constants_1.CONDITIONALLY_INTERACTIVE_EUI_COMPONENTS);
23
+ const INTERACTIVE_ELEMENTS = new Set([
24
+ ...INTERACTIVE_HTML_ELEMENTS,
25
+ ...constants_1.INTERACTIVE_EUI_COMPONENTS.filter((c) => !CONDITIONALLY_INTERACTIVE_SET.has(c)),
26
+ ]);
27
+ function findInteractiveElement(node) {
28
+ if (node.type === 'JSXElement') {
29
+ const el = node;
30
+ const { name } = el.openingElement;
31
+ if (name.type === 'JSXIdentifier' && INTERACTIVE_ELEMENTS.has(name.name)) {
32
+ return el.openingElement;
33
+ }
34
+ for (const child of el.children) {
35
+ const found = findInteractiveElement(child);
36
+ if (found)
37
+ return found;
38
+ }
39
+ }
40
+ else if (node.type === 'JSXFragment') {
41
+ for (const child of node.children) {
42
+ const found = findInteractiveElement(child);
43
+ if (found)
44
+ return found;
45
+ }
46
+ }
47
+ else if (node.type === 'JSXExpressionContainer') {
48
+ const { expression } = node;
49
+ if (expression.type !== 'JSXEmptyExpression') {
50
+ return findInteractiveElement(expression);
51
+ }
52
+ }
53
+ else if (node.type === 'LogicalExpression') {
54
+ const { left, right } = node;
55
+ return findInteractiveElement(left) ?? findInteractiveElement(right);
56
+ }
57
+ else if (node.type === 'ConditionalExpression') {
58
+ const { consequent, alternate } = node;
59
+ return (findInteractiveElement(consequent) ?? findInteractiveElement(alternate));
60
+ }
61
+ return null;
62
+ }
63
+ exports.TooltipNoInteractiveContent = utils_1.ESLintUtils.RuleCreator.withoutDocs({
64
+ create(context) {
65
+ return {
66
+ JSXElement(node) {
67
+ const { openingElement } = node;
68
+ if (openingElement.name.type !== 'JSXIdentifier' ||
69
+ !TOOLTIP_COMPONENTS.includes(openingElement.name.name)) {
70
+ return;
71
+ }
72
+ const componentName = openingElement.name.name;
73
+ for (const attr of openingElement.attributes) {
74
+ if (attr.type !== 'JSXAttribute' ||
75
+ attr.name.type !== 'JSXIdentifier' ||
76
+ !TOOLTIP_CONTENT_PROPS.includes(attr.name.name)) {
77
+ continue;
78
+ }
79
+ if (!attr.value || attr.value.type !== 'JSXExpressionContainer') {
80
+ continue;
81
+ }
82
+ const { expression } = attr.value;
83
+ if (expression.type === 'JSXEmptyExpression') {
84
+ continue;
85
+ }
86
+ const interactiveEl = findInteractiveElement(expression);
87
+ if (interactiveEl) {
88
+ context.report({
89
+ node: interactiveEl,
90
+ messageId: 'noInteractiveContent',
91
+ data: {
92
+ propName: attr.name.name,
93
+ componentName,
94
+ elementName: interactiveEl.name
95
+ .name,
96
+ },
97
+ });
98
+ }
99
+ }
100
+ },
101
+ };
102
+ },
103
+ meta: {
104
+ type: 'problem',
105
+ docs: {
106
+ description: 'Disallow interactive elements in `EuiToolTip` and `EuiIconTip` content',
107
+ },
108
+ schema: [],
109
+ messages: {
110
+ noInteractiveContent: '{{ elementName }} inside {{ componentName }} {{ propName }} is not keyboard-reachable. Tooltip content renders in a portal with `role="tooltip"`. Use `EuiPopover` for interactive content instead.',
111
+ },
112
+ },
113
+ defaultOptions: [],
114
+ });
115
+ //# sourceMappingURL=tooltip_no_interactive_content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip_no_interactive_content.js","sourceRoot":"","sources":["../../../../src/rules/a11y/tooltip_no_interactive_content.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.HrefOnClick = void 0;
11
11
  const utils_1 = require("@typescript-eslint/utils");
12
- const componentNames = ['EuiButton', 'EuiButtonEmpty', 'EuiLink', 'EuiBadge'];
12
+ const componentNames = ['EuiButton', 'EuiButtonEmpty', 'EuiBadge'];
13
13
  exports.HrefOnClick = utils_1.ESLintUtils.RuleCreator.withoutDocs({
14
14
  create(context) {
15
15
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"href_or_on_click.js","sourceRoot":"","sources":["../../../src/rules/href_or_on_click.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH,oDAAiE;AAEjE,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAEjE,QAAA,WAAW,GAAG,mBAAW,CAAC,WAAW,CAAC,WAAW,CAAC;IAC7D,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,iBAAiB,CAAC,IAAgC;gBAChD,uDAAuD;gBACvD,IACE,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe;oBAClC,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACxC,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,6DAA6D;gBAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAClC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CACpE,CAAC;gBACF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACrC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CACvE,CAAC;gBAEF,iDAAiD;gBACjD,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;oBAC1B,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,SAAS,EAAE,eAAe;wBAC1B,IAAI,EAAE;4BACJ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;yBACrB;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,2EAA2E;SAC9E;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,aAAa,EACX,6MAA6M;SAChN;KACF;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
1
+ {"version":3,"file":"href_or_on_click.js","sourceRoot":"","sources":["../../../src/rules/href_or_on_click.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH,oDAAiE;AAEjE,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,gBAAgB,EAAE,UAAU,CAAC,CAAC;AAEtD,QAAA,WAAW,GAAG,mBAAW,CAAC,WAAW,CAAC,WAAW,CAAC;IAC7D,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,iBAAiB,CAAC,IAAgC;gBAChD,uDAAuD;gBACvD,IACE,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe;oBAClC,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACxC,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,6DAA6D;gBAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAClC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CACpE,CAAC;gBACF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACrC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CACvE,CAAC;gBAEF,iDAAiD;gBACjD,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;oBAC1B,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,SAAS,EAAE,eAAe;wBAC1B,IAAI,EAAE;4BACJ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;yBACrB;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,2EAA2E;SAC9E;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,aAAa,EACX,6MAA6M;SAChN;KACF;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const PreferTooltipTriggerFocusTestUtility: ESLintUtils.RuleModule<"preferTooltipTriggerFocusTestUtility", [], unknown, ESLintUtils.RuleListener>;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
4
+ * or more contributor license agreements. Licensed under the Elastic License
5
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
6
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
7
+ * Side Public License, v 1.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.PreferTooltipTriggerFocusTestUtility = void 0;
11
+ const utils_1 = require("@typescript-eslint/utils");
12
+ const TOOLTIP_ROLE_QUERIES = new Set([
13
+ 'getByRole',
14
+ 'queryByRole',
15
+ 'findByRole',
16
+ ]);
17
+ const isTooltipRoleQuery = (node) => {
18
+ const firstArg = node.arguments[0];
19
+ if (firstArg?.type !== 'Literal' || firstArg.value !== 'tooltip')
20
+ return false;
21
+ const callee = node.callee;
22
+ // `screen.getByRole('tooltip')` or destructured `getByRole('tooltip')`
23
+ if (callee.type === 'MemberExpression')
24
+ return (callee.property.type === 'Identifier' &&
25
+ TOOLTIP_ROLE_QUERIES.has(callee.property.name));
26
+ if (callee.type === 'Identifier')
27
+ return TOOLTIP_ROLE_QUERIES.has(callee.name);
28
+ return false;
29
+ };
30
+ const isFireEventFocus = (node) => node.callee.type === 'MemberExpression' &&
31
+ node.callee.object.type === 'Identifier' &&
32
+ node.callee.object.name === 'fireEvent' &&
33
+ node.callee.property.type === 'Identifier' &&
34
+ node.callee.property.name === 'focus';
35
+ const isEuiToolTipClassSelector = (node) => typeof node.value === 'string' && node.value.includes('euiToolTip');
36
+ const isTestBlock = (node) => {
37
+ const callee = node.callee;
38
+ return (callee.type === 'Identifier' &&
39
+ (callee.name === 'it' || callee.name === 'test'));
40
+ };
41
+ exports.PreferTooltipTriggerFocusTestUtility = utils_1.ESLintUtils.RuleCreator.withoutDocs({
42
+ create(context) {
43
+ const blockStack = [];
44
+ const currentBlock = () => blockStack[blockStack.length - 1];
45
+ return {
46
+ CallExpression(node) {
47
+ if (isTestBlock(node)) {
48
+ blockStack.push({ violations: [], hasTooltipSignal: false });
49
+ return;
50
+ }
51
+ const block = currentBlock();
52
+ if (!block)
53
+ return;
54
+ if (isTooltipRoleQuery(node))
55
+ block.hasTooltipSignal = true;
56
+ if (isFireEventFocus(node))
57
+ block.violations.push(node);
58
+ },
59
+ Literal(node) {
60
+ const block = currentBlock();
61
+ if (!block)
62
+ return;
63
+ if (isEuiToolTipClassSelector(node))
64
+ block.hasTooltipSignal = true;
65
+ },
66
+ 'CallExpression:exit'(node) {
67
+ if (!isTestBlock(node))
68
+ return;
69
+ const block = blockStack.pop();
70
+ if (!block?.hasTooltipSignal)
71
+ return;
72
+ for (const violation of block.violations) {
73
+ context.report({
74
+ node: violation,
75
+ messageId: 'preferTooltipTriggerFocusTestUtility',
76
+ });
77
+ }
78
+ },
79
+ };
80
+ },
81
+ meta: {
82
+ type: 'suggestion',
83
+ docs: {
84
+ description: 'Prefer `focusEuiToolTipTrigger` over `fireEvent.focus` in tooltip tests. Plain `fireEvent.focus` does not simulate `:focus-visible` in jsdom and will not trigger the tooltip.',
85
+ },
86
+ schema: [],
87
+ messages: {
88
+ preferTooltipTriggerFocusTestUtility: 'Use `focusEuiToolTipTrigger` from EUI test utilities instead of `fireEvent.focus` when testing tooltip focus behavior.',
89
+ },
90
+ },
91
+ defaultOptions: [],
92
+ });
93
+ //# sourceMappingURL=prefer_tooltip_trigger_focus_test_utility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefer_tooltip_trigger_focus_test_utility.js","sourceRoot":"","sources":["../../../src/rules/prefer_tooltip_trigger_focus_test_utility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const RequireHrefForLink: ESLintUtils.RuleModule<"requireHrefForLink", [], unknown, ESLintUtils.RuleListener>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
4
+ * or more contributor license agreements. Licensed under the Elastic License
5
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
6
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
7
+ * Side Public License, v 1.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.RequireHrefForLink = void 0;
11
+ const utils_1 = require("@typescript-eslint/utils");
12
+ const has_spread_1 = require("../utils/has_spread");
13
+ exports.RequireHrefForLink = utils_1.ESLintUtils.RuleCreator.withoutDocs({
14
+ create(context) {
15
+ return {
16
+ JSXOpeningElement(node) {
17
+ if (node.name.type !== 'JSXIdentifier' ||
18
+ node.name.name !== 'EuiLink') {
19
+ return;
20
+ }
21
+ // Bail out if props are spread — we can't statically determine
22
+ // whether `href` is provided via the spread
23
+ if ((0, has_spread_1.hasSpread)(node.attributes)) {
24
+ return;
25
+ }
26
+ // Check if the node has `href` and `onClick` attributes
27
+ const hasHref = node.attributes.some((attr) => attr.type === 'JSXAttribute' && attr.name.name === 'href');
28
+ const hasOnClick = node.attributes.some((attr) => attr.type === 'JSXAttribute' && attr.name.name === 'onClick');
29
+ // Report an issue if `onClick` is present without `href`
30
+ if (hasOnClick && !hasHref) {
31
+ context.report({
32
+ node,
33
+ messageId: 'requireHrefForLink',
34
+ data: {
35
+ name: node.name.name,
36
+ },
37
+ });
38
+ }
39
+ },
40
+ };
41
+ },
42
+ meta: {
43
+ type: 'suggestion',
44
+ docs: {
45
+ description: 'Recommend including `href` alongside `onClick` on EuiLink so that Ctrl/Cmd+Click (open link in new tab) works.',
46
+ },
47
+ schema: [],
48
+ messages: {
49
+ requireHrefForLink: '<{{name}}> has `onClick` but no `href`. Consider adding `href` so that the component renders as a link and supports Ctrl/Cmd+Click / "Open link in new tab".',
50
+ },
51
+ },
52
+ defaultOptions: [],
53
+ });
54
+ //# sourceMappingURL=require_href_for_link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require_href_for_link.js","sourceRoot":"","sources":["../../../src/rules/require_href_for_link.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH,oDAAiE;AAEjE,oDAAgD;AAEnC,QAAA,kBAAkB,GAAG,mBAAW,CAAC,WAAW,CAAC,WAAW,CAAC;IACpE,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,iBAAiB,CAAC,IAAgC;gBAChD,IACE,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,eAAe;oBAClC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,EAC5B,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,+DAA+D;gBAC/D,4CAA4C;gBAC5C,IAAI,IAAA,sBAAS,EAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBAED,wDAAwD;gBACxD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAClC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CACpE,CAAC;gBACF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CACrC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CACvE,CAAC;gBAEF,yDAAyD;gBACzD,IAAI,UAAU,IAAI,CAAC,OAAO,EAAE,CAAC;oBAC3B,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,SAAS,EAAE,oBAAoB;wBAC/B,IAAI,EAAE;4BACJ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;yBACrB;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IACD,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,gHAAgH;SACnH;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,kBAAkB,EAChB,8JAA8J;SACjK;KACF;IACD,cAAc,EAAE,EAAE;CACnB,CAAC,CAAC"}
@@ -24,3 +24,10 @@ export declare const NON_INTERACTIVE_HTML_TAGS: string[];
24
24
  * This list should be kept up to date with EUI's interactive component offerings.
25
25
  */
26
26
  export declare const INTERACTIVE_EUI_COMPONENTS: string[];
27
+ /**
28
+ * EUI components that are only interactive when `onClick` or `href` is provided.
29
+ * Without those props they render as a plain non-focusable element (span/div),
30
+ * so rules that need to distinguish unconditionally-interactive components should
31
+ * exclude these.
32
+ */
33
+ export declare const CONDITIONALLY_INTERACTIVE_EUI_COMPONENTS: string[];
@@ -7,7 +7,7 @@
7
7
  * Side Public License, v 1.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.INTERACTIVE_EUI_COMPONENTS = exports.NON_INTERACTIVE_HTML_TAGS = void 0;
10
+ exports.CONDITIONALLY_INTERACTIVE_EUI_COMPONENTS = exports.INTERACTIVE_EUI_COMPONENTS = exports.NON_INTERACTIVE_HTML_TAGS = void 0;
11
11
  /**
12
12
  * A list of standard HTML tags that are considered **non-interactive** elements.
13
13
  *
@@ -75,45 +75,61 @@ exports.NON_INTERACTIVE_HTML_TAGS = [
75
75
  * This list should be kept up to date with EUI's interactive component offerings.
76
76
  */
77
77
  exports.INTERACTIVE_EUI_COMPONENTS = [
78
- 'EuiLink',
78
+ 'EuiBadge',
79
+ 'EuiBasicTable',
80
+ 'EuiBetaBadge',
81
+ 'EuiBreadcrumbs',
79
82
  'EuiButton',
80
83
  'EuiButtonEmpty',
84
+ 'EuiButtonGroup',
81
85
  'EuiButtonIcon',
86
+ 'EuiCard',
87
+ 'EuiCheckableCard',
88
+ 'EuiCheckbox',
89
+ 'EuiColorPicker',
90
+ 'EuiComboBox',
91
+ 'EuiContextMenuItem',
92
+ 'EuiDatePicker',
93
+ 'EuiDualRange',
82
94
  'EuiFacetButton',
95
+ 'EuiFieldNumber',
96
+ 'EuiFieldPassword',
97
+ 'EuiFieldSearch',
98
+ 'EuiFieldText',
99
+ 'EuiFilterButton',
100
+ 'EuiFilterSelectItem',
101
+ 'EuiFilterSelectable',
83
102
  'EuiHeaderLink',
84
- 'EuiHeaderSectionItemButton',
85
103
  'EuiHeaderLogo',
104
+ 'EuiHeaderSectionItemButton',
105
+ 'EuiInMemoryTable',
106
+ 'EuiKeyPadMenuItem',
107
+ 'EuiLink',
86
108
  'EuiListGroupItem',
109
+ 'EuiPagination',
87
110
  'EuiPinnableListGroup',
111
+ 'EuiRadio',
112
+ 'EuiRange',
113
+ 'EuiSelect',
114
+ 'EuiSelectable',
88
115
  'EuiSideNav',
89
- 'EuiBreadcrumbs',
90
- 'EuiTab',
91
- 'EuiContextMenuItem',
92
- 'EuiKeyPadMenuItem',
93
- 'EuiPagination',
94
- 'EuiTreeView',
95
116
  'EuiStepHorizontal',
96
- 'EuiCard',
97
- 'EuiCheckableCard',
98
- 'EuiBasicTable',
99
- 'EuiInMemoryTable',
100
- 'EuiFilterButton',
101
- 'EuiFilterSelectItem',
102
- 'EuiFilterSelectable',
103
- 'EuiBadge',
104
- 'EuiBetaBadge',
105
- 'EuiSelectable',
106
- 'EuiComboBox',
117
+ 'EuiSuperDatePicker',
107
118
  'EuiSuperSelect',
108
- 'EuiSelect',
109
- 'EuiCheckbox',
110
- 'EuiRadio',
111
119
  'EuiSwitch',
112
- 'EuiButtonGroup',
113
- 'EuiRange',
114
- 'EuiDualRange',
115
- 'EuiColorPicker',
116
- 'EuiDatePicker',
117
- 'EuiSuperDatePicker'
120
+ 'EuiTab',
121
+ 'EuiTextArea',
122
+ 'EuiTreeView'
123
+ ];
124
+ /**
125
+ * EUI components that are only interactive when `onClick` or `href` is provided.
126
+ * Without those props they render as a plain non-focusable element (span/div),
127
+ * so rules that need to distinguish unconditionally-interactive components should
128
+ * exclude these.
129
+ */
130
+ exports.CONDITIONALLY_INTERACTIVE_EUI_COMPONENTS = [
131
+ 'EuiBadge',
132
+ 'EuiBetaBadge',
133
+ 'EuiCard',
118
134
  ];
119
135
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/utils/constants.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH;;;;;;;;;;;;GAYG;AACU,QAAA,yBAAyB,GAAG;IACvC,KAAK;IACL,MAAM;IACN,GAAG;IACH,SAAS;IACT,OAAO;IACP,YAAY;IACZ,IAAI;IACJ,SAAS;IACT,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,YAAY;IACZ,QAAQ;IACR,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,MAAM;IACN,KAAK;IACL,IAAI;IACJ,KAAK;IACL,SAAS;IACT,OAAO;IACP,QAAQ;IACR,KAAK;IACL,KAAK;IACL,OAAO;IACP,OAAO;IACP,IAAI;IACJ,OAAO;IACP,IAAI;IACJ,OAAO;IACP,IAAI;IACJ,IAAI;CACL,CAAC;AAEF;;;;;;;;;;GAUG;AACU,QAAA,0BAA0B,GAAG;IACxC,SAAS;IACT,WAAW;IACX,gBAAgB;IAChB,eAAe;IACf,gBAAgB;IAChB,eAAe;IACf,4BAA4B;IAC5B,eAAe;IACf,kBAAkB;IAClB,sBAAsB;IACtB,YAAY;IACZ,gBAAgB;IAChB,QAAQ;IACR,oBAAoB;IACpB,mBAAmB;IACnB,eAAe;IACf,aAAa;IACb,mBAAmB;IACnB,SAAS;IACT,kBAAkB;IAClB,eAAe;IACf,kBAAkB;IAClB,iBAAiB;IACjB,qBAAqB;IACrB,qBAAqB;IACrB,UAAU;IACV,cAAc;IACd,eAAe;IACf,aAAa;IACb,gBAAgB;IAChB,WAAW;IACX,aAAa;IACb,UAAU;IACV,WAAW;IACX,gBAAgB;IAChB,UAAU;IACV,cAAc;IACd,gBAAgB;IAChB,eAAe;IACf,oBAAoB;CACrB,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/utils/constants.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAEH;;;;;;;;;;;;GAYG;AACU,QAAA,yBAAyB,GAAG;IACvC,KAAK;IACL,MAAM;IACN,GAAG;IACH,SAAS;IACT,OAAO;IACP,YAAY;IACZ,IAAI;IACJ,SAAS;IACT,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,YAAY;IACZ,QAAQ;IACR,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,IAAI;IACJ,MAAM;IACN,KAAK;IACL,IAAI;IACJ,KAAK;IACL,SAAS;IACT,OAAO;IACP,QAAQ;IACR,KAAK;IACL,KAAK;IACL,OAAO;IACP,OAAO;IACP,IAAI;IACJ,OAAO;IACP,IAAI;IACJ,OAAO;IACP,IAAI;IACJ,IAAI;CACL,CAAC;AAEF;;;;;;;;;;GAUG;AACU,QAAA,0BAA0B,GAAG;IACxC,UAAU;IACV,eAAe;IACf,cAAc;IACd,gBAAgB;IAChB,WAAW;IACX,gBAAgB;IAChB,gBAAgB;IAChB,eAAe;IACf,SAAS;IACT,kBAAkB;IAClB,aAAa;IACb,gBAAgB;IAChB,aAAa;IACb,oBAAoB;IACpB,eAAe;IACf,cAAc;IACd,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,gBAAgB;IAChB,cAAc;IACd,iBAAiB;IACjB,qBAAqB;IACrB,qBAAqB;IACrB,eAAe;IACf,eAAe;IACf,4BAA4B;IAC5B,kBAAkB;IAClB,mBAAmB;IACnB,SAAS;IACT,kBAAkB;IAClB,eAAe;IACf,sBAAsB;IACtB,UAAU;IACV,UAAU;IACV,WAAW;IACX,eAAe;IACf,YAAY;IACZ,mBAAmB;IACnB,oBAAoB;IACpB,gBAAgB;IAChB,WAAW;IACX,QAAQ;IACR,aAAa;IACb,aAAa;CACd,CAAC;AAEF;;;;;GAKG;AACU,QAAA,wCAAwC,GAAG;IACtD,UAAU;IACV,cAAc;IACd,SAAS;CACV,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elastic/eslint-plugin-eui",
3
- "version": "2.11.1",
3
+ "version": "2.13.0-snapshot.1779110362015",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -57,5 +57,6 @@
57
57
  "!**/*.test.js",
58
58
  "!**/*.test.js.map",
59
59
  "!**/*.test.d.ts"
60
- ]
60
+ ],
61
+ "stableVersion": "2.13.0"
61
62
  }