[eslint-plugin] Add button-group-no-invalid-children rule - #9849
[eslint-plugin] Add button-group-no-invalid-children rule#9849mgadewoll wants to merge 12 commits into
button-group-no-invalid-children rule#9849Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new @elastic/eui ESLint rule to statically validate EuiButtonGroup’s new Children API usage, helping consumers avoid composing unsupported child elements and wrapper patterns.
Changes:
- Implement
button-group-no-invalid-childrenrule, validating allowed direct children and supported wrappers (including special handling forEuiPopover’sbuttonprop). - Add JSX child-collection utilities to expand/resolve common composition patterns (fragments, conditionals, arrays, identifiers, local arrow components, and
.map()callbacks). - Add comprehensive unit tests and document the new rule in the plugin README; register the rule in the plugin’s exported config.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/eslint-plugin/src/utils/is_fragment.ts | New helper to identify <Fragment> / <React.Fragment> opening elements. |
| packages/eslint-plugin/src/utils/flat_map.ts | New flatMap shim used by rule utilities. |
| packages/eslint-plugin/src/utils/collect_jsx_children.ts | New utility to expand JSX children into concrete JSXElements for validation. |
| packages/eslint-plugin/src/rules/button_group_no_invalid_children.ts | New ESLint rule implementation for EuiButtonGroup Children API validation. |
| packages/eslint-plugin/src/rules/button_group_no_invalid_children.test.ts | Unit tests covering valid/invalid child patterns and wrapper handling. |
| packages/eslint-plugin/src/index.ts | Registers the new rule and enables it in recommended. |
| packages/eslint-plugin/README.md | Adds end-user documentation and examples for the new rule. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- since the component API is new and there aren't any usages yet, we can enforce the right usage strictly from the beginning
button-group-no-invalid-children rulebutton-group-no-invalid-children rule
weronikaolejniczak
left a comment
There was a problem hiding this comment.
Overall, the rule looks good 👍🏻 impressive test coverage, took me some time to go over all cases. This is a new API that doesn't have usage in Kibana so it's hard to discuss what cases have to be covered. I only noticed one inconsistency and that is:
EuiPopover button prop can have a custom component <SaveButton />, it gets invalidPopoverButton that doesn't have the hint about suppressing a comment. Shouldn't it use invalidUnresolvableChild (or could be invalidUnresolvablePopoverButton)? Same with EuiToolTip inside EuiPopover case. We can mention it inside "Custom button wrapper components" as well.
One potential friction point is the rule returns invalidChild for all Eui-prefixed components that might be defined by the consumer (e.g. EuiCustomWrapper) instead of invalidUnresorvableChild and they don't see the suppress hint.
There was a problem hiding this comment.
non-blocking suggestion: This utility could be simplified to just the traversal (fragment expansion, conditionals, arrays, &&) and it could be reused in callout_prefer_props_for_content.ts and tooltip_no_interactive_content.ts. The scope lookups could stay in the rule's source directly. The only functional difference between the rules is the && behavior because in both callout and tooltip rules we check both sides, this utility checks only the right.
But it's not something I'll push for, this is fine as-is.
There was a problem hiding this comment.
That's correct. I mainly didn't want to update other rules in this PR to not potentially affect other code outside the scope. 😅
But if we're ok adding it here to be complete, we can refactor it 👍 It needs a bit more refactoring to ensure it's truly generic. I added a proposal refactor (commit) that walks the children and applies custom checks per rule. I think a simple shared collector wasn't enough because the three rules have different needs (button group should resolve variable references via scope, callout checks non-JSXElement content like plain text and literals). Let me know what you think, in case we can also revert it.
…mponent usages get proper hint messages
Ah yes, thank's for the catch! Those should show the hint indeed to be useful. Updated in 5590292 |
- makes the util more generic and universally useable by rules to traverse children; provides means to customize by
💚 Build Succeeded
History
cc @mgadewoll |
💚 Build Succeeded
History
cc @mgadewoll |
Summary
Note
This PR is related to #9845 which adds the new Children API on
EuiButtonGroup.button-group-no-invalid-childrento prevent passing unexpectedchildrentoEuiButtonGroupwhen used with the new Children API (added in [EuiButtonGroup] Implement redesign #1 - Add EuiButtonContext & Children API support #9845).EuiButtonGroupchildren to only allowEuiButton,EuiButtonEmptyandEuiButtonIcon+ wrappersEuiPopover,EuiToolTipandEuiCopy. This check is set toerroras the tested API is new and we want to enforce correct usage.Examples
✅ valid
❌ invalid
Additional information
The EuiButtonGroup redesign adds a Children API (PR) as an alternative to the existing options-array API. With this API, consumers compose button components directly as JSX children rather than passing a configuration array. Because JSX children are freeform, the rule provides a lint-time guardrail that catches invalid compositions without requiring a runtime throw.
What the rule validates
The rule validates direct children for
variant="default": EuiButton, EuiButtonEmpty, EuiButtonIcon. (later iterations will support morevariantvalues). Additionally it validates allowed the following allowed wrapper components:EuiPopover,EuiToolTipandEuiCopy.Note
This rule will be extended with the following implementation iterations as there are different requirements for the button group depending on some props.
API Changes
⚪ No API changes
Screenshots
Impact Assessment
Note: Most PRs should be tested in Kibana to help gauge their Impact before merging.
Impact level: 🟢 None
Release Readiness
QA instructions for reviewer
yarn workspace @elastic/eslint-plugin-eui buildon rootyarn workspace @elastic/eui build:workspaceson rootbutton_group_children.stories.tsxand run eslint check withyarn workspace @elastic/eui linton rootChecklist before marking Ready for Review
breaking changelabel (if applicable)Reviewer checklist