@elastic/eslint-plugin-eui 2.15.0 → 2.16.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 +125 -0
- package/lib/cjs/index.js +10 -1
- package/lib/cjs/rules/a11y/no_nested_copy_tooltip.d.ts +3 -0
- package/lib/cjs/rules/a11y/no_nested_copy_tooltip.d.ts.map +1 -0
- package/lib/cjs/rules/a11y/no_nested_copy_tooltip.js +84 -0
- package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.d.ts.map +1 -1
- package/lib/cjs/rules/a11y/tooltip_button_icon_wrap.js +29 -1
- package/lib/cjs/rules/a11y/tooltip_no_interactive_content.d.ts.map +1 -1
- package/lib/cjs/rules/a11y/tooltip_no_interactive_content.js +17 -45
- package/lib/cjs/rules/button_group_no_invalid_children.d.ts +3 -0
- package/lib/cjs/rules/button_group_no_invalid_children.d.ts.map +1 -0
- package/lib/cjs/rules/button_group_no_invalid_children.js +159 -0
- package/lib/cjs/rules/callout_prefer_props_for_content.d.ts.map +1 -1
- package/lib/cjs/rules/callout_prefer_props_for_content.js +93 -156
- package/lib/cjs/rules/no_deprecated_icon_aliases.d.ts +174 -0
- package/lib/cjs/rules/no_deprecated_icon_aliases.d.ts.map +1 -0
- package/lib/cjs/rules/no_deprecated_icon_aliases.js +302 -0
- package/lib/cjs/utils/flat_map.d.ts +2 -0
- package/lib/cjs/utils/flat_map.d.ts.map +1 -0
- package/lib/cjs/utils/flat_map.js +24 -0
- package/lib/cjs/utils/has_meaningful_attr.d.ts +20 -0
- package/lib/cjs/utils/has_meaningful_attr.d.ts.map +1 -0
- package/lib/cjs/utils/has_meaningful_attr.js +61 -0
- package/lib/cjs/utils/is_fragment.d.ts +8 -0
- package/lib/cjs/utils/is_fragment.d.ts.map +1 -0
- package/lib/cjs/utils/is_fragment.js +26 -0
- package/lib/cjs/utils/walk_jsx_children.d.ts +30 -0
- package/lib/cjs/utils/walk_jsx_children.d.ts.map +1 -0
- package/lib/cjs/utils/walk_jsx_children.js +210 -0
- package/lib/esm/index.js +9 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/rules/a11y/no_nested_copy_tooltip.d.ts +2 -0
- package/lib/esm/rules/a11y/no_nested_copy_tooltip.js +84 -0
- package/lib/esm/rules/a11y/no_nested_copy_tooltip.js.map +1 -0
- package/lib/esm/rules/a11y/tooltip_button_icon_wrap.js +31 -0
- package/lib/esm/rules/a11y/tooltip_button_icon_wrap.js.map +1 -1
- package/lib/esm/rules/a11y/tooltip_no_interactive_content.js +19 -42
- package/lib/esm/rules/a11y/tooltip_no_interactive_content.js.map +1 -1
- package/lib/esm/rules/button_group_no_invalid_children.d.ts +2 -0
- package/lib/esm/rules/button_group_no_invalid_children.js +198 -0
- package/lib/esm/rules/button_group_no_invalid_children.js.map +1 -0
- package/lib/esm/rules/callout_prefer_props_for_content.js +75 -112
- package/lib/esm/rules/callout_prefer_props_for_content.js.map +1 -1
- package/lib/esm/rules/no_deprecated_icon_aliases.d.ts +173 -0
- package/lib/esm/rules/no_deprecated_icon_aliases.js +307 -0
- package/lib/esm/rules/no_deprecated_icon_aliases.js.map +1 -0
- package/lib/esm/utils/flat_map.d.ts +1 -0
- package/lib/esm/utils/flat_map.js +22 -0
- package/lib/esm/utils/flat_map.js.map +1 -0
- package/lib/esm/utils/has_meaningful_attr.d.ts +19 -0
- package/lib/esm/utils/has_meaningful_attr.js +55 -0
- package/lib/esm/utils/has_meaningful_attr.js.map +1 -0
- package/lib/esm/utils/is_fragment.d.ts +7 -0
- package/lib/esm/utils/is_fragment.js +26 -0
- package/lib/esm/utils/is_fragment.js.map +1 -0
- package/lib/esm/utils/walk_jsx_children.d.ts +29 -0
- package/lib/esm/utils/walk_jsx_children.js +213 -0
- package/lib/esm/utils/walk_jsx_children.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,16 @@ We don't use `no-restricted-imports` because ESLint doesn't allow multiple error
|
|
|
29
29
|
|
|
30
30
|
All deprecations still must follow our [deprecation process](../../wiki/eui-team-processes/deprecations.md).
|
|
31
31
|
|
|
32
|
+
### `@elastic/eui/no-deprecated-icon-aliases`
|
|
33
|
+
|
|
34
|
+
Disallows deprecated EUI icon aliases in static JSX props on components imported
|
|
35
|
+
from `@elastic/eui`. The rule checks props ending in `IconType`, the `type` prop
|
|
36
|
+
on `EuiIcon` and `EuiIconTip`, and component-specific icon props such as
|
|
37
|
+
`timelineAvatar`, `icon`, `logo`, `iconLeft`, and `iconRight`. It automatically
|
|
38
|
+
replaces deprecated aliases with their supported icon handles.
|
|
39
|
+
|
|
40
|
+
See the full table of icon replacements here: https://github.com/elastic/eui/issues/9561
|
|
41
|
+
|
|
32
42
|
### `@elastic/eui/no-css-color`
|
|
33
43
|
|
|
34
44
|
This rule warns engineers to not use literal css color in the codebase, particularly for CSS properties that apply color to either the html element or text nodes, but rather urge users to defer to using the color tokens provided by EUI.
|
|
@@ -256,6 +266,8 @@ The rule reports two situations:
|
|
|
256
266
|
|
|
257
267
|
Buttons with spread props (`{...props}`) are intentionally skipped when no `title` prop is explicitly present because their final prop set cannot be statically determined.
|
|
258
268
|
|
|
269
|
+
**Exception**: an `EuiButtonIcon` rendered as the render-prop child of an `EuiCopy` that sets a non-empty `beforeMessage` is not flagged for a missing wrapper. `EuiCopy` wraps its child in an `EuiToolTip` using `beforeMessage` as the content, so adding another `EuiToolTip` would create nested, conflicting tooltips — see `@elastic/eui/no-nested-copy-tooltip`. This exception is conditional: because `EuiToolTip` suppresses itself when its content is empty, an `EuiButtonIcon` inside an `EuiCopy` with a missing or statically empty/falsy `beforeMessage` (e.g. `""`, `{null}`, `{undefined}`, `{false}`) has no visible tooltip and is still reported. A button passed through the `beforeMessage` prop itself (rather than the render-prop child) is also still reported, since it is not the element `EuiCopy`'s tooltip wraps. A `title` prop is always reported in this case, since browser-native tooltips remain discouraged.
|
|
270
|
+
|
|
259
271
|
#### Examples
|
|
260
272
|
|
|
261
273
|
```tsx
|
|
@@ -266,6 +278,11 @@ Buttons with spread props (`{...props}`) are intentionally skipped when no `titl
|
|
|
266
278
|
<EuiToolTip content="Edit item">
|
|
267
279
|
<EuiButtonIcon aria-label="Edit item" iconType="pencil" />
|
|
268
280
|
</EuiToolTip>
|
|
281
|
+
|
|
282
|
+
// ✓ Good - EuiCopy provides the tooltip via `beforeMessage`, no wrapper needed
|
|
283
|
+
<EuiCopy textToCopy="some text" beforeMessage="Copy me">
|
|
284
|
+
{(copy) => <EuiButtonIcon onClick={copy} aria-label="Copy" iconType="copy" />}
|
|
285
|
+
</EuiCopy>
|
|
269
286
|
```
|
|
270
287
|
|
|
271
288
|
```tsx
|
|
@@ -328,6 +345,114 @@ it('shows tooltip on focus', async () => {
|
|
|
328
345
|
```
|
|
329
346
|
|
|
330
347
|
|
|
348
|
+
### `@elastic/eui/no-nested-copy-tooltip`
|
|
349
|
+
|
|
350
|
+
Disallow wrapping the `EuiCopy` render-prop child in an `EuiToolTip` when the `beforeMessage` prop is set.
|
|
351
|
+
|
|
352
|
+
`EuiCopy` already wraps its child in an `EuiToolTip` internally and uses `beforeMessage` as that tooltip's content. If the render-prop returns its own `EuiToolTip` as the first child, the result is a nested, conflicting tooltip. Remove the `EuiToolTip` wrapper and move its message into the `beforeMessage` prop so `EuiCopy`'s built-in tooltip is the single source of truth.
|
|
353
|
+
|
|
354
|
+
This rule reports the pattern but does not autofix it, because the tooltip's `content` and the existing `beforeMessage` value cannot always be merged safely (e.g. when either is a variable or JSX). Apply the fix manually.
|
|
355
|
+
|
|
356
|
+
#### Examples
|
|
357
|
+
|
|
358
|
+
```tsx
|
|
359
|
+
// ✗ Bad - nested tooltips, the child EuiToolTip conflicts with `beforeMessage`
|
|
360
|
+
<EuiCopy textToCopy="some text" beforeMessage="Click to copy">
|
|
361
|
+
{(copy) => (
|
|
362
|
+
<EuiToolTip content="Copy me">
|
|
363
|
+
<EuiButton onClick={copy}>Copy</EuiButton>
|
|
364
|
+
</EuiToolTip>
|
|
365
|
+
)}
|
|
366
|
+
</EuiCopy>
|
|
367
|
+
|
|
368
|
+
// ✓ Good - drop the EuiToolTip wrapper and let `beforeMessage` drive the tooltip
|
|
369
|
+
<EuiCopy textToCopy="some text" beforeMessage="Copy me">
|
|
370
|
+
{(copy) => <EuiButton onClick={copy}>Copy</EuiButton>}
|
|
371
|
+
</EuiCopy>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
When the render-prop child is not an `EuiToolTip`, `beforeMessage` simply configures `EuiCopy`'s own tooltip and the pattern is left untouched.
|
|
375
|
+
|
|
376
|
+
### `@elastic/eui/button-group-no-invalid-children`
|
|
377
|
+
|
|
378
|
+
Enforce that `EuiButtonGroup` children (when using the Children API) are valid button components.
|
|
379
|
+
|
|
380
|
+
Valid direct children are:
|
|
381
|
+
- `variant="default"`: `EuiButton`, `EuiButtonEmpty`, and `EuiButtonIcon`
|
|
382
|
+
|
|
383
|
+
Besides those button components, these three wrapper components are also allowed: `EuiPopover`, `EuiToolTip` and `EuiCopy`.
|
|
384
|
+
|
|
385
|
+
#### Examples
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
// ✗ Bad - non-button elements
|
|
389
|
+
<EuiButtonGroup legend="Actions">
|
|
390
|
+
<div>Not a button</div>
|
|
391
|
+
<EuiFlexGroup>...</EuiFlexGroup>
|
|
392
|
+
</EuiButtonGroup>
|
|
393
|
+
|
|
394
|
+
// ✓ Good - direct buttons
|
|
395
|
+
<EuiButtonGroup legend="Actions">
|
|
396
|
+
<EuiButton>Save</EuiButton>
|
|
397
|
+
<EuiButtonEmpty color="text">Cancel</EuiButtonEmpty>
|
|
398
|
+
</EuiButtonGroup>
|
|
399
|
+
|
|
400
|
+
// ✓ Good - icon button with tooltip
|
|
401
|
+
<EuiButtonGroup legend="Actions">
|
|
402
|
+
<EuiButton>Save</EuiButton>
|
|
403
|
+
<EuiToolTip content="Delete">
|
|
404
|
+
<EuiButtonIcon iconType="trash" aria-label="Delete" />
|
|
405
|
+
</EuiToolTip>
|
|
406
|
+
</EuiButtonGroup>
|
|
407
|
+
|
|
408
|
+
// ✓ Good - EuiCopy with render prop (expression or block body)
|
|
409
|
+
<EuiButtonGroup legend="Actions">
|
|
410
|
+
<EuiCopy textToCopy="text">
|
|
411
|
+
{(copy) => <EuiButton onClick={copy}>Copy</EuiButton>}
|
|
412
|
+
</EuiCopy>
|
|
413
|
+
</EuiButtonGroup>
|
|
414
|
+
|
|
415
|
+
// ✓ Good - .map() with expression or block body
|
|
416
|
+
<EuiButtonGroup legend="Actions">
|
|
417
|
+
{buttons.map((b) => <EuiButton key={b.id} onClick={b.onClick}>{b.label}</EuiButton>)}
|
|
418
|
+
</EuiButtonGroup>
|
|
419
|
+
|
|
420
|
+
// ✓ Good - EuiPopover with a button trigger
|
|
421
|
+
<EuiButtonGroup legend="Actions">
|
|
422
|
+
<EuiPopover
|
|
423
|
+
button={<EuiButton onClick={togglePopover}>More</EuiButton>}
|
|
424
|
+
isOpen={isOpen}
|
|
425
|
+
closePopover={closePopover}
|
|
426
|
+
>
|
|
427
|
+
Panel content
|
|
428
|
+
</EuiPopover>
|
|
429
|
+
</EuiButtonGroup>
|
|
430
|
+
|
|
431
|
+
// ✓ Good - EuiPopover with an EuiToolTip-wrapped icon trigger
|
|
432
|
+
<EuiButtonGroup legend="Actions">
|
|
433
|
+
<EuiPopover
|
|
434
|
+
button={
|
|
435
|
+
<EuiToolTip content="More options">
|
|
436
|
+
<EuiButtonIcon iconType="boxesVertical" aria-label="More options" />
|
|
437
|
+
</EuiToolTip>
|
|
438
|
+
}
|
|
439
|
+
isOpen={isOpen}
|
|
440
|
+
closePopover={closePopover}
|
|
441
|
+
>
|
|
442
|
+
Panel content
|
|
443
|
+
</EuiPopover>
|
|
444
|
+
</EuiButtonGroup>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### Custom button wrapper components
|
|
448
|
+
|
|
449
|
+
If a project-specific button component (e.g. `<SaveButton />`) is used as a child and the rule cannot resolve it statically, it reports `invalidUnresolvableChild` which suggests suppressing the rule inline with a comment:
|
|
450
|
+
|
|
451
|
+
```tsx
|
|
452
|
+
// eslint-disable-next-line @elastic/eui/button-group-no-invalid-children -- SaveButton returns EuiButton
|
|
453
|
+
<SaveButton />
|
|
454
|
+
```
|
|
455
|
+
|
|
331
456
|
## Testing
|
|
332
457
|
|
|
333
458
|
### Running unit tests
|
package/lib/cjs/index.js
CHANGED
|
@@ -7,6 +7,7 @@ var _consistent_is_invalid_props = require("./rules/a11y/consistent_is_invalid_p
|
|
|
7
7
|
var _href_or_on_click = require("./rules/href_or_on_click");
|
|
8
8
|
var _require_href_for_link = require("./rules/require_href_for_link");
|
|
9
9
|
var _no_css_color = require("./rules/no_css_color");
|
|
10
|
+
var _no_nested_copy_tooltip = require("./rules/a11y/no_nested_copy_tooltip");
|
|
10
11
|
var _no_restricted_eui_imports = require("./rules/no_restricted_eui_imports");
|
|
11
12
|
var _no_static_z_index = require("./rules/no_static_z_index");
|
|
12
13
|
var _no_unnamed_interactive_element = require("./rules/a11y/no_unnamed_interactive_element");
|
|
@@ -21,6 +22,8 @@ var _badge_accessibility_rules = require("./rules/a11y/badge_accessibility_rules
|
|
|
21
22
|
var _icon_accessibility_rules = require("./rules/a11y/icon_accessibility_rules");
|
|
22
23
|
var _tooltip_no_interactive_content = require("./rules/a11y/tooltip_no_interactive_content");
|
|
23
24
|
var _tooltip_button_icon_wrap = require("./rules/a11y/tooltip_button_icon_wrap");
|
|
25
|
+
var _no_deprecated_icon_aliases = require("./rules/no_deprecated_icon_aliases");
|
|
26
|
+
var _button_group_no_invalid_children = require("./rules/button_group_no_invalid_children");
|
|
24
27
|
/*
|
|
25
28
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
26
29
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
@@ -37,6 +40,8 @@ const config = {
|
|
|
37
40
|
'consistent-is-invalid-props': _consistent_is_invalid_props.ConsistentIsInvalidProps,
|
|
38
41
|
'href-or-on-click': _href_or_on_click.HrefOnClick,
|
|
39
42
|
'no-css-color': _no_css_color.NoCssColor,
|
|
43
|
+
'no-deprecated-icon-aliases': _no_deprecated_icon_aliases.NoDeprecatedIconAliases,
|
|
44
|
+
'no-nested-copy-tooltip': _no_nested_copy_tooltip.NoNestedCopyTooltip,
|
|
40
45
|
'no-restricted-eui-imports': _no_restricted_eui_imports.NoRestrictedEuiImports,
|
|
41
46
|
'no-static-z-index': _no_static_z_index.NoStaticZIndex,
|
|
42
47
|
'no-unnamed-interactive-element': _no_unnamed_interactive_element.NoUnnamedInteractiveElement,
|
|
@@ -51,18 +56,22 @@ const config = {
|
|
|
51
56
|
'icon-accessibility-rules': _icon_accessibility_rules.EuiIconAccessibilityRules,
|
|
52
57
|
'require-href-for-link': _require_href_for_link.RequireHrefForLink,
|
|
53
58
|
'tooltip-no-interactive-content': _tooltip_no_interactive_content.TooltipNoInteractiveContent,
|
|
54
|
-
'tooltip-button-icon-wrap': _tooltip_button_icon_wrap.TooltipButtonIconWrap
|
|
59
|
+
'tooltip-button-icon-wrap': _tooltip_button_icon_wrap.TooltipButtonIconWrap,
|
|
60
|
+
'button-group-no-invalid-children': _button_group_no_invalid_children.ButtonGroupNoInvalidChildren
|
|
55
61
|
},
|
|
56
62
|
configs: {
|
|
57
63
|
recommended: {
|
|
58
64
|
plugins: ['@elastic/eslint-plugin-eui'],
|
|
59
65
|
rules: {
|
|
60
66
|
'@elastic/eui/accessible-interactive-element': 'warn',
|
|
67
|
+
'@elastic/eui/button-group-no-invalid-children': 'error',
|
|
61
68
|
'@elastic/eui/callout-announce-on-mount': 'warn',
|
|
62
69
|
'@elastic/eui/callout-prefer-props-for-content': 'warn',
|
|
63
70
|
'@elastic/eui/consistent-is-invalid-props': 'warn',
|
|
64
71
|
'@elastic/eui/href-or-on-click': 'warn',
|
|
65
72
|
'@elastic/eui/no-css-color': 'warn',
|
|
73
|
+
'@elastic/eui/no-deprecated-icon-aliases': 'warn',
|
|
74
|
+
'@elastic/eui/no-nested-copy-tooltip': 'warn',
|
|
66
75
|
'@elastic/eui/no-restricted-eui-imports': 'warn',
|
|
67
76
|
'@elastic/eui/no-static-z-index': 'warn',
|
|
68
77
|
'@elastic/eui/no-unnamed-interactive-element': 'warn',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no_nested_copy_tooltip.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/no_nested_copy_tooltip.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAsDtE,eAAO,MAAM,mBAAmB,0FA6C/B,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NoNestedCopyTooltip = void 0;
|
|
7
|
+
var _utils = require("@typescript-eslint/utils");
|
|
8
|
+
var _has_meaningful_attr = require("../../utils/has_meaningful_attr");
|
|
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 COPY = 'EuiCopy';
|
|
18
|
+
const TOOLTIP = 'EuiToolTip';
|
|
19
|
+
const BEFORE_MESSAGE = 'beforeMessage';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the JSX element returned by the `EuiCopy` render-prop child.
|
|
23
|
+
*
|
|
24
|
+
* `EuiCopy` renders its children through a function (`children(copy)`), so the
|
|
25
|
+
* "first child" is the root JSX element that function returns. This handles
|
|
26
|
+
* both implicit arrow returns (`(copy) => <El />`) and block bodies with an
|
|
27
|
+
* explicit `return` statement. Non-render-prop children (e.g. a leading JSX
|
|
28
|
+
* comment) are skipped so the render-prop function is still found.
|
|
29
|
+
*/
|
|
30
|
+
function getRenderPropRootElement(node) {
|
|
31
|
+
const renderProp = node.children.find(child => child.type === 'JSXExpressionContainer' && (child.expression.type === 'ArrowFunctionExpression' || child.expression.type === 'FunctionExpression'));
|
|
32
|
+
if (!renderProp) return null;
|
|
33
|
+
const expression = renderProp.expression;
|
|
34
|
+
let {
|
|
35
|
+
body
|
|
36
|
+
} = expression;
|
|
37
|
+
if (body.type === 'BlockStatement') {
|
|
38
|
+
const returnStatement = body.body.find(statement => statement.type === 'ReturnStatement');
|
|
39
|
+
if (!returnStatement?.argument) return null;
|
|
40
|
+
body = returnStatement.argument;
|
|
41
|
+
}
|
|
42
|
+
return body.type === 'JSXElement' ? body : null;
|
|
43
|
+
}
|
|
44
|
+
function isToolTipElement(element) {
|
|
45
|
+
const {
|
|
46
|
+
name
|
|
47
|
+
} = element.openingElement;
|
|
48
|
+
return name.type === 'JSXIdentifier' && name.name === TOOLTIP;
|
|
49
|
+
}
|
|
50
|
+
const NoNestedCopyTooltip = exports.NoNestedCopyTooltip = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
51
|
+
create(context) {
|
|
52
|
+
return {
|
|
53
|
+
JSXElement(node) {
|
|
54
|
+
const {
|
|
55
|
+
openingElement
|
|
56
|
+
} = node;
|
|
57
|
+
if (openingElement.name.type !== 'JSXIdentifier' || openingElement.name.name !== COPY) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// A statically empty/falsy `beforeMessage` renders no `EuiCopy`
|
|
62
|
+
// tooltip, so a child `EuiToolTip` is not redundant — skip those.
|
|
63
|
+
if (!(0, _has_meaningful_attr.hasMeaningfulAttr)(openingElement, BEFORE_MESSAGE)) return;
|
|
64
|
+
const rootElement = getRenderPropRootElement(node);
|
|
65
|
+
if (!rootElement || !isToolTipElement(rootElement)) return;
|
|
66
|
+
context.report({
|
|
67
|
+
node: rootElement,
|
|
68
|
+
messageId: 'redundantTooltipWrapper'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
meta: {
|
|
74
|
+
type: 'problem',
|
|
75
|
+
docs: {
|
|
76
|
+
description: `Disallow wrapping the ${COPY} render-prop child in an ${TOOLTIP} when \`${BEFORE_MESSAGE}\` is set`
|
|
77
|
+
},
|
|
78
|
+
schema: [],
|
|
79
|
+
messages: {
|
|
80
|
+
redundantTooltipWrapper: [`${COPY} already wraps its child in an ${TOOLTIP} and uses \`${BEFORE_MESSAGE}\` as that tooltip's content.`, `Wrapping the child in another ${TOOLTIP} creates a nested, conflicting tooltip.`, `Remove the ${TOOLTIP} wrapper and move its message into the \`${BEFORE_MESSAGE}\` prop instead.`].join(' ')
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
defaultOptions: []
|
|
84
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip_button_icon_wrap.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/tooltip_button_icon_wrap.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"tooltip_button_icon_wrap.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/tooltip_button_icon_wrap.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AA0EtE,eAAO,MAAM,qBAAqB,qHAkEhC,CAAC"}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.TooltipButtonIconWrap = void 0;
|
|
7
7
|
var _utils = require("@typescript-eslint/utils");
|
|
8
8
|
var _has_spread = require("../../utils/has_spread");
|
|
9
|
+
var _has_meaningful_attr = require("../../utils/has_meaningful_attr");
|
|
9
10
|
/*
|
|
10
11
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
11
12
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
@@ -16,6 +17,8 @@ var _has_spread = require("../../utils/has_spread");
|
|
|
16
17
|
|
|
17
18
|
const BUTTON_ICON = 'EuiButtonIcon';
|
|
18
19
|
const TOOLTIP = 'EuiToolTip';
|
|
20
|
+
const COPY = 'EuiCopy';
|
|
21
|
+
const BEFORE_MESSAGE = 'beforeMessage';
|
|
19
22
|
function isWrappedByTooltip(node) {
|
|
20
23
|
let current = node.parent;
|
|
21
24
|
while (current) {
|
|
@@ -41,6 +44,31 @@ function isWrappedByTooltip(node) {
|
|
|
41
44
|
}
|
|
42
45
|
return false;
|
|
43
46
|
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* `EuiCopy` renders its render-prop child inside its own `EuiToolTip`, but only
|
|
50
|
+
* when `beforeMessage` is set — that prop is the tooltip's `content`, and
|
|
51
|
+
* `EuiToolTip` suppresses itself when the content is empty. So an
|
|
52
|
+
* `EuiButtonIcon` rendered within an `<EuiCopy beforeMessage="…">` already has a
|
|
53
|
+
* tooltip for sighted users and wrapping it in another `EuiToolTip` would create
|
|
54
|
+
* nested, conflicting tooltips — see the `no-nested-copy-tooltip` rule. Without
|
|
55
|
+
* a meaningful `beforeMessage` there is no such tooltip, so the button must
|
|
56
|
+
* still be wrapped.
|
|
57
|
+
*
|
|
58
|
+
* The exemption only applies to buttons in the render-prop output. If the walk
|
|
59
|
+
* crosses a `JSXAttribute` before reaching the `EuiCopy`, the button lives in a
|
|
60
|
+
* prop value (e.g. `beforeMessage={<EuiButtonIcon />}`) rather than the child
|
|
61
|
+
* `EuiCopy` renders, so it is not covered by the built-in tooltip.
|
|
62
|
+
*/
|
|
63
|
+
function isInsideEuiCopyWithBeforeMessage(node) {
|
|
64
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
65
|
+
if (current.type === 'JSXAttribute') return false;
|
|
66
|
+
if (current.type === 'JSXElement' && current.openingElement.name.type === 'JSXIdentifier' && current.openingElement.name.name === COPY) {
|
|
67
|
+
return (0, _has_meaningful_attr.hasMeaningfulAttr)(current.openingElement, BEFORE_MESSAGE);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
44
72
|
const TooltipButtonIconWrap = exports.TooltipButtonIconWrap = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
45
73
|
create(context) {
|
|
46
74
|
return {
|
|
@@ -63,7 +91,7 @@ const TooltipButtonIconWrap = exports.TooltipButtonIconWrap = _utils.ESLintUtils
|
|
|
63
91
|
});
|
|
64
92
|
return;
|
|
65
93
|
}
|
|
66
|
-
if (!isWrappedByTooltip(node) && !(0, _has_spread.hasSpread)(openingElement.attributes)) {
|
|
94
|
+
if (!isWrappedByTooltip(node) && !isInsideEuiCopyWithBeforeMessage(node) && !(0, _has_spread.hasSpread)(openingElement.attributes)) {
|
|
67
95
|
context.report({
|
|
68
96
|
node: openingElement,
|
|
69
97
|
messageId: 'wrapWithEuiToolTip'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip_no_interactive_content.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/tooltip_no_interactive_content.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"tooltip_no_interactive_content.d.ts","sourceRoot":"","sources":["../../../../src/rules/a11y/tooltip_no_interactive_content.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AA4BtE,eAAO,MAAM,2BAA2B,uFAgFtC,CAAC"}
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.TooltipNoInteractiveContent = void 0;
|
|
7
7
|
var _utils = require("@typescript-eslint/utils");
|
|
8
8
|
var _constants = require("../../utils/constants");
|
|
9
|
+
var _walk_jsx_children = require("../../utils/walk_jsx_children");
|
|
10
|
+
var _get_element_name = require("../../utils/get_element_name");
|
|
9
11
|
/*
|
|
10
12
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
11
13
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
@@ -19,46 +21,6 @@ const TOOLTIP_CONTENT_PROPS = ['content', 'title'];
|
|
|
19
21
|
const INTERACTIVE_HTML_ELEMENTS = ['a', 'button', 'input', 'select', 'textarea'];
|
|
20
22
|
const CONDITIONALLY_INTERACTIVE_SET = new Set(_constants.CONDITIONALLY_INTERACTIVE_EUI_COMPONENTS);
|
|
21
23
|
const INTERACTIVE_ELEMENTS = new Set([...INTERACTIVE_HTML_ELEMENTS, ..._constants.INTERACTIVE_EUI_COMPONENTS.filter(c => !CONDITIONALLY_INTERACTIVE_SET.has(c))]);
|
|
22
|
-
function findInteractiveElement(node) {
|
|
23
|
-
if (node.type === 'JSXElement') {
|
|
24
|
-
const el = node;
|
|
25
|
-
const {
|
|
26
|
-
name
|
|
27
|
-
} = el.openingElement;
|
|
28
|
-
if (name.type === 'JSXIdentifier' && INTERACTIVE_ELEMENTS.has(name.name)) {
|
|
29
|
-
return el.openingElement;
|
|
30
|
-
}
|
|
31
|
-
for (const child of el.children) {
|
|
32
|
-
const found = findInteractiveElement(child);
|
|
33
|
-
if (found) return found;
|
|
34
|
-
}
|
|
35
|
-
} else if (node.type === 'JSXFragment') {
|
|
36
|
-
for (const child of node.children) {
|
|
37
|
-
const found = findInteractiveElement(child);
|
|
38
|
-
if (found) return found;
|
|
39
|
-
}
|
|
40
|
-
} else if (node.type === 'JSXExpressionContainer') {
|
|
41
|
-
const {
|
|
42
|
-
expression
|
|
43
|
-
} = node;
|
|
44
|
-
if (expression.type !== 'JSXEmptyExpression') {
|
|
45
|
-
return findInteractiveElement(expression);
|
|
46
|
-
}
|
|
47
|
-
} else if (node.type === 'LogicalExpression') {
|
|
48
|
-
const {
|
|
49
|
-
left,
|
|
50
|
-
right
|
|
51
|
-
} = node;
|
|
52
|
-
return findInteractiveElement(left) ?? findInteractiveElement(right);
|
|
53
|
-
} else if (node.type === 'ConditionalExpression') {
|
|
54
|
-
const {
|
|
55
|
-
consequent,
|
|
56
|
-
alternate
|
|
57
|
-
} = node;
|
|
58
|
-
return findInteractiveElement(consequent) ?? findInteractiveElement(alternate);
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
24
|
const TooltipNoInteractiveContent = exports.TooltipNoInteractiveContent = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
63
25
|
create(context) {
|
|
64
26
|
return {
|
|
@@ -83,18 +45,28 @@ const TooltipNoInteractiveContent = exports.TooltipNoInteractiveContent = _utils
|
|
|
83
45
|
if (expression.type === 'JSXEmptyExpression') {
|
|
84
46
|
continue;
|
|
85
47
|
}
|
|
86
|
-
|
|
87
|
-
|
|
48
|
+
let found = false;
|
|
49
|
+
(0, _walk_jsx_children.walkJsxChildren)(expression, leaf => {
|
|
50
|
+
if (found || leaf.type !== 'JSXElement') return;
|
|
51
|
+
const el = leaf;
|
|
52
|
+
const name = (0, _get_element_name.getElementName)(el.openingElement);
|
|
53
|
+
if (!name || !INTERACTIVE_ELEMENTS.has(name)) return;
|
|
88
54
|
context.report({
|
|
89
|
-
node:
|
|
55
|
+
node: el.openingElement,
|
|
90
56
|
messageId: 'noInteractiveContent',
|
|
91
57
|
data: {
|
|
92
58
|
propName: attr.name.name,
|
|
93
59
|
componentName,
|
|
94
|
-
elementName:
|
|
60
|
+
elementName: name
|
|
95
61
|
}
|
|
96
62
|
});
|
|
97
|
-
|
|
63
|
+
found = true;
|
|
64
|
+
}, {
|
|
65
|
+
shouldSkip: el => {
|
|
66
|
+
const name = (0, _get_element_name.getElementName)(el.openingElement);
|
|
67
|
+
return !name || !INTERACTIVE_ELEMENTS.has(name);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
98
70
|
}
|
|
99
71
|
}
|
|
100
72
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
export declare const ButtonGroupNoInvalidChildren: TSESLint.RuleModule<"invalidUnresolvableWrapperChild" | "invalidWrapperChild" | "invalidChild" | "invalidUnresolvableChild" | "invalidPopoverButton" | "invalidUnresolvablePopoverButton", [], unknown, TSESLint.RuleListener>;
|
|
3
|
+
//# sourceMappingURL=button_group_no_invalid_children.d.ts.map
|
|
@@ -0,0 +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;AAwDlC,eAAO,MAAM,4BAA4B,gOAuLxC,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ButtonGroupNoInvalidChildren = 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 _walk_jsx_children = require("../utils/walk_jsx_children");
|
|
12
|
+
var _get_attr_value = require("../utils/get_attr_value");
|
|
13
|
+
/*
|
|
14
|
+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
15
|
+
* or more contributor license agreements. Licensed under the Elastic License
|
|
16
|
+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
17
|
+
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
18
|
+
* Side Public License, v 1.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const BUTTON_GROUP = 'EuiButtonGroup';
|
|
22
|
+
const VALID_BUTTONS = new Set(['EuiButton', 'EuiButtonEmpty', 'EuiButtonIcon']);
|
|
23
|
+
const VALID_WRAPPERS = new Set(['EuiToolTip', 'EuiPopover', 'EuiCopy']);
|
|
24
|
+
const VALID_BUTTONS_LIST = Array.from(VALID_BUTTONS).join(', ');
|
|
25
|
+
const VALID_WRAPPERS_LIST = Array.from(VALID_WRAPPERS).join(', ');
|
|
26
|
+
function isCustomComponent(name) {
|
|
27
|
+
return name[0] === name[0].toUpperCase();
|
|
28
|
+
}
|
|
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
|
+
function reportInvalidWrapperChildren(wrapper, wrapperName, context) {
|
|
41
|
+
const children = (0, _flat_map.flatMap)(wrapper.children, c => collectJsxChildren(c, context.sourceCode));
|
|
42
|
+
for (const wrapperChild of children) {
|
|
43
|
+
if ((0, _has_spread.hasSpread)(wrapperChild.openingElement.attributes)) continue;
|
|
44
|
+
const wrapperChildName = (0, _get_element_name.getElementName)(wrapperChild.openingElement);
|
|
45
|
+
if (wrapperChildName === null || VALID_BUTTONS.has(wrapperChildName)) continue;
|
|
46
|
+
context.report({
|
|
47
|
+
node: wrapperChild.openingElement,
|
|
48
|
+
messageId: isCustomComponent(wrapperChildName) ? 'invalidUnresolvableWrapperChild' : 'invalidWrapperChild',
|
|
49
|
+
data: {
|
|
50
|
+
name: wrapperChildName,
|
|
51
|
+
wrapper: wrapperName
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const ButtonGroupNoInvalidChildren = exports.ButtonGroupNoInvalidChildren = _utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
57
|
+
create(context) {
|
|
58
|
+
return {
|
|
59
|
+
JSXElement(node) {
|
|
60
|
+
const {
|
|
61
|
+
openingElement
|
|
62
|
+
} = node;
|
|
63
|
+
if (openingElement.name.type !== 'JSXIdentifier' || openingElement.name.name !== BUTTON_GROUP) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Only validate when the variant is "default" (or absent — the default).
|
|
68
|
+
// Rules for "segmented" and "selection" are added in later chunks.
|
|
69
|
+
// Dynamic variant values (variables) are conservatively skipped.
|
|
70
|
+
const variant = (0, _get_attr_value.findAttrValue)(context, openingElement.attributes, 'variant');
|
|
71
|
+
if (variant !== undefined && variant !== 'default') return;
|
|
72
|
+
const children = (0, _flat_map.flatMap)(node.children, c => collectJsxChildren(c, context.sourceCode));
|
|
73
|
+
if (children.length === 0) return;
|
|
74
|
+
for (const child of children) {
|
|
75
|
+
const name = (0, _get_element_name.getElementName)(child.openingElement);
|
|
76
|
+
if (name === null) continue;
|
|
77
|
+
if (VALID_BUTTONS.has(name)) continue;
|
|
78
|
+
if (VALID_WRAPPERS.has(name)) {
|
|
79
|
+
if (name === 'EuiToolTip') {
|
|
80
|
+
// Validate JSX children (expanding fragments/conditionals).
|
|
81
|
+
reportInvalidWrapperChildren(child, name, context);
|
|
82
|
+
} else if (name === 'EuiPopover') {
|
|
83
|
+
// The trigger is the `button` prop, not JSX children (panel
|
|
84
|
+
// content). The prop value may be a JSXExpressionContainer or
|
|
85
|
+
// a bare JSX element; collectJsxChildren handles both.
|
|
86
|
+
// EuiToolTip wrapping the trigger button is also supported.
|
|
87
|
+
const buttonProp = child.openingElement.attributes.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'JSXIdentifier' && attr.name.name === 'button');
|
|
88
|
+
if (buttonProp?.value != null) {
|
|
89
|
+
const triggerElements = collectJsxChildren(buttonProp.value, context.sourceCode);
|
|
90
|
+
for (const triggerElement of triggerElements) {
|
|
91
|
+
if ((0, _has_spread.hasSpread)(triggerElement.openingElement.attributes)) continue;
|
|
92
|
+
const triggerElementName = (0, _get_element_name.getElementName)(triggerElement.openingElement);
|
|
93
|
+
if (triggerElementName === null || VALID_BUTTONS.has(triggerElementName)) continue;
|
|
94
|
+
if (triggerElementName === 'EuiToolTip') {
|
|
95
|
+
// EuiToolTip wrapping the trigger — validate its children.
|
|
96
|
+
const tooltipChildren = (0, _flat_map.flatMap)(triggerElement.children, c => collectJsxChildren(c, context.sourceCode));
|
|
97
|
+
for (const tooltipChild of tooltipChildren) {
|
|
98
|
+
if ((0, _has_spread.hasSpread)(tooltipChild.openingElement.attributes)) continue;
|
|
99
|
+
const tooltipChildName = (0, _get_element_name.getElementName)(tooltipChild.openingElement);
|
|
100
|
+
if (tooltipChildName === null || VALID_BUTTONS.has(tooltipChildName)) continue;
|
|
101
|
+
context.report({
|
|
102
|
+
node: tooltipChild.openingElement,
|
|
103
|
+
messageId: isCustomComponent(tooltipChildName) ? 'invalidUnresolvablePopoverButton' : 'invalidPopoverButton',
|
|
104
|
+
data: {
|
|
105
|
+
name: tooltipChildName
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
context.report({
|
|
112
|
+
node: triggerElement.openingElement,
|
|
113
|
+
messageId: isCustomComponent(triggerElementName) ? 'invalidUnresolvablePopoverButton' : 'invalidPopoverButton',
|
|
114
|
+
data: {
|
|
115
|
+
name: triggerElementName
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} else if (name === 'EuiCopy') {
|
|
121
|
+
// Children is a render prop: {(copy) => <EuiButton />}
|
|
122
|
+
// ArrowFunctionExpression with expression body is expanded by
|
|
123
|
+
// collectJsxChildren, so validation works the same as EuiToolTip.
|
|
124
|
+
reportInvalidWrapperChildren(child, name, context);
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// For elements that aren't known valid buttons or wrappers, a spread
|
|
130
|
+
// prevents static classification; skip conservatively.
|
|
131
|
+
if ((0, _has_spread.hasSpread)(child.openingElement.attributes)) continue;
|
|
132
|
+
context.report({
|
|
133
|
+
node: child.openingElement,
|
|
134
|
+
messageId: isCustomComponent(name) ? 'invalidUnresolvableChild' : 'invalidChild',
|
|
135
|
+
data: {
|
|
136
|
+
name
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
meta: {
|
|
144
|
+
type: 'problem',
|
|
145
|
+
docs: {
|
|
146
|
+
description: `Enforce that EuiButtonGroup children are ${VALID_BUTTONS_LIST}, or a supported wrapper (${VALID_WRAPPERS_LIST})`
|
|
147
|
+
},
|
|
148
|
+
schema: [],
|
|
149
|
+
messages: {
|
|
150
|
+
invalidChild: [`{{ name }} is not a valid child of EuiButtonGroup.`, `Allowed children: ${VALID_BUTTONS_LIST}.`, `Allowed wrappers: ${VALID_WRAPPERS_LIST}.`].join(' '),
|
|
151
|
+
invalidUnresolvableChild: [`{{ name }} cannot be verified as a valid child of EuiButtonGroup.`, `Allowed children: ${VALID_BUTTONS_LIST}.`, `Allowed wrappers: ${VALID_WRAPPERS_LIST}.`, `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(' '),
|
|
152
|
+
invalidPopoverButton: [`{{ name }} is not a valid trigger button for EuiPopover in EuiButtonGroup.`, `The \`button\` prop must be ${VALID_BUTTONS_LIST}.`].join(' '),
|
|
153
|
+
invalidUnresolvablePopoverButton: [`{{ name }} cannot be verified as a valid trigger button for EuiPopover in EuiButtonGroup.`, `The \`button\` prop must be ${VALID_BUTTONS_LIST}.`, `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(' '),
|
|
154
|
+
invalidWrapperChild: [`{{ name }} inside {{ wrapper }} is not a valid button for EuiButtonGroup.`, `The wrapper must contain ${VALID_BUTTONS_LIST}.`].join(' '),
|
|
155
|
+
invalidUnresolvableWrapperChild: [`{{ name }} inside {{ wrapper }} cannot be verified as a valid button for EuiButtonGroup.`, `The wrapper must contain ${VALID_BUTTONS_LIST}.`, `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(' ')
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
defaultOptions: []
|
|
159
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"callout_prefer_props_for_content.d.ts","sourceRoot":"","sources":["../../../src/rules/callout_prefer_props_for_content.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"callout_prefer_props_for_content.d.ts","sourceRoot":"","sources":["../../../src/rules/callout_prefer_props_for_content.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAoBtE,KAAK,UAAU,GACX,uBAAuB,GACvB,kBAAkB,GAClB,qBAAqB,CAAC;AAC1B,KAAK,OAAO,GAAG,CAAC;IAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAE3C,eAAO,MAAM,4BAA4B,gFA2JvC,CAAC"}
|