@elastic/eslint-plugin-eui 2.6.0 → 2.8.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 (39) hide show
  1. package/README.md +15 -0
  2. package/lib/cjs/index.js +33 -24
  3. package/lib/cjs/rules/a11y/badge_accessibility_rules.d.ts +3 -0
  4. package/lib/cjs/rules/a11y/badge_accessibility_rules.d.ts.map +1 -0
  5. package/lib/cjs/rules/a11y/badge_accessibility_rules.js +82 -0
  6. package/lib/cjs/rules/a11y/icon_accessibility_rules.d.ts +3 -0
  7. package/lib/cjs/rules/a11y/icon_accessibility_rules.d.ts.map +1 -0
  8. package/lib/cjs/rules/a11y/icon_accessibility_rules.js +82 -0
  9. package/lib/cjs/rules/no_css_color.d.ts.map +1 -1
  10. package/lib/cjs/rules/no_css_color.js +6 -4
  11. package/lib/cjs/rules/no_static_z_index.d.ts +3 -0
  12. package/lib/cjs/rules/no_static_z_index.d.ts.map +1 -0
  13. package/lib/cjs/rules/no_static_z_index.js +361 -0
  14. package/lib/cjs/utils/get_property_name.d.ts +3 -0
  15. package/lib/cjs/utils/get_property_name.d.ts.map +1 -0
  16. package/lib/cjs/utils/get_property_name.js +28 -0
  17. package/lib/cjs/utils/remove_attr.d.ts +26 -0
  18. package/lib/cjs/utils/remove_attr.d.ts.map +1 -0
  19. package/lib/cjs/utils/remove_attr.js +39 -0
  20. package/lib/esm/index.js +33 -24
  21. package/lib/esm/index.js.map +1 -1
  22. package/lib/esm/rules/a11y/badge_accessibility_rules.d.ts +2 -0
  23. package/lib/esm/rules/a11y/badge_accessibility_rules.js +84 -0
  24. package/lib/esm/rules/a11y/badge_accessibility_rules.js.map +1 -0
  25. package/lib/esm/rules/a11y/icon_accessibility_rules.d.ts +2 -0
  26. package/lib/esm/rules/a11y/icon_accessibility_rules.js +87 -0
  27. package/lib/esm/rules/a11y/icon_accessibility_rules.js.map +1 -0
  28. package/lib/esm/rules/no_css_color.js +10 -7
  29. package/lib/esm/rules/no_css_color.js.map +1 -1
  30. package/lib/esm/rules/no_static_z_index.d.ts +2 -0
  31. package/lib/esm/rules/no_static_z_index.js +394 -0
  32. package/lib/esm/rules/no_static_z_index.js.map +1 -0
  33. package/lib/esm/utils/get_property_name.d.ts +2 -0
  34. package/lib/esm/utils/get_property_name.js +27 -0
  35. package/lib/esm/utils/get_property_name.js.map +1 -0
  36. package/lib/esm/utils/remove_attr.d.ts +25 -0
  37. package/lib/esm/utils/remove_attr.js +34 -0
  38. package/lib/esm/utils/remove_attr.js.map +1 -0
  39. package/package.json +1 -1
@@ -0,0 +1,361 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NoStaticZIndex = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _get_property_name = require("../utils/get_property_name");
9
+ /*
10
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
11
+ * or more contributor license agreements. Licensed under the Elastic License
12
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
13
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
14
+ * Side Public License, v 1.
15
+ */
16
+
17
+ const propertiesCheckingZIndex = ['zIndex', 'z-index'];
18
+ const zIndexDeclarationRegex = RegExp(String.raw`(${propertiesCheckingZIndex.join('|')})`);
19
+
20
+ // Regex to find z-index declarations in CSS strings
21
+ // Matches:
22
+ // 1. z-index property name (case insensitive)
23
+ // 2. colon
24
+ // 3. value (captured group 1) until semicolon, closing brace, or !important
25
+ const cssZIndexRegex = /z-index\s*:\s*([^;!}]+)/gi;
26
+ const checkPropertySpecifiesInvalidZIndex = (property, value) => {
27
+ if (!property || value === undefined || value === null) return false;
28
+ const normalizedProperty = property.trim();
29
+ const isZIndex = propertiesCheckingZIndex.includes(normalizedProperty);
30
+ if (!isZIndex) return false;
31
+ return isInvalidZIndexValue(value);
32
+ };
33
+ const isInvalidZIndexValue = value => {
34
+ const allowedCssKeywords = ['auto', 'inherit', 'initial', 'unset', 'revert', 'revert-layer'];
35
+ const stringValue = String(value).trim().toLowerCase();
36
+ if (allowedCssKeywords.includes(stringValue)) {
37
+ return false;
38
+ }
39
+
40
+ // Check if it's a number (positive, negative, or zero)
41
+ // This regex allows integers. z-index only accepts integers.
42
+ if (/^-?\d+$/.test(stringValue)) {
43
+ return true;
44
+ }
45
+ return false;
46
+ };
47
+ const raiseReportIfPropertyHasInvalidZIndex = (context, propertyNode, messageToReport) => {
48
+ let didReport = false;
49
+ const propertyName = (0, _get_property_name.getPropertyName)(propertyNode);
50
+ if (!propertyName || !zIndexDeclarationRegex.test(propertyName)) {
51
+ return didReport;
52
+ }
53
+ const visitNode = node => {
54
+ // Handle Literal values: zIndex: 10, 'z-index': '10'
55
+ if (node.type === 'Literal') {
56
+ if (checkPropertySpecifiesInvalidZIndex(propertyName, node.value)) {
57
+ didReport = true;
58
+ context.report({
59
+ ...messageToReport,
60
+ loc: node.loc
61
+ });
62
+ }
63
+ }
64
+ // Handle Identifier values: zIndex: someVar
65
+ else if (node.type === 'Identifier') {
66
+ const identifierName = node.name;
67
+ const identifierDeclaration = context.sourceCode.getScope(node).variables.find(variable => variable.name === identifierName);
68
+ const identifierDeclarationInit = identifierDeclaration?.defs[0]?.node.type === 'VariableDeclarator' ? identifierDeclaration.defs[0].node.init : undefined;
69
+ if (identifierDeclarationInit?.type === 'Literal' && checkPropertySpecifiesInvalidZIndex(propertyName, identifierDeclarationInit.value)) {
70
+ didReport = true;
71
+ context.report({
72
+ loc: node.loc,
73
+ messageId: 'noStaticZIndexSpecificDeclaredVariable',
74
+ data: {
75
+ property: propertyName,
76
+ line: String(node.loc.start.line),
77
+ variableName: node.name
78
+ }
79
+ });
80
+ }
81
+ } else if (node.type === 'ConditionalExpression') {
82
+ visitNode(node.consequent);
83
+ visitNode(node.alternate);
84
+ } else if (node.type === 'LogicalExpression') {
85
+ visitNode(node.left);
86
+ visitNode(node.right);
87
+ } else if (node.type === 'TSAsExpression') {
88
+ visitNode(node.expression);
89
+ } else if (node.type === 'UnaryExpression') {
90
+ if (node.operator === '-') {
91
+ if (node.argument.type === 'Literal' && typeof node.argument.value === 'number') {
92
+ if (checkPropertySpecifiesInvalidZIndex(propertyName, -node.argument.value)) {
93
+ didReport = true;
94
+ context.report({
95
+ ...messageToReport,
96
+ loc: node.loc
97
+ });
98
+ }
99
+ } else {
100
+ visitNode(node.argument);
101
+ }
102
+ }
103
+ }
104
+ };
105
+ if (propertyNode.value) {
106
+ visitNode(propertyNode.value);
107
+ }
108
+ return didReport;
109
+ };
110
+ const handleObjectProperties = (context, propertyParentNode, property, reportMessage) => {
111
+ if (property.type === 'Property') {
112
+ if (property.value.type === 'ObjectExpression') {
113
+ property.value.properties.forEach(nestedProperty => {
114
+ const nestedReportMessage = {
115
+ ...reportMessage,
116
+ loc: nestedProperty.loc
117
+ };
118
+ if (nestedReportMessage.data) {
119
+ const newData = {
120
+ ...nestedReportMessage.data,
121
+ property: (0, _get_property_name.getPropertyName)(nestedProperty) || 'unknown'
122
+ };
123
+ if ('line' in newData) {
124
+ newData.line = String(nestedProperty.loc.start.line);
125
+ }
126
+ nestedReportMessage.data = newData;
127
+ }
128
+ handleObjectProperties(context, propertyParentNode, nestedProperty, nestedReportMessage);
129
+ });
130
+ } else {
131
+ raiseReportIfPropertyHasInvalidZIndex(context, property, reportMessage);
132
+ }
133
+ } else if (property.type === 'SpreadElement') {
134
+ if (property.argument.type !== 'Identifier') {
135
+ return;
136
+ }
137
+ const spreadElementIdentifierName = property.argument.name;
138
+ let scopeNode = propertyParentNode;
139
+ if (propertyParentNode.type === 'JSXAttribute' && propertyParentNode.value?.type === 'JSXExpressionContainer') {
140
+ scopeNode = propertyParentNode.value.expression;
141
+ }
142
+ const spreadElementDeclaration = context.sourceCode.getScope(scopeNode).references.find(ref => ref.identifier.name === spreadElementIdentifierName)?.resolved;
143
+ if (!spreadElementDeclaration) {
144
+ return;
145
+ }
146
+ const propertyName = (0, _get_property_name.getPropertyName)(property) || 'spread';
147
+ reportMessage = {
148
+ loc: propertyParentNode.loc,
149
+ messageId: 'noStaticZIndexSpecificDeclaredVariable',
150
+ data: {
151
+ property: propertyName,
152
+ variableName: spreadElementIdentifierName,
153
+ line: String(property.loc.start.line)
154
+ }
155
+ };
156
+ const spreadElementDeclarationNode = spreadElementDeclaration.defs[0]?.node.type === 'VariableDeclarator' ? spreadElementDeclaration.defs[0].node.init : undefined;
157
+ if (spreadElementDeclarationNode?.type === 'ObjectExpression') {
158
+ spreadElementDeclarationNode.properties.forEach(spreadProperty => {
159
+ handleObjectProperties(context, propertyParentNode, spreadProperty, reportMessage);
160
+ });
161
+ }
162
+ }
163
+ };
164
+ const checkTemplateLiteralForZIndex = (context, node) => {
165
+ for (let i = 0; i < node.quasis.length; i++) {
166
+ const declarationTemplateNode = node.quasis[i];
167
+ const rawValue = declarationTemplateNode.value.raw;
168
+ // Strip comments
169
+ const valueWithoutComments = rawValue.replace(/\/\*[\s\S]*?\*\//g, '');
170
+ let match;
171
+ // reset regex state
172
+ cssZIndexRegex.lastIndex = 0;
173
+ while ((match = cssZIndexRegex.exec(valueWithoutComments)) !== null) {
174
+ const value = match[1].trim();
175
+ if (isInvalidZIndexValue(value)) {
176
+ context.report({
177
+ node: declarationTemplateNode,
178
+ messageId: 'noStaticZIndex'
179
+ });
180
+ }
181
+ }
182
+ }
183
+ };
184
+ const NoStaticZIndex = exports.NoStaticZIndex = _utils.ESLintUtils.RuleCreator.withoutDocs({
185
+ create(context) {
186
+ return {
187
+ TaggedTemplateExpression(node) {
188
+ if (node.tag.type !== 'Identifier' || node.tag.type === 'Identifier' && node.tag.name !== 'css') {
189
+ return;
190
+ }
191
+ checkTemplateLiteralForZIndex(context, node.quasi);
192
+ },
193
+ JSXAttribute(node) {
194
+ if (!(node.name.name === 'style' || node.name.name === 'css')) {
195
+ return;
196
+ }
197
+ if (!node.value || node.value.type !== 'JSXExpressionContainer') {
198
+ return;
199
+ }
200
+ const expression = node.value.expression;
201
+
202
+ // Handle identifier expression: style={someStyle}
203
+ if (expression.type === 'Identifier') {
204
+ const styleVariableName = expression.name;
205
+ const nodeScope = context.sourceCode.getScope(expression);
206
+ const variableDeclarationMatches = nodeScope.references.find(ref => ref.identifier.name === styleVariableName)?.resolved;
207
+ let variableInitializationNode;
208
+ if (variableDeclarationMatches?.defs[0]?.node.type === 'VariableDeclarator' && variableDeclarationMatches.defs[0].node.init) {
209
+ variableInitializationNode = variableDeclarationMatches.defs[0].node.init;
210
+ if (variableInitializationNode.type === 'ObjectExpression') {
211
+ variableInitializationNode.properties.forEach(property => {
212
+ handleObjectProperties(context, node, property, {
213
+ loc: property.loc,
214
+ messageId: 'noStaticZIndexSpecificDeclaredVariable',
215
+ data: {
216
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown',
217
+ variableName: styleVariableName,
218
+ line: String(property.loc.start.line)
219
+ }
220
+ });
221
+ });
222
+ } else if (variableInitializationNode.type === 'CallExpression' && variableInitializationNode.callee.type === 'Identifier' && variableInitializationNode.callee.name === 'css') {
223
+ variableInitializationNode.arguments.forEach(argument => {
224
+ if (argument.type === 'ObjectExpression') {
225
+ argument.properties.forEach(property => {
226
+ handleObjectProperties(context, node, property, {
227
+ loc: property.loc,
228
+ messageId: 'noStaticZIndexSpecificDeclaredVariable',
229
+ data: {
230
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown',
231
+ variableName: styleVariableName,
232
+ line: String(property.loc.start.line)
233
+ }
234
+ });
235
+ });
236
+ }
237
+ });
238
+ }
239
+ }
240
+ return;
241
+ }
242
+
243
+ // Handle inline object: style={{ zIndex: 10 }}
244
+ if (expression.type === 'ObjectExpression') {
245
+ const declarationPropertiesNode = expression.properties;
246
+ declarationPropertiesNode?.forEach(property => {
247
+ handleObjectProperties(context, node, property, {
248
+ loc: property.loc,
249
+ messageId: 'noStaticZIndexSpecific',
250
+ data: {
251
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
252
+ }
253
+ });
254
+ });
255
+ return;
256
+ }
257
+
258
+ // Handle inline CallExpression: css={css({ zIndex: 10 })}
259
+ if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && expression.callee.name === 'css') {
260
+ expression.arguments.forEach(argument => {
261
+ if (argument.type === 'ObjectExpression') {
262
+ argument.properties.forEach(property => {
263
+ handleObjectProperties(context, node, property, {
264
+ loc: node.loc,
265
+ messageId: 'noStaticZIndexSpecific',
266
+ data: {
267
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
268
+ }
269
+ });
270
+ });
271
+ }
272
+ });
273
+ return;
274
+ }
275
+
276
+ // Handle inline ArrayExpression: css={[...]}
277
+ if (expression.type === 'ArrayExpression') {
278
+ expression.elements.forEach(element => {
279
+ if (!element) return;
280
+ if (element.type === 'ObjectExpression') {
281
+ element.properties.forEach(property => {
282
+ handleObjectProperties(context, node, property, {
283
+ loc: property.loc,
284
+ messageId: 'noStaticZIndexSpecific',
285
+ data: {
286
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
287
+ }
288
+ });
289
+ });
290
+ } else if (element.type === 'CallExpression' && element.callee.type === 'Identifier' && element.callee.name === 'css') {
291
+ element.arguments.forEach(argument => {
292
+ if (argument.type === 'ObjectExpression') {
293
+ argument.properties.forEach(property => {
294
+ handleObjectProperties(context, node, property, {
295
+ loc: property.loc,
296
+ messageId: 'noStaticZIndexSpecific',
297
+ data: {
298
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
299
+ }
300
+ });
301
+ });
302
+ }
303
+ });
304
+ }
305
+ });
306
+ }
307
+
308
+ // Handle css prop with template literal or function
309
+ if (node.name.name === 'css') {
310
+ // css={`...`}
311
+ if (expression.type === 'TemplateLiteral') {
312
+ checkTemplateLiteralForZIndex(context, expression);
313
+ return;
314
+ }
315
+
316
+ // css={() => ({ ... })} or css={function() { return { ... } }}
317
+ if (expression.type === 'FunctionExpression' || expression.type === 'ArrowFunctionExpression') {
318
+ let declarationPropertiesNode = [];
319
+ if (expression.body.type === 'ObjectExpression') {
320
+ declarationPropertiesNode = expression.body.properties;
321
+ }
322
+ if (expression.body.type === 'BlockStatement') {
323
+ const functionReturnStatementNode = expression.body.body?.find(_node => {
324
+ return _node.type === 'ReturnStatement';
325
+ });
326
+ if (functionReturnStatementNode?.type === 'ReturnStatement' && functionReturnStatementNode.argument?.type === 'ObjectExpression') {
327
+ declarationPropertiesNode = functionReturnStatementNode.argument.properties.filter(property => property.type === 'Property');
328
+ }
329
+ }
330
+ if (!declarationPropertiesNode.length) {
331
+ return;
332
+ }
333
+ declarationPropertiesNode.forEach(property => {
334
+ handleObjectProperties(context, node, property, {
335
+ loc: property.loc,
336
+ messageId: 'noStaticZIndexSpecific',
337
+ data: {
338
+ property: (0, _get_property_name.getPropertyName)(property) || 'unknown'
339
+ }
340
+ });
341
+ });
342
+ return;
343
+ }
344
+ }
345
+ }
346
+ };
347
+ },
348
+ meta: {
349
+ type: 'suggestion',
350
+ docs: {
351
+ description: 'Use `z-index` definitions from `euiTheme` as opposed to static values'
352
+ },
353
+ messages: {
354
+ noStaticZIndexSpecificDeclaredVariable: 'Avoid using a literal z-index value for "{{property}}", use an EUI theme z-index level instead in declared variable {{variableName}} on line {{line}}',
355
+ noStaticZIndexSpecific: 'Avoid using a literal z-index value for "{{property}}", use an EUI theme z-index level instead',
356
+ noStaticZIndex: 'Avoid using a literal z-index value, use an EUI theme z-index level instead'
357
+ },
358
+ schema: []
359
+ },
360
+ defaultOptions: []
361
+ });
@@ -0,0 +1,3 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ export declare const getPropertyName: (propertyNode: TSESTree.Property | TSESTree.SpreadElement) => string | null;
3
+ //# sourceMappingURL=get_property_name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_property_name.d.ts","sourceRoot":"","sources":["../../../src/utils/get_property_name.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,eAAO,MAAM,eAAe,GAC1B,cAAc,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,aAAa,KACvD,MAAM,GAAG,IAeX,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getPropertyName = void 0;
7
+ /*
8
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
9
+ * or more contributor license agreements. Licensed under the Elastic License
10
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
11
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
12
+ * Side Public License, v 1.
13
+ */
14
+
15
+ const getPropertyName = propertyNode => {
16
+ if (propertyNode.type === 'Property') {
17
+ if (propertyNode.key.type === 'Identifier') {
18
+ return propertyNode.key.name;
19
+ }
20
+ if (propertyNode.key.type === 'Literal') {
21
+ return String(propertyNode.key.value);
22
+ }
23
+ } else if (propertyNode.type === 'SpreadElement' && propertyNode.argument.type === 'Identifier') {
24
+ return propertyNode.argument.name;
25
+ }
26
+ return null;
27
+ };
28
+ exports.getPropertyName = getPropertyName;
@@ -0,0 +1,26 @@
1
+ import { type TSESTree, type TSESLint } from '@typescript-eslint/utils';
2
+ /**
3
+ * Computes the removal range for a JSX attribute, including a preceding space
4
+ * when present, to keep formatting intact after autofix.
5
+ *
6
+ * This helper is useful in ESLint rule fixers when calling `fixer.removeRange(...)`,
7
+ * ensuring that the attribute and its leading space are removed cleanly.
8
+ *
9
+ * @typeParam TContext - An ESLint rule context type extending `TSESLint.RuleContext`.
10
+ * @param context - The current ESLint rule context providing access to `SourceCode`.
11
+ * @param attr - The JSX attribute node to remove.
12
+ * @returns A readonly tuple `[start, end]` representing the inclusive start and exclusive end indexes for removal.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * context.report({
17
+ * node: openingElement,
18
+ * messageId: 'removeAttr',
19
+ * fix: fixer => {
20
+ * const [start, end] = removeAttribute(context, ariaHiddenAttr);
21
+ * return fixer.removeRange([start, end]);
22
+ * },
23
+ * });
24
+ **/
25
+ export declare function removeAttribute<TContext extends TSESLint.RuleContext<string, unknown[]>>(context: TContext, attr: TSESTree.JSXAttribute): readonly [number, number];
26
+ //# sourceMappingURL=remove_attr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove_attr.d.ts","sourceRoot":"","sources":["../../../src/utils/remove_attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;IAsBI;AAEJ,wBAAgB,eAAe,CAC7B,QAAQ,SAAS,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EAExD,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,QAAQ,CAAC,YAAY,6BAO5B"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.removeAttribute = removeAttribute;
7
+ /**
8
+ * Computes the removal range for a JSX attribute, including a preceding space
9
+ * when present, to keep formatting intact after autofix.
10
+ *
11
+ * This helper is useful in ESLint rule fixers when calling `fixer.removeRange(...)`,
12
+ * ensuring that the attribute and its leading space are removed cleanly.
13
+ *
14
+ * @typeParam TContext - An ESLint rule context type extending `TSESLint.RuleContext`.
15
+ * @param context - The current ESLint rule context providing access to `SourceCode`.
16
+ * @param attr - The JSX attribute node to remove.
17
+ * @returns A readonly tuple `[start, end]` representing the inclusive start and exclusive end indexes for removal.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * context.report({
22
+ * node: openingElement,
23
+ * messageId: 'removeAttr',
24
+ * fix: fixer => {
25
+ * const [start, end] = removeAttribute(context, ariaHiddenAttr);
26
+ * return fixer.removeRange([start, end]);
27
+ * },
28
+ * });
29
+ **/
30
+
31
+ function removeAttribute(context, attr) {
32
+ const {
33
+ sourceCode
34
+ } = context;
35
+ const start = attr.range[0];
36
+ const before = sourceCode.text[start - 1];
37
+ const rangeStart = before === ' ' ? start - 1 : start;
38
+ return [rangeStart, attr.range[1]];
39
+ }
package/lib/esm/index.js CHANGED
@@ -7,52 +7,61 @@
7
7
  * Side Public License, v 1.
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
+ const accessible_interactive_element_1 = require("./rules/a11y/accessible_interactive_element");
11
+ const callout_announce_on_mount_1 = require("./rules/a11y/callout_announce_on_mount");
12
+ const consistent_is_invalid_props_1 = require("./rules/a11y/consistent_is_invalid_props");
10
13
  const href_or_on_click_1 = require("./rules/href_or_on_click");
11
- const no_restricted_eui_imports_1 = require("./rules/no_restricted_eui_imports");
12
14
  const no_css_color_1 = require("./rules/no_css_color");
15
+ const no_restricted_eui_imports_1 = require("./rules/no_restricted_eui_imports");
16
+ const no_static_z_index_1 = require("./rules/no_static_z_index");
17
+ const no_unnamed_interactive_element_1 = require("./rules/a11y/no_unnamed_interactive_element");
18
+ const no_unnamed_radio_group_1 = require("./rules/a11y/no_unnamed_radio_group");
19
+ const prefer_eui_icon_tip_1 = require("./rules/a11y/prefer_eui_icon_tip");
13
20
  const require_aria_label_for_modals_1 = require("./rules/a11y/require_aria_label_for_modals");
14
- const consistent_is_invalid_props_1 = require("./rules/a11y/consistent_is_invalid_props");
21
+ const require_table_caption_1 = require("./rules/a11y/require_table_caption");
15
22
  const sr_output_disabled_tooltip_1 = require("./rules/a11y/sr_output_disabled_tooltip");
16
- const prefer_eui_icon_tip_1 = require("./rules/a11y/prefer_eui_icon_tip");
17
- const no_unnamed_radio_group_1 = require("./rules/a11y/no_unnamed_radio_group");
18
- const no_unnamed_interactive_element_1 = require("./rules/a11y/no_unnamed_interactive_element");
19
23
  const tooltip_focusable_anchor_1 = require("./rules/a11y/tooltip_focusable_anchor");
20
- const callout_announce_on_mount_1 = require("./rules/a11y/callout_announce_on_mount");
21
- const accessible_interactive_element_1 = require("./rules/a11y/accessible_interactive_element");
22
- const require_table_caption_1 = require("./rules/a11y/require_table_caption");
24
+ const badge_accessibility_rules_1 = require("./rules/a11y/badge_accessibility_rules");
25
+ const icon_accessibility_rules_1 = require("./rules/a11y/icon_accessibility_rules");
23
26
  const config = {
24
27
  rules: {
28
+ 'accessible-interactive-element': accessible_interactive_element_1.AccessibleInteractiveElements,
29
+ 'callout-announce-on-mount': callout_announce_on_mount_1.CallOutAnnounceOnMount,
30
+ 'consistent-is-invalid-props': consistent_is_invalid_props_1.ConsistentIsInvalidProps,
25
31
  'href-or-on-click': href_or_on_click_1.HrefOnClick,
26
- 'no-restricted-eui-imports': no_restricted_eui_imports_1.NoRestrictedEuiImports,
27
32
  'no-css-color': no_css_color_1.NoCssColor,
33
+ 'no-restricted-eui-imports': no_restricted_eui_imports_1.NoRestrictedEuiImports,
34
+ 'no-static-z-index': no_static_z_index_1.NoStaticZIndex,
35
+ 'no-unnamed-interactive-element': no_unnamed_interactive_element_1.NoUnnamedInteractiveElement,
36
+ 'no-unnamed-radio-group': no_unnamed_radio_group_1.NoUnnamedRadioGroup,
37
+ 'prefer-eui-icon-tip': prefer_eui_icon_tip_1.PreferEuiIconTip,
28
38
  'require-aria-label-for-modals': require_aria_label_for_modals_1.RequireAriaLabelForModals,
29
- 'consistent-is-invalid-props': consistent_is_invalid_props_1.ConsistentIsInvalidProps,
39
+ 'require-table-caption': require_table_caption_1.RequireTableCaption,
30
40
  'sr-output-disabled-tooltip': sr_output_disabled_tooltip_1.ScreenReaderOutputDisabledTooltip,
31
- 'prefer-eui-icon-tip': prefer_eui_icon_tip_1.PreferEuiIconTip,
32
- 'no-unnamed-radio-group': no_unnamed_radio_group_1.NoUnnamedRadioGroup,
33
- 'callout-announce-on-mount': callout_announce_on_mount_1.CallOutAnnounceOnMount,
34
- 'no-unnamed-interactive-element': no_unnamed_interactive_element_1.NoUnnamedInteractiveElement,
35
41
  'tooltip-focusable-anchor': tooltip_focusable_anchor_1.TooltipFocusableAnchor,
36
- 'accessible-interactive-element': accessible_interactive_element_1.AccessibleInteractiveElements,
37
- 'require-table-caption': require_table_caption_1.RequireTableCaption,
42
+ 'badge-accessibility-rules': badge_accessibility_rules_1.EuiBadgeAccessibilityRules,
43
+ 'icon-accessibility-rules': icon_accessibility_rules_1.EuiIconAccessibilityRules
38
44
  },
39
45
  configs: {
40
46
  recommended: {
41
47
  plugins: ['@elastic/eslint-plugin-eui'],
42
48
  rules: {
49
+ '@elastic/eui/accessible-interactive-element': 'warn',
50
+ '@elastic/eui/callout-announce-on-mount': 'warn',
51
+ '@elastic/eui/consistent-is-invalid-props': 'warn',
43
52
  '@elastic/eui/href-or-on-click': 'warn',
44
- '@elastic/eui/no-restricted-eui-imports': 'warn',
45
53
  '@elastic/eui/no-css-color': 'warn',
54
+ '@elastic/eui/no-restricted-eui-imports': 'warn',
55
+ '@elastic/eui/no-static-z-index': 'warn',
56
+ '@elastic/eui/no-unnamed-interactive-element': 'warn',
57
+ '@elastic/eui/no-unnamed-radio-group': 'warn',
58
+ '@elastic/eui/prefer-eui-icon-tip': 'warn',
46
59
  '@elastic/eui/require-aria-label-for-modals': 'warn',
47
- '@elastic/eui/consistent-is-invalid-props': 'warn',
60
+ '@elastic/eui/require-table-caption': 'warn',
48
61
  '@elastic/eui/sr-output-disabled-tooltip': 'warn',
49
- '@elastic/eui/prefer-eui-icon-tip': 'warn',
50
- '@elastic/eui/no-unnamed-radio-group': 'warn',
51
- '@elastic/eui/callout-announce-on-mount': 'warn',
52
- '@elastic/eui/no-unnamed-interactive-element': 'warn',
53
62
  '@elastic/eui/tooltip-focusable-anchor': 'warn',
54
- '@elastic/eui/accessible-interactive-element': 'warn',
55
- '@elastic/eui/require-table-caption': 'warn',
63
+ '@elastic/eui/badge-accessibility-rules': 'warn',
64
+ '@elastic/eui/icon-accessibility-rules': 'warn',
56
65
  },
57
66
  },
58
67
  },
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAGH,+DAAuD;AACvD,iFAA2E;AAC3E,uDAAkD;AAElD,8FAAuF;AACvF,0FAAoF;AACpF,wFAA4F;AAC5F,0EAAoE;AACpE,gFAA0E;AAC1E,gGAA0F;AAC1F,oFAA+E;AAC/E,sFAAgF;AAChF,gGAA4F;AAC5F,8EAAyE;AAEzE,MAAM,MAAM,GAAG;IACb,KAAK,EAAE;QACL,kBAAkB,EAAE,8BAAW;QAC/B,2BAA2B,EAAE,kDAAsB;QACnD,cAAc,EAAE,yBAAU;QAC1B,+BAA+B,EAAE,yDAAyB;QAC1D,6BAA6B,EAAE,sDAAwB;QACvD,4BAA4B,EAAE,8DAAiC;QAC/D,qBAAqB,EAAE,sCAAgB;QACvC,wBAAwB,EAAG,4CAAmB;QAC9C,2BAA2B,EAAE,kDAAsB;QACnD,gCAAgC,EAAE,4DAA2B;QAC7D,0BAA0B,EAAE,iDAAsB;QAClD,gCAAgC,EAAE,8DAA6B;QAC/D,uBAAuB,EAAE,2CAAmB;KAC7C;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,4BAA4B,CAAC;YACvC,KAAK,EAAE;gBACL,+BAA+B,EAAE,MAAM;gBACvC,wCAAwC,EAAE,MAAM;gBAChD,2BAA2B,EAAE,MAAM;gBACnC,4CAA4C,EAAE,MAAM;gBACpD,0CAA0C,EAAE,MAAM;gBAClD,yCAAyC,EAAE,MAAM;gBACjD,kCAAkC,EAAE,MAAM;gBAC1C,qCAAqC,EAAE,MAAM;gBAC7C,wCAAwC,EAAE,MAAM;gBAChD,6CAA6C,EAAE,MAAM;gBACrD,uCAAuC,EAAE,MAAM;gBAC/C,6CAA6C,EAAE,MAAM;gBACrD,oCAAoC,EAAE,MAAM;aAC7C;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;;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"}
@@ -0,0 +1,2 @@
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ export declare const EuiBadgeAccessibilityRules: ESLintUtils.RuleModule<"sameCallback" | "iconOnClickAriaLabelWithoutIconOnClick" | "onClickAriaLabelWithoutOnClick", [], unknown, ESLintUtils.RuleListener>;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EuiBadgeAccessibilityRules = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const BADGE_COMPONENT = 'EuiBadge';
6
+ exports.EuiBadgeAccessibilityRules = utils_1.ESLintUtils.RuleCreator.withoutDocs({
7
+ create(context) {
8
+ return {
9
+ JSXElement(node) {
10
+ const { openingElement } = node;
11
+ if (openingElement.name.type !== 'JSXIdentifier' ||
12
+ openingElement.name.name !== BADGE_COMPONENT) {
13
+ return;
14
+ }
15
+ let iconOnClick, onClick, iconOnClickAriaLabel, onClickAriaLabel;
16
+ for (const attr of openingElement.attributes) {
17
+ if (attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier') {
18
+ switch (attr.name.name) {
19
+ case 'iconOnClick':
20
+ iconOnClick = attr;
21
+ break;
22
+ case 'onClick':
23
+ onClick = attr;
24
+ break;
25
+ case 'iconOnClickAriaLabel':
26
+ iconOnClickAriaLabel = attr;
27
+ break;
28
+ case 'onClickAriaLabel':
29
+ onClickAriaLabel = attr;
30
+ break;
31
+ }
32
+ }
33
+ }
34
+ // 1. iconOnClick and onClick cannot refer to the same callback
35
+ if (iconOnClick &&
36
+ onClick &&
37
+ iconOnClick.value &&
38
+ onClick.value &&
39
+ iconOnClick.value.type === 'JSXExpressionContainer' &&
40
+ onClick.value.type === 'JSXExpressionContainer' &&
41
+ iconOnClick.value.expression.type === 'Identifier' &&
42
+ onClick.value.expression.type === 'Identifier' &&
43
+ iconOnClick.value.expression.name === onClick.value.expression.name) {
44
+ context.report({
45
+ node: iconOnClick,
46
+ messageId: 'sameCallback',
47
+ fix: fixer => fixer.removeRange([iconOnClick.range[0], iconOnClick.range[1]]),
48
+ });
49
+ }
50
+ // 2. iconOnClickAriaLabel should be set only if iconOnClick is set
51
+ if (iconOnClickAriaLabel && !iconOnClick) {
52
+ context.report({
53
+ node: iconOnClickAriaLabel,
54
+ messageId: 'iconOnClickAriaLabelWithoutIconOnClick',
55
+ fix: fixer => fixer.removeRange([iconOnClickAriaLabel.range[0], iconOnClickAriaLabel.range[1]]),
56
+ });
57
+ }
58
+ // 3. onClickAriaLabel should be set only if onClick is set
59
+ if (onClickAriaLabel && !onClick) {
60
+ context.report({
61
+ node: onClickAriaLabel,
62
+ messageId: 'onClickAriaLabelWithoutOnClick',
63
+ fix: fixer => fixer.removeRange([onClickAriaLabel.range[0], onClickAriaLabel.range[1]]),
64
+ });
65
+ }
66
+ },
67
+ };
68
+ },
69
+ meta: {
70
+ type: 'problem',
71
+ docs: {
72
+ description: `Ensure the ${BADGE_COMPONENT} includes appropriate accessibility attributes`,
73
+ },
74
+ fixable: 'code',
75
+ schema: [],
76
+ messages: {
77
+ sameCallback: '`iconOnClick` and `onClick` should not refer to the same callback. Remove `iconOnClick` and keep only `onClick`.',
78
+ iconOnClickAriaLabelWithoutIconOnClick: '`iconOnClickAriaLabel` should only be set if `iconOnClick` is present. Remove this prop.',
79
+ onClickAriaLabelWithoutOnClick: '`onClickAriaLabel` should only be set if `onClick` is present. Remove this prop.',
80
+ },
81
+ },
82
+ defaultOptions: [],
83
+ });
84
+ //# sourceMappingURL=badge_accessibility_rules.js.map