Skip to content

[eslint-plugin] update button-group-no-invalid-children rule to support segmented variant - #9890

Merged
mgadewoll merged 7 commits into
elastic:mainfrom
mgadewoll:buttongroup/eslint-no-invalid-children-segmented
Aug 24, 2026
Merged

[eslint-plugin] update button-group-no-invalid-children rule to support segmented variant#9890
mgadewoll merged 7 commits into
elastic:mainfrom
mgadewoll:buttongroup/eslint-no-invalid-children-segmented

Conversation

@mgadewoll

@mgadewoll mgadewoll commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Note

This PR is related to #9862 which adds variant="segmented" to EuiButtonGroup.
This PR should be rebased after merging the implementation PR.

  • What: Extends the existing button-group-no-invalid-children ESLint rule with validation for variant="segmented".
  • Why: Part of https://github.com/elastic/eui-private/issues/726. The segmented variant has stricter child requirements than the default variant and needs lint-time enforcement to match.
  • How: Adds a SEGMENTED_VALID_BUTTONS set (EuiButton, EuiButtonIconEuiButtonEmpty is excluded) and a mixed-type check that reports an error when both EuiButton and EuiButtonIcon appear in the same segmented group.

Examples

✅ valid

// EuiButton children only
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButton>Save</EuiButton>
  <EuiButton>Cancel</EuiButton>
</EuiButtonGroup>

// EuiButtonIcon children only
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButtonIcon iconType="pencil" />
  <EuiButtonIcon iconType="trash" />
</EuiButtonGroup>

// EuiToolTip wrapping a valid button
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButton>Save</EuiButton>
  <EuiToolTip content="Delete permanently">
    <EuiButton color="danger">Delete</EuiButton>
  </EuiToolTip>
</EuiButtonGroup>

// EuiPopover with a valid trigger
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButton>Save</EuiButton>
  <EuiPopover button={<EuiButton>More</EuiButton>} isOpen={false} closePopover={() => {}}>
    Panel content
  </EuiPopover>
</EuiButtonGroup>

❌ invalid

// EuiButtonEmpty is not valid for segmented
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButtonEmpty color="text">Cancel</EuiButtonEmpty>
</EuiButtonGroup>

// mixing EuiButton and EuiButtonIcon is not allowed
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButton>Save</EuiButton>
  <EuiButtonIcon iconType="trash" />
</EuiButtonGroup>

// invalid component inside EuiToolTip
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiToolTip content="Cancel">
    <EuiButtonEmpty color="text">Cancel</EuiButtonEmpty>
  </EuiToolTip>
</EuiButtonGroup>

// invalid EuiPopover trigger for segmented
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiPopover button={<EuiButtonEmpty>More</EuiButtonEmpty>} isOpen={false} closePopover={() => {}}>
    Panel content
  </EuiPopover>
</EuiButtonGroup>

Additional information

What the rule validates for variant="segmented"

  • Valid direct children: EuiButton, EuiButtonIcon (not EuiButtonEmpty - it has no background and is incompatible with the segmented visual design)
  • Valid wrappers: same as variant="default" (EuiToolTip, EuiPopover, EuiCopy) but with the restricted button set applied inside each
  • Mixed types: reports invalidSegmentedMixedTypes when both EuiButton and EuiButtonIcon are present in the same group. Children inside EuiToolTip are inspected for this check; EuiPopover triggers and EuiCopy render props are not (too indirect to resolve statically)

Error message changes

The allowed list in all error messages is now dynamic (interpolated via {{ allowed }}) so segmented errors list only EuiButton, EuiButtonIcon while default-variant errors continue to list EuiButton, EuiButtonEmpty, EuiButtonIcon.

API Changes

⚪ No API changes

Screenshots

Screenshot 2026-08-11 at 15 27 48 Screenshot 2026-08-11 at 15 28 08

Impact Assessment

Note: Most PRs should be tested in Kibana to help gauge their Impact before merging.

  • 🔴 Breaking changes — What will break? How many usages in Kibana/Cloud UI are impacted?
  • 💅 Visual changes — May impact style overrides; could require visual testing. Explain and estimate impact.
  • 🧪 Test impact — May break functional or snapshot tests (e.g., HTML structure, class names, default values).
  • 🔧 Hard to integrate — If changes require substantial updates to Kibana, please stage the changes and link them here.

Impact level: 🟢 None

Release Readiness

  • Documentation: {link to docs page(s)}
  • Figma: {link to Figma or issue}
  • Migration guide: {steps or link, for breaking/visual changes or deprecations}
  • Adoption plan (new features): {link to issue/doc or outline who will integrate this and where}

QA instructions for reviewer

  • CI passes
  • verify locally the rule applies and flags invalid usages
    • checkout the PR
    • run yarn workspace @elastic/eslint-plugin-eui build on root
    • run yarn workspace @elastic/eui build:workspaces on root
    • add testing code example (e.g. see above) and/or update button_group_children.stories.tsx and run eslint check with yarn workspace @elastic/eui lint on root

Checklist before marking Ready for Review

Reviewer checklist

  • Approved Impact Assessment — Acceptable to merge given the consumer impact.
  • Approved Release Readiness — Docs, Figma, and migration info are sufficient to ship.

@mgadewoll mgadewoll self-assigned this Aug 11, 2026
@mgadewoll
mgadewoll marked this pull request as ready for review August 14, 2026 06:15
@mgadewoll
mgadewoll requested a review from a team as a code owner August 14, 2026 06:15
Copilot AI lite review requested due to automatic review settings August 14, 2026 06:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the @elastic/eslint-plugin-eui button-group-no-invalid-children rule to lint EuiButtonGroup usages with variant="segmented", enforcing stricter allowed children and adding a mixed-type check to match the new segmented design constraints.

Changes:

  • Added segmented-specific allowed child validation (EuiButton, EuiButtonIcon) and dynamic “allowed children” messaging.
  • Added segmented-only validation that disallows mixing EuiButton and EuiButtonIcon within the same group (with limited wrapper introspection as documented in the rule).
  • Expanded/updated rule tests to cover segmented valid/invalid scenarios and updated expected message data payloads.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/eslint-plugin/src/rules/button_group_no_invalid_children.ts Adds segmented variant validation, dynamic allowed-list messaging, and mixed-type detection for segmented groups.
packages/eslint-plugin/src/rules/button_group_no_invalid_children.test.ts Adds comprehensive segmented test coverage and updates expected error data to include the dynamic allowed string.
packages/eslint-plugin/changelogs/upcoming/9890.md Adds a changelog entry for the rule enhancement (but currently links the wrong PR number).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/eslint-plugin/changelogs/upcoming/9890.md Outdated
@weronikaolejniczak
weronikaolejniczak self-requested a review August 18, 2026 13:16
Comment thread packages/eslint-plugin/changelogs/upcoming/9890.md Outdated
Comment thread packages/eslint-plugin/src/rules/button_group_no_invalid_children.test.ts Outdated
Comment thread packages/eslint-plugin/src/rules/button_group_no_invalid_children.ts Outdated

@weronikaolejniczak weronikaolejniczak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of nits. Blocking:

  1. README.md rule section should document these changes.
  2. We already check for EuiPopover and EuiCopy render props, so we can actually resolve:
<EuiButtonGroup legend="Actions" variant="segmented">
  <EuiButton>Save</EuiButton>
  <EuiPopover button={<EuiButtonIcon iconType="menu" aria-label="More" />}  />
</EuiButtonGroup>

and warn about mixed usage.

  1. The rule reports: "EuiButtonEmpty cannot be verified as a valid child of EuiButtonGroup. Allowed children: EuiButton, EuiButtonIcon. If EuiButtonEmpty is a shared button wrapper component... suppress this rule..." but this is kinda misleading. It should report invalidChild.

@mgadewoll

Copy link
Copy Markdown
Contributor Author
  1. The rule reports: "EuiButtonEmpty cannot be verified as a valid child of EuiButtonGroup. Allowed children: EuiButton, EuiButtonIcon. If EuiButtonEmpty is a shared button wrapper component... suppress this rule..." but this is kinda misleading. It should report isInvalidChild.

This is a side effect of the changes we did here. We either filter for Eui prefix always, for a certain list (higher maintenance effort) or not at all 😅

There are Eui-prefixed custom components on Kibana (which is honestly a bit unexpected) like EuiButtonTo but they are a minority.

@weronikaolejniczak

Copy link
Copy Markdown
Contributor

@mgadewoll I understand but in my mind, we could just simply re-route to invalidChild when EuiButtonEmpty. Is that possible?

@mgadewoll

Copy link
Copy Markdown
Contributor Author

@mgadewoll I understand but in my mind, we could just simply re-route to invalidChild when EuiButtonEmpty. Is that possible?

Ah ok, you meant specifically for EuiButtonEmpty, thanks for clarifying! Sure we can do that 👍

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @mgadewoll

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @mgadewoll

@weronikaolejniczak weronikaolejniczak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing my feedback, Lene! LGTM 🟢

Btw the PR still mentions "EuiButtonEmpty is excluded".

Mentioned sth relevant to this PR here: #9929 (review)

@mgadewoll
mgadewoll merged commit 3f1c5db into elastic:main Aug 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants