@elastic/eslint-plugin-eui 2.16.0-snapshot.1788177701701 → 3.0.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.
- package/README.md +71 -2
- package/lib/cjs/index.js +4 -1
- package/lib/cjs/rules/button_group_no_invalid_children.d.ts +1 -3
- package/lib/cjs/rules/button_group_no_invalid_children.d.ts.map +1 -1
- package/lib/cjs/rules/button_group_no_invalid_children.js +28 -38
- package/lib/cjs/rules/button_group_selection_require_id.d.ts +3 -0
- package/lib/cjs/rules/button_group_selection_require_id.d.ts.map +1 -0
- package/lib/cjs/rules/button_group_selection_require_id.js +114 -0
- package/lib/cjs/rules/no_deprecated_icon_aliases.d.ts +45 -4
- package/lib/cjs/rules/no_deprecated_icon_aliases.d.ts.map +1 -1
- package/lib/cjs/rules/no_deprecated_icon_aliases.js +61 -7
- package/lib/cjs/utils/button_group_constants.d.ts +6 -0
- package/lib/cjs/utils/button_group_constants.d.ts.map +1 -0
- package/lib/cjs/utils/button_group_constants.js +19 -0
- package/lib/cjs/utils/collect_jsx_children.d.ts +3 -0
- package/lib/cjs/utils/collect_jsx_children.d.ts.map +1 -0
- package/lib/cjs/utils/collect_jsx_children.js +26 -0
- package/lib/esm/index.js +3 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/rules/button_group_no_invalid_children.d.ts +1 -3
- package/lib/esm/rules/button_group_no_invalid_children.js +34 -42
- package/lib/esm/rules/button_group_no_invalid_children.js.map +1 -1
- package/lib/esm/rules/button_group_selection_require_id.d.ts +2 -0
- package/lib/esm/rules/button_group_selection_require_id.js +118 -0
- package/lib/esm/rules/button_group_selection_require_id.js.map +1 -0
- package/lib/esm/rules/no_deprecated_icon_aliases.d.ts +45 -4
- package/lib/esm/rules/no_deprecated_icon_aliases.js +66 -5
- package/lib/esm/rules/no_deprecated_icon_aliases.js.map +1 -1
- package/lib/esm/utils/button_group_constants.d.ts +5 -0
- package/lib/esm/utils/button_group_constants.js +20 -0
- package/lib/esm/utils/button_group_constants.js.map +1 -0
- package/lib/esm/utils/collect_jsx_children.d.ts +2 -0
- package/lib/esm/utils/collect_jsx_children.js +21 -0
- package/lib/esm/utils/collect_jsx_children.js.map +1 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -380,10 +380,11 @@ Enforce that `EuiButtonGroup` children (when using the Children API) are valid b
|
|
|
380
380
|
Valid direct children depend on the variant:
|
|
381
381
|
- `variant="default"` (or no variant): `EuiButton`, `EuiButtonEmpty`, and `EuiButtonIcon`
|
|
382
382
|
- `variant="segmented"`: `EuiButton` and `EuiButtonIcon` only (`EuiButtonEmpty` is not allowed)
|
|
383
|
+
- `variant="selection"`: `EuiButton` and `EuiButtonIcon` only (`EuiButtonEmpty` is not allowed)
|
|
383
384
|
|
|
384
|
-
In addition, these wrapper components are allowed for
|
|
385
|
+
In addition, these wrapper components are allowed for all variants: `EuiPopover`, `EuiToolTip`, and `EuiCopy`.
|
|
385
386
|
|
|
386
|
-
For `variant="segmented"`, all children must also use the **same button type** — either all `EuiButton` or all `EuiButtonIcon`. Mixing both types is reported as an error, including when buttons appear inside `EuiToolTip`, as an `EuiPopover` trigger, or in an `EuiCopy` render prop.
|
|
387
|
+
For `variant="segmented"` and `variant="selection"`, all children must also use the **same button type** — either all `EuiButton` or all `EuiButtonIcon`. Mixing both types is reported as an error, including when buttons appear inside `EuiToolTip`, as an `EuiPopover` trigger, or in an `EuiCopy` render prop.
|
|
387
388
|
|
|
388
389
|
#### Examples
|
|
389
390
|
|
|
@@ -406,6 +407,17 @@ For `variant="segmented"`, all children must also use the **same button type**
|
|
|
406
407
|
<EuiButtonIcon iconType="trash" aria-label="Delete" />
|
|
407
408
|
</EuiButtonGroup>
|
|
408
409
|
|
|
410
|
+
// ✗ Bad - variant="selection" with EuiButtonEmpty (not allowed)
|
|
411
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
412
|
+
<EuiButtonEmpty color="text">Bold</EuiButtonEmpty>
|
|
413
|
+
</EuiButtonGroup>
|
|
414
|
+
|
|
415
|
+
// ✗ Bad - variant="selection" mixing EuiButton and EuiButtonIcon
|
|
416
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
417
|
+
<EuiButton id="bold">Bold</EuiButton>
|
|
418
|
+
<EuiButtonIcon id="italic" iconType="italic" aria-label="Italic" />
|
|
419
|
+
</EuiButtonGroup>
|
|
420
|
+
|
|
409
421
|
// ✓ Good - direct buttons
|
|
410
422
|
<EuiButtonGroup legend="Actions">
|
|
411
423
|
<EuiButton>Save</EuiButton>
|
|
@@ -469,6 +481,18 @@ For `variant="segmented"`, all children must also use the **same button type**
|
|
|
469
481
|
<EuiButtonIcon iconType="pencil" aria-label="Edit" />
|
|
470
482
|
<EuiButtonIcon iconType="trash" aria-label="Delete" />
|
|
471
483
|
</EuiButtonGroup>
|
|
484
|
+
|
|
485
|
+
// ✓ Good - variant="selection" with all EuiButton
|
|
486
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
487
|
+
<EuiButton id="bold">Bold</EuiButton>
|
|
488
|
+
<EuiButton id="italic">Italic</EuiButton>
|
|
489
|
+
</EuiButtonGroup>
|
|
490
|
+
|
|
491
|
+
// ✓ Good - variant="selection" with all EuiButtonIcon
|
|
492
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
493
|
+
<EuiButtonIcon id="bold" iconType="bold" aria-label="Bold" />
|
|
494
|
+
<EuiButtonIcon id="italic" iconType="italic" aria-label="Italic" />
|
|
495
|
+
</EuiButtonGroup>
|
|
472
496
|
```
|
|
473
497
|
|
|
474
498
|
#### Custom button wrapper components
|
|
@@ -480,6 +504,51 @@ If a project-specific button component (e.g. `<SaveButton />`) is used as a chil
|
|
|
480
504
|
<SaveButton />
|
|
481
505
|
```
|
|
482
506
|
|
|
507
|
+
### `@elastic/eui/button-group-selection-require-id`
|
|
508
|
+
|
|
509
|
+
Enforce that every `EuiButton` and `EuiButtonIcon` child of an `EuiButtonGroup` with `variant="selection"` has an `id` prop. The selection variant uses the `id` of each button to track which buttons are selected, so a missing `id` will cause incorrect or broken selection state at runtime.
|
|
510
|
+
|
|
511
|
+
The rule only fires when `variant="selection"` is set as a static string. Dynamic variants (`variant={myVar}`) are skipped conservatively. It traverses the same nesting depth as `button-group-no-invalid-children`: fragments, conditionals, variable resolution, `.map()`, and the three supported wrappers (`EuiToolTip`, `EuiPopover` trigger, `EuiCopy` render prop). Children with spread props (`{...props}`) are skipped — the `id` may be provided via the spread.
|
|
512
|
+
|
|
513
|
+
#### Examples
|
|
514
|
+
|
|
515
|
+
```tsx
|
|
516
|
+
// ✗ Bad - missing id on direct child
|
|
517
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
518
|
+
<EuiButton>Bold</EuiButton>
|
|
519
|
+
</EuiButtonGroup>
|
|
520
|
+
|
|
521
|
+
// ✗ Bad - missing id on button inside EuiToolTip
|
|
522
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
523
|
+
<EuiToolTip content="Italic">
|
|
524
|
+
<EuiButton>Italic</EuiButton>
|
|
525
|
+
</EuiToolTip>
|
|
526
|
+
</EuiButtonGroup>
|
|
527
|
+
|
|
528
|
+
// ✗ Bad - missing id on EuiPopover trigger
|
|
529
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
530
|
+
<EuiPopover button={<EuiButton>More</EuiButton>} isOpen={false} closePopover={() => {}}>
|
|
531
|
+
Panel content
|
|
532
|
+
</EuiPopover>
|
|
533
|
+
</EuiButtonGroup>
|
|
534
|
+
|
|
535
|
+
// ✓ Good - all children have id
|
|
536
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
537
|
+
<EuiButton id="bold">Bold</EuiButton>
|
|
538
|
+
<EuiToolTip content="Italic">
|
|
539
|
+
<EuiButton id="italic">Italic</EuiButton>
|
|
540
|
+
</EuiToolTip>
|
|
541
|
+
<EuiPopover button={<EuiButton id="more">More</EuiButton>} isOpen={false} closePopover={() => {}}>
|
|
542
|
+
Panel content
|
|
543
|
+
</EuiPopover>
|
|
544
|
+
</EuiButtonGroup>
|
|
545
|
+
|
|
546
|
+
// ✓ Good - spread props are skipped (id may come from the spread)
|
|
547
|
+
<EuiButtonGroup legend="Format" variant="selection">
|
|
548
|
+
<EuiButton {...buttonProps} />
|
|
549
|
+
</EuiButtonGroup>
|
|
550
|
+
```
|
|
551
|
+
|
|
483
552
|
## Testing
|
|
484
553
|
|
|
485
554
|
### Running unit tests
|
package/lib/cjs/index.js
CHANGED
|
@@ -24,6 +24,7 @@ var _tooltip_no_interactive_content = require("./rules/a11y/tooltip_no_interacti
|
|
|
24
24
|
var _tooltip_button_icon_wrap = require("./rules/a11y/tooltip_button_icon_wrap");
|
|
25
25
|
var _no_deprecated_icon_aliases = require("./rules/no_deprecated_icon_aliases");
|
|
26
26
|
var _button_group_no_invalid_children = require("./rules/button_group_no_invalid_children");
|
|
27
|
+
var _button_group_selection_require_id = require("./rules/button_group_selection_require_id");
|
|
27
28
|
/*
|
|
28
29
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
29
30
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
@@ -57,7 +58,8 @@ const config = {
|
|
|
57
58
|
'require-href-for-link': _require_href_for_link.RequireHrefForLink,
|
|
58
59
|
'tooltip-no-interactive-content': _tooltip_no_interactive_content.TooltipNoInteractiveContent,
|
|
59
60
|
'tooltip-button-icon-wrap': _tooltip_button_icon_wrap.TooltipButtonIconWrap,
|
|
60
|
-
'button-group-no-invalid-children': _button_group_no_invalid_children.ButtonGroupNoInvalidChildren
|
|
61
|
+
'button-group-no-invalid-children': _button_group_no_invalid_children.ButtonGroupNoInvalidChildren,
|
|
62
|
+
'button-group-selection-require-id': _button_group_selection_require_id.ButtonGroupSelectionRequireId
|
|
61
63
|
},
|
|
62
64
|
configs: {
|
|
63
65
|
recommended: {
|
|
@@ -65,6 +67,7 @@ const config = {
|
|
|
65
67
|
rules: {
|
|
66
68
|
'@elastic/eui/accessible-interactive-element': 'warn',
|
|
67
69
|
'@elastic/eui/button-group-no-invalid-children': 'error',
|
|
70
|
+
'@elastic/eui/button-group-selection-require-id': 'error',
|
|
68
71
|
'@elastic/eui/callout-announce-on-mount': 'warn',
|
|
69
72
|
'@elastic/eui/callout-prefer-props-for-content': 'warn',
|
|
70
73
|
'@elastic/eui/consistent-is-invalid-props': 'warn',
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const SEGMENTED_VALID_BUTTONS: Set<string>;
|
|
4
|
-
export declare const ButtonGroupNoInvalidChildren: TSESLint.RuleModule<"invalidUnresolvableWrapperChild" | "invalidWrapperChild" | "invalidChild" | "invalidUnresolvableChild" | "invalidPopoverButton" | "invalidUnresolvablePopoverButton" | "invalidSegmentedMixedTypes", [], unknown, TSESLint.RuleListener>;
|
|
2
|
+
export declare const ButtonGroupNoInvalidChildren: TSESLint.RuleModule<"invalidUnresolvableWrapperChild" | "invalidWrapperChild" | "invalidChild" | "invalidUnresolvableChild" | "invalidPopoverButton" | "invalidUnresolvablePopoverButton" | "invalidMixedTypes", [], unknown, TSESLint.RuleListener>;
|
|
5
3
|
//# sourceMappingURL=button_group_no_invalid_children.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button_group_no_invalid_children.d.ts","sourceRoot":"","sources":["../../../src/rules/button_group_no_invalid_children.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"button_group_no_invalid_children.d.ts","sourceRoot":"","sources":["../../../src/rules/button_group_no_invalid_children.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAwDlC,eAAO,MAAM,4BAA4B,sPAsPxC,CAAC"}
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.ButtonGroupNoInvalidChildren = void 0;
|
|
7
7
|
var _utils = require("@typescript-eslint/utils");
|
|
8
8
|
var _has_spread = require("../utils/has_spread");
|
|
9
9
|
var _flat_map = require("../utils/flat_map");
|
|
10
10
|
var _get_element_name = require("../utils/get_element_name");
|
|
11
|
-
var _walk_jsx_children = require("../utils/walk_jsx_children");
|
|
12
11
|
var _get_attr_value = require("../utils/get_attr_value");
|
|
12
|
+
var _collect_jsx_children = require("../utils/collect_jsx_children");
|
|
13
|
+
var _button_group_constants = require("../utils/button_group_constants");
|
|
13
14
|
/*
|
|
14
15
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
15
16
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
@@ -18,27 +19,12 @@ var _get_attr_value = require("../utils/get_attr_value");
|
|
|
18
19
|
* Side Public License, v 1.
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
const VALID_BUTTONS = exports.VALID_BUTTONS = new Set(['EuiButton', 'EuiButtonEmpty', 'EuiButtonIcon']);
|
|
23
|
-
const SEGMENTED_VALID_BUTTONS = exports.SEGMENTED_VALID_BUTTONS = new Set(['EuiButton', 'EuiButtonIcon']);
|
|
24
|
-
const VALID_WRAPPERS = new Set(['EuiToolTip', 'EuiPopover', 'EuiCopy']);
|
|
25
|
-
const VALID_WRAPPERS_LIST = Array.from(VALID_WRAPPERS).join(', ');
|
|
22
|
+
const VALID_WRAPPERS_LIST = Array.from(_button_group_constants.VALID_WRAPPERS).join(', ');
|
|
26
23
|
function isCustomComponent(name) {
|
|
27
24
|
return name[0] === name[0].toUpperCase();
|
|
28
25
|
}
|
|
29
|
-
function collectJsxChildren(node, sourceCode) {
|
|
30
|
-
const results = [];
|
|
31
|
-
(0, _walk_jsx_children.walkJsxChildren)(node, leaf => {
|
|
32
|
-
if (leaf.type === 'JSXElement') {
|
|
33
|
-
results.push(leaf);
|
|
34
|
-
}
|
|
35
|
-
}, {
|
|
36
|
-
sourceCode
|
|
37
|
-
});
|
|
38
|
-
return results;
|
|
39
|
-
}
|
|
40
26
|
function reportInvalidWrapperChildren(wrapper, wrapperName, context, validButtons, allowed, seenButtonTypes) {
|
|
41
|
-
const children = (0, _flat_map.flatMap)(wrapper.children, c => collectJsxChildren(c, context.sourceCode));
|
|
27
|
+
const children = (0, _flat_map.flatMap)(wrapper.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
42
28
|
for (const wrapperChild of children) {
|
|
43
29
|
if ((0, _has_spread.hasSpread)(wrapperChild.openingElement.attributes)) continue;
|
|
44
30
|
const wrapperChildName = (0, _get_element_name.getElementName)(wrapperChild.openingElement);
|
|
@@ -49,7 +35,7 @@ function reportInvalidWrapperChildren(wrapper, wrapperName, context, validButton
|
|
|
49
35
|
}
|
|
50
36
|
context.report({
|
|
51
37
|
node: wrapperChild.openingElement,
|
|
52
|
-
messageId: isCustomComponent(wrapperChildName) && !VALID_BUTTONS.has(wrapperChildName) ? 'invalidUnresolvableWrapperChild' : 'invalidWrapperChild',
|
|
38
|
+
messageId: isCustomComponent(wrapperChildName) && !_button_group_constants.VALID_BUTTONS.has(wrapperChildName) ? 'invalidUnresolvableWrapperChild' : 'invalidWrapperChild',
|
|
53
39
|
data: {
|
|
54
40
|
name: wrapperChildName,
|
|
55
41
|
wrapper: wrapperName,
|
|
@@ -65,23 +51,24 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
65
51
|
const {
|
|
66
52
|
openingElement
|
|
67
53
|
} = node;
|
|
68
|
-
if (openingElement.name.type !== 'JSXIdentifier' || openingElement.name.name !== BUTTON_GROUP) {
|
|
54
|
+
if (openingElement.name.type !== 'JSXIdentifier' || openingElement.name.name !== _button_group_constants.BUTTON_GROUP) {
|
|
69
55
|
return;
|
|
70
56
|
}
|
|
71
57
|
|
|
72
|
-
// Validate "default" and "
|
|
58
|
+
// Validate "default", "segmented", and "selection" variants.
|
|
73
59
|
// Dynamic variant values (variables) are conservatively skipped.
|
|
74
60
|
const variant = (0, _get_attr_value.findAttrValue)(context, openingElement.attributes, 'variant');
|
|
75
|
-
if (variant !== undefined && variant !== 'default' && variant !== 'segmented') return;
|
|
61
|
+
if (variant !== undefined && variant !== 'default' && variant !== 'segmented' && variant !== 'selection') return;
|
|
76
62
|
const isSegmented = variant === 'segmented';
|
|
77
|
-
const
|
|
63
|
+
const isSelection = variant === 'selection';
|
|
64
|
+
const validButtons = isSegmented ? _button_group_constants.SEGMENTED_VALID_BUTTONS : isSelection ? _button_group_constants.SELECTION_VALID_BUTTONS : _button_group_constants.VALID_BUTTONS;
|
|
78
65
|
const allowed = Array.from(validButtons).join(', ');
|
|
79
|
-
const children = (0, _flat_map.flatMap)(node.children, c => collectJsxChildren(c, context.sourceCode));
|
|
66
|
+
const children = (0, _flat_map.flatMap)(node.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
80
67
|
if (children.length === 0) return;
|
|
81
68
|
|
|
82
|
-
// Collect seen button types for the segmented mixed-type check.
|
|
69
|
+
// Collect seen button types for the segmented/selection mixed-type check.
|
|
83
70
|
// Populated during the main loop to avoid a second traversal.
|
|
84
|
-
const seenButtonTypes = isSegmented ? new Set() : null;
|
|
71
|
+
const seenButtonTypes = isSegmented || isSelection ? new Set() : null;
|
|
85
72
|
for (const child of children) {
|
|
86
73
|
const name = (0, _get_element_name.getElementName)(child.openingElement);
|
|
87
74
|
if (name === null) continue;
|
|
@@ -89,7 +76,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
89
76
|
seenButtonTypes?.add(name);
|
|
90
77
|
continue;
|
|
91
78
|
}
|
|
92
|
-
if (VALID_WRAPPERS.has(name)) {
|
|
79
|
+
if (_button_group_constants.VALID_WRAPPERS.has(name)) {
|
|
93
80
|
if (name === 'EuiToolTip') {
|
|
94
81
|
// Validate JSX children (expanding fragments/conditionals).
|
|
95
82
|
// Also collects button types for the segmented mixed-type check.
|
|
@@ -101,7 +88,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
101
88
|
// EuiToolTip wrapping the trigger button is also supported.
|
|
102
89
|
const buttonProp = child.openingElement.attributes.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && attr.name.name === 'button');
|
|
103
90
|
if (buttonProp?.value != null) {
|
|
104
|
-
const triggerElements = collectJsxChildren(buttonProp.value, context.sourceCode);
|
|
91
|
+
const triggerElements = (0, _collect_jsx_children.collectJsxChildren)(buttonProp.value, context.sourceCode);
|
|
105
92
|
for (const triggerElement of triggerElements) {
|
|
106
93
|
if ((0, _has_spread.hasSpread)(triggerElement.openingElement.attributes)) continue;
|
|
107
94
|
const triggerElementName = (0, _get_element_name.getElementName)(triggerElement.openingElement);
|
|
@@ -112,7 +99,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
112
99
|
}
|
|
113
100
|
if (triggerElementName === 'EuiToolTip') {
|
|
114
101
|
// EuiToolTip wrapping the trigger — validate its children.
|
|
115
|
-
const tooltipChildren = (0, _flat_map.flatMap)(triggerElement.children, c => collectJsxChildren(c, context.sourceCode));
|
|
102
|
+
const tooltipChildren = (0, _flat_map.flatMap)(triggerElement.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
116
103
|
for (const tooltipChild of tooltipChildren) {
|
|
117
104
|
if ((0, _has_spread.hasSpread)(tooltipChild.openingElement.attributes)) continue;
|
|
118
105
|
const tooltipChildName = (0, _get_element_name.getElementName)(tooltipChild.openingElement);
|
|
@@ -123,7 +110,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
123
110
|
}
|
|
124
111
|
context.report({
|
|
125
112
|
node: tooltipChild.openingElement,
|
|
126
|
-
messageId: isCustomComponent(tooltipChildName) && !VALID_BUTTONS.has(tooltipChildName) ? 'invalidUnresolvablePopoverButton' : 'invalidPopoverButton',
|
|
113
|
+
messageId: isCustomComponent(tooltipChildName) && !_button_group_constants.VALID_BUTTONS.has(tooltipChildName) ? 'invalidUnresolvablePopoverButton' : 'invalidPopoverButton',
|
|
127
114
|
data: {
|
|
128
115
|
name: tooltipChildName,
|
|
129
116
|
allowed
|
|
@@ -134,7 +121,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
134
121
|
}
|
|
135
122
|
context.report({
|
|
136
123
|
node: triggerElement.openingElement,
|
|
137
|
-
messageId: isCustomComponent(triggerElementName) && !VALID_BUTTONS.has(triggerElementName) ? 'invalidUnresolvablePopoverButton' : 'invalidPopoverButton',
|
|
124
|
+
messageId: isCustomComponent(triggerElementName) && !_button_group_constants.VALID_BUTTONS.has(triggerElementName) ? 'invalidUnresolvablePopoverButton' : 'invalidPopoverButton',
|
|
138
125
|
data: {
|
|
139
126
|
name: triggerElementName,
|
|
140
127
|
allowed
|
|
@@ -157,7 +144,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
157
144
|
if ((0, _has_spread.hasSpread)(child.openingElement.attributes)) continue;
|
|
158
145
|
context.report({
|
|
159
146
|
node: child.openingElement,
|
|
160
|
-
messageId: isCustomComponent(name) && !VALID_BUTTONS.has(name) ? 'invalidUnresolvableChild' : 'invalidChild',
|
|
147
|
+
messageId: isCustomComponent(name) && !_button_group_constants.VALID_BUTTONS.has(name) ? 'invalidUnresolvableChild' : 'invalidChild',
|
|
161
148
|
data: {
|
|
162
149
|
name,
|
|
163
150
|
allowed
|
|
@@ -165,13 +152,16 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
165
152
|
});
|
|
166
153
|
}
|
|
167
154
|
|
|
168
|
-
// For segmented, all children must be the same button type —
|
|
169
|
-
// all EuiButton or all EuiButtonIcon. Types are collected during
|
|
170
|
-
// main loop above, including from EuiToolTip, EuiPopover, and EuiCopy.
|
|
155
|
+
// For segmented/selection, all children must be the same button type —
|
|
156
|
+
// either all EuiButton or all EuiButtonIcon. Types are collected during
|
|
157
|
+
// the main loop above, including from EuiToolTip, EuiPopover, and EuiCopy.
|
|
171
158
|
if (seenButtonTypes?.has('EuiButton') && seenButtonTypes.has('EuiButtonIcon')) {
|
|
172
159
|
context.report({
|
|
173
160
|
node: openingElement,
|
|
174
|
-
messageId: '
|
|
161
|
+
messageId: 'invalidMixedTypes',
|
|
162
|
+
data: {
|
|
163
|
+
variant: isSelection ? 'selection' : 'segmented'
|
|
164
|
+
}
|
|
175
165
|
});
|
|
176
166
|
}
|
|
177
167
|
}
|
|
@@ -190,7 +180,7 @@ const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _uti
|
|
|
190
180
|
invalidUnresolvablePopoverButton: [`{{ name }} cannot be verified as a valid trigger button for EuiPopover in EuiButtonGroup.`, `The \`button\` prop must be {{ allowed }}.`, `If {{ name }} is a shared button wrapper component only containing`, `valid button children, suppress this rule inline with a comment`, `explaining why it's valid.`, `// eslint-disable-next-line @elastic/eui/button-group-no-invalid-children -- SaveButton only wraps EuiButton`].join(' '),
|
|
191
181
|
invalidWrapperChild: [`{{ name }} inside {{ wrapper }} is not a valid button for EuiButtonGroup.`, `The wrapper must contain {{ allowed }}.`].join(' '),
|
|
192
182
|
invalidUnresolvableWrapperChild: [`{{ name }} inside {{ wrapper }} cannot be verified as a valid button for EuiButtonGroup.`, `The wrapper must contain {{ allowed }}.`, `If {{ name }} is a shared button wrapper component, suppress this rule inline`, `with a comment explaining why it renders valid button children:`, `// eslint-disable-next-line @elastic/eui/button-group-no-invalid-children -- SaveButton wraps EuiButton`].join(' '),
|
|
193
|
-
|
|
183
|
+
invalidMixedTypes: [`EuiButtonGroup with variant="{{ variant }}" must use a single button type throughout.`, `Use either all EuiButton or all EuiButtonIcon children — not both.`].join(' ')
|
|
194
184
|
}
|
|
195
185
|
},
|
|
196
186
|
defaultOptions: []
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button_group_selection_require_id.d.ts","sourceRoot":"","sources":["../../../src/rules/button_group_selection_require_id.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AA8BlC,eAAO,MAAM,6BAA6B,sEAiHtC,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ButtonGroupSelectionRequireId = void 0;
|
|
7
|
+
var _utils = require("@typescript-eslint/utils");
|
|
8
|
+
var _has_spread = require("../utils/has_spread");
|
|
9
|
+
var _flat_map = require("../utils/flat_map");
|
|
10
|
+
var _get_element_name = require("../utils/get_element_name");
|
|
11
|
+
var _get_attr_value = require("../utils/get_attr_value");
|
|
12
|
+
var _has_meaningful_attr = require("../utils/has_meaningful_attr");
|
|
13
|
+
var _collect_jsx_children = require("../utils/collect_jsx_children");
|
|
14
|
+
var _button_group_constants = require("../utils/button_group_constants");
|
|
15
|
+
/*
|
|
16
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
17
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
18
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
19
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
20
|
+
* Side Public License, v 1.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
function checkButtonForId(element, context) {
|
|
24
|
+
const name = (0, _get_element_name.getElementName)(element.openingElement);
|
|
25
|
+
if (name === null || !_button_group_constants.SELECTION_VALID_BUTTONS.has(name)) return;
|
|
26
|
+
const {
|
|
27
|
+
attributes
|
|
28
|
+
} = element.openingElement;
|
|
29
|
+
if ((0, _has_meaningful_attr.hasMeaningfulAttr)(element.openingElement, 'id')) return;
|
|
30
|
+
if ((0, _has_spread.hasSpread)(attributes)) return;
|
|
31
|
+
context.report({
|
|
32
|
+
node: element.openingElement,
|
|
33
|
+
messageId: 'missingId',
|
|
34
|
+
data: {
|
|
35
|
+
name
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const ButtonGroupSelectionRequireId = exports.ButtonGroupSelectionRequireId = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
40
|
+
create(context) {
|
|
41
|
+
return {
|
|
42
|
+
JSXElement(node) {
|
|
43
|
+
const {
|
|
44
|
+
openingElement
|
|
45
|
+
} = node;
|
|
46
|
+
if (openingElement.name.type !== 'JSXIdentifier' || openingElement.name.name !== _button_group_constants.BUTTON_GROUP) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Only validate variant="selection". Dynamic or other variants are skipped.
|
|
51
|
+
const variant = (0, _get_attr_value.findAttrValue)(context, openingElement.attributes, 'variant');
|
|
52
|
+
if (variant !== 'selection') return;
|
|
53
|
+
const children = (0, _flat_map.flatMap)(node.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
54
|
+
if (children.length === 0) return;
|
|
55
|
+
for (const child of children) {
|
|
56
|
+
const name = (0, _get_element_name.getElementName)(child.openingElement);
|
|
57
|
+
if (name === null) continue;
|
|
58
|
+
if (_button_group_constants.SELECTION_VALID_BUTTONS.has(name)) {
|
|
59
|
+
checkButtonForId(child, context);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (_button_group_constants.VALID_WRAPPERS.has(name)) {
|
|
63
|
+
if (name === 'EuiToolTip') {
|
|
64
|
+
const wrapperChildren = (0, _flat_map.flatMap)(child.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
65
|
+
for (const wrapperChild of wrapperChildren) {
|
|
66
|
+
checkButtonForId(wrapperChild, context);
|
|
67
|
+
}
|
|
68
|
+
} else if (name === 'EuiPopover') {
|
|
69
|
+
const buttonProp = child.openingElement.attributes.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && attr.name.name === 'button');
|
|
70
|
+
if (buttonProp?.value != null) {
|
|
71
|
+
const triggerElements = (0, _collect_jsx_children.collectJsxChildren)(buttonProp.value, context.sourceCode);
|
|
72
|
+
for (const triggerElement of triggerElements) {
|
|
73
|
+
const triggerName = (0, _get_element_name.getElementName)(triggerElement.openingElement);
|
|
74
|
+
if (triggerName === null) continue;
|
|
75
|
+
if (_button_group_constants.SELECTION_VALID_BUTTONS.has(triggerName)) {
|
|
76
|
+
checkButtonForId(triggerElement, context);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (triggerName === 'EuiToolTip') {
|
|
80
|
+
// EuiToolTip wrapping the popover trigger — check its children.
|
|
81
|
+
const tooltipChildren = (0, _flat_map.flatMap)(triggerElement.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
82
|
+
for (const tooltipChild of tooltipChildren) {
|
|
83
|
+
checkButtonForId(tooltipChild, context);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else if (name === 'EuiCopy') {
|
|
89
|
+
// Children is a render prop; walkJsxChildren expands it.
|
|
90
|
+
const wrapperChildren = (0, _flat_map.flatMap)(child.children, c => (0, _collect_jsx_children.collectJsxChildren)(c, context.sourceCode));
|
|
91
|
+
for (const wrapperChild of wrapperChildren) {
|
|
92
|
+
checkButtonForId(wrapperChild, context);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Non-button, non-wrapper children are skipped — the other rule handles those.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
meta: {
|
|
104
|
+
type: 'problem',
|
|
105
|
+
docs: {
|
|
106
|
+
description: 'Enforce that EuiButtonGroup children have an id prop when variant="selection"'
|
|
107
|
+
},
|
|
108
|
+
schema: [],
|
|
109
|
+
messages: {
|
|
110
|
+
missingId: [`{{ name }} inside EuiButtonGroup with variant="selection" is missing a required id prop.`, `Each button must have a unique id so the group can track selection state.`].join(' ')
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
defaultOptions: []
|
|
114
|
+
});
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
2
|
export declare const DEPRECATED_ICON_ALIASES: {
|
|
3
3
|
readonly alert: "warning";
|
|
4
|
+
readonly analyzeEvent: "cube";
|
|
4
5
|
readonly anomalyChart: "chartAnomaly";
|
|
6
|
+
readonly anomalySwimLane: "chartHeatmap";
|
|
7
|
+
readonly annotation: "flag";
|
|
8
|
+
readonly apps: "grid";
|
|
5
9
|
readonly apmTrace: "chartWaterfall";
|
|
6
10
|
readonly arrowDown: "chevronSingleDown";
|
|
7
11
|
readonly arrowEnd: "chevronLimitRight";
|
|
@@ -21,6 +25,9 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
21
25
|
readonly color: "paintBucket";
|
|
22
26
|
readonly compute: "processor";
|
|
23
27
|
readonly console: "commandLine";
|
|
28
|
+
readonly container: "package";
|
|
29
|
+
readonly continuityAbove: "upload";
|
|
30
|
+
readonly continuityWithin: "maximize";
|
|
24
31
|
readonly contrastHigh: "contrastFill";
|
|
25
32
|
readonly controlsHorizontal: "controls";
|
|
26
33
|
readonly controlsVertical: "controls";
|
|
@@ -67,6 +74,7 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
67
74
|
readonly editorUnorderedList: "listBullet";
|
|
68
75
|
readonly email: "mail";
|
|
69
76
|
readonly eql: "query";
|
|
77
|
+
readonly esqlVis: "query";
|
|
70
78
|
readonly errorFilled: "errorFill";
|
|
71
79
|
readonly exit: "logOut";
|
|
72
80
|
readonly expand: "maximize";
|
|
@@ -76,15 +84,23 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
76
84
|
readonly eyeClosed: "eyeSlash";
|
|
77
85
|
readonly fieldStatistics: "tableInfo";
|
|
78
86
|
readonly filterInCircle: "filter";
|
|
87
|
+
readonly fold: "minimize";
|
|
79
88
|
readonly folderCheck: "check";
|
|
89
|
+
readonly folderClose: "folder";
|
|
90
|
+
readonly folderClosed: "folder";
|
|
80
91
|
readonly folderOpened: "folderOpen";
|
|
92
|
+
readonly frameNext: "chevronSingleRight";
|
|
93
|
+
readonly framePrevious: "chevronSingleLeft";
|
|
81
94
|
readonly glasses: "readOnly";
|
|
95
|
+
readonly help: "question";
|
|
82
96
|
readonly grab: "dragVertical";
|
|
83
97
|
readonly grabHorizontal: "dragHorizontal";
|
|
84
98
|
readonly grabOmnidirectional: "drag";
|
|
85
99
|
readonly heatmap: "chartHeatmap";
|
|
86
100
|
readonly importAction: "download";
|
|
101
|
+
readonly index: "table";
|
|
87
102
|
readonly indexClose: "tableCross";
|
|
103
|
+
readonly ip: "tokenIP";
|
|
88
104
|
readonly indexEdit: "tablePencil";
|
|
89
105
|
readonly indexFlush: "chartThreshold";
|
|
90
106
|
readonly indexMapping: "mapping";
|
|
@@ -107,7 +123,10 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
107
123
|
readonly listAdd: "plusCircle";
|
|
108
124
|
readonly logPatternAnalysis: "pattern";
|
|
109
125
|
readonly logRateAnalysis: "chartBarVertical";
|
|
126
|
+
readonly logstashFilter: "filter";
|
|
110
127
|
readonly logstashIf: "if";
|
|
128
|
+
readonly logstashInput: "download";
|
|
129
|
+
readonly logstashOutput: "upload";
|
|
111
130
|
readonly logstashQueue: "queue";
|
|
112
131
|
readonly magnifyWithExclamation: "magnifyExclamation";
|
|
113
132
|
readonly magnifyWithMinus: "magnifyMinus";
|
|
@@ -124,6 +143,7 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
124
143
|
readonly offline: "wifiSlash";
|
|
125
144
|
readonly online: "wifi";
|
|
126
145
|
readonly pagesSelect: "documentsCheck";
|
|
146
|
+
readonly payment: "money";
|
|
127
147
|
readonly pinFilled: "pinFill";
|
|
128
148
|
readonly pipeBreaks: "lineBreak";
|
|
129
149
|
readonly pipeNoBreaks: "lineBreakSlash";
|
|
@@ -136,15 +156,27 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
136
156
|
readonly returnKey: "return";
|
|
137
157
|
readonly search: "magnify";
|
|
138
158
|
readonly securitySignal: "radar";
|
|
159
|
+
readonly sessionViewer: "commandLine";
|
|
160
|
+
readonly singleMetricViewer: "chartArea";
|
|
161
|
+
readonly spaces: "grid";
|
|
139
162
|
readonly starEmpty: "star";
|
|
163
|
+
readonly starEmptySpace: "star";
|
|
164
|
+
readonly starFillSpace: "starFill";
|
|
140
165
|
readonly starFilled: "starFill";
|
|
141
|
-
readonly starFilledSpace: "
|
|
142
|
-
readonly
|
|
143
|
-
readonly
|
|
166
|
+
readonly starFilledSpace: "starFill";
|
|
167
|
+
readonly starMinusEmpty: "star";
|
|
168
|
+
readonly starMinusFill: "starFill";
|
|
169
|
+
readonly starMinusFilled: "starFill";
|
|
170
|
+
readonly starPlusEmpty: "star";
|
|
171
|
+
readonly starPlusFill: "starFill";
|
|
172
|
+
readonly starPlusFilled: "starFill";
|
|
144
173
|
readonly stopFilled: "stopFill";
|
|
174
|
+
readonly stats: "chartLine";
|
|
145
175
|
readonly streamsClassic: "productStreamsClassic";
|
|
146
176
|
readonly streamsWired: "productStreamsWired";
|
|
177
|
+
readonly string: "tokenString";
|
|
147
178
|
readonly submodule: "merge";
|
|
179
|
+
readonly tableOfContents: "listBullet";
|
|
148
180
|
readonly tableDensityCompact: "tableDensityHigh";
|
|
149
181
|
readonly tableDensityExpanded: "tableDensityLow";
|
|
150
182
|
readonly tableDensityNormal: "table";
|
|
@@ -154,6 +186,7 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
154
186
|
readonly timeslider: "clockControl";
|
|
155
187
|
readonly tokenDenseVector: "tokenVectorDense";
|
|
156
188
|
readonly training: "presentation";
|
|
189
|
+
readonly unfold: "maximize";
|
|
157
190
|
readonly unlink: "linkSlash";
|
|
158
191
|
readonly userAvatar: "user";
|
|
159
192
|
readonly vector: "vectorSquare";
|
|
@@ -164,6 +197,7 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
164
197
|
readonly visBarVertical: "chartBarVertical";
|
|
165
198
|
readonly visBarVerticalStacked: "chartBarVerticalStack";
|
|
166
199
|
readonly visGauge: "chartGauge";
|
|
200
|
+
readonly visGoal: "chartGauge";
|
|
167
201
|
readonly visLine: "chartLine";
|
|
168
202
|
readonly visMapCoordinate: "waypoint";
|
|
169
203
|
readonly visMapRegion: "map";
|
|
@@ -176,7 +210,14 @@ export declare const DEPRECATED_ICON_ALIASES: {
|
|
|
176
210
|
readonly visVega: "code";
|
|
177
211
|
readonly visVisualBuilder: "productTSVB";
|
|
178
212
|
readonly warningFilled: "warningFill";
|
|
213
|
+
readonly wordWrap: "lineBreak";
|
|
214
|
+
readonly wordWrapDisabled: "lineBreakSlash";
|
|
179
215
|
};
|
|
180
216
|
export declare const DEPRECATED_ICONS_WITHOUT_REPLACEMENT: ReadonlySet<string>;
|
|
181
|
-
export declare const
|
|
217
|
+
export declare const DEPRECATED_ICONS_WITH_GUIDANCE: {
|
|
218
|
+
readonly folderExclamation: "Use `linkSlash` for unlink actions or `hourglass` for Kibana Cases status.";
|
|
219
|
+
readonly stopFill: "Use `EuiColorPickerSwatch` for a color chip or `stop` otherwise.";
|
|
220
|
+
readonly stopSlash: "Use `EuiColorPickerSwatch` for no/transparent color or `stop` otherwise.";
|
|
221
|
+
};
|
|
222
|
+
export declare const NoDeprecatedIconAliases: ESLintUtils.RuleModule<"deprecatedIcon" | "deprecatedIconAlias" | "deprecatedIconGuidance", [], unknown, ESLintUtils.RuleListener>;
|
|
182
223
|
//# sourceMappingURL=no_deprecated_icon_aliases.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no_deprecated_icon_aliases.d.ts","sourceRoot":"","sources":["../../../src/rules/no_deprecated_icon_aliases.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AAEjE,eAAO,MAAM,uBAAuB
|
|
1
|
+
{"version":3,"file":"no_deprecated_icon_aliases.d.ts","sourceRoot":"","sources":["../../../src/rules/no_deprecated_icon_aliases.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AAEjE,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqN1B,CAAC;AAEX,eAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC,MAAM,CAQjE,CAAC;AAEL,eAAO,MAAM,8BAA8B;;;;CAOjC,CAAC;AA+EX,eAAO,MAAM,uBAAuB,oIAuHlC,CAAC"}
|