Skip to content

[EuiButtonGroup] Implement redesign #3 - Selection variant - #9929

Merged
mgadewoll merged 16 commits into
elastic:mainfrom
mgadewoll:buttongroup/redesign-part-3
Aug 25, 2026
Merged

[EuiButtonGroup] Implement redesign #3 - Selection variant#9929
mgadewoll merged 16 commits into
elastic:mainfrom
mgadewoll:buttongroup/redesign-part-3

Conversation

@mgadewoll

@mgadewoll mgadewoll commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Note

This PR is part 3 of implementing a new button group design and API.
It builds on the segmented variant added in part 2.

  • What: Adds support for variant="selection" to EuiButtonGroup with the new Children API.
  • Why: Part of the button group redesign work. Chunk one for https://github.com/elastic/eui-private/issues/727.
  • How:
    • Added new props for the Children API:
      • Added variant="selection" which renders the group as a visually connected toggle control with aria-pressed semantics per button
      • Added type prop ('single' | 'multi') to switch between single or multi selection (same as existing Options API)
      • Added idSelected prop for single-state selection (same as existing Options API)
      • Added idToSelectedMap prop for multi-state selection (same as existing Options API)
      • Added onChange prop (same as existing Options API)
      • Added display prop ('regular' | 'highlighted' | 'inverse') to render different visual variants
      • Added useEuiButtonGroupSelection hook for handling button group selection

Note

There will be another PR after this one that applies the new styles to the existing Options API button group and updates eui internal usages, effectively swapping over to the full redesign.

API Changes

component / parent prop / child change description
EuiButtonGroup (Children API) variant Updated Added selection value to support a selection button group similar to the legacy Options API.
EuiButtonGroup (Children API) type Added Defines selection type. Values: `single
EuiButtonGroup (Children API) idSelected Added When set, defines the selected child for single-selection
EuiButtonGroup (Children API) idToSelectedMap Added When set, defines the selected children for multi-selection
EuiButtonGroup (Children API) onChange Added Callback fired when a child button is selected. Returns the id of the button.
EuiButtonGroup (Children API) display Added Defines the visual sub-variant. Values: `regular

Screenshots

Description Light Dark
type="single" Screenshot 2026-08-19 at 09 10 54 Screenshot 2026-08-19 at 09 11 00
type="multi" Screenshot 2026-08-19 at 09 11 10 Screenshot 2026-08-19 at 09 11 13
display="highlighted" Screenshot 2026-08-19 at 09 12 51 Screenshot 2026-08-19 at 09 12 55
display="inverse" Screenshot 2026-08-19 at 09 13 01 Screenshot 2026-08-19 at 09 13 07
showDividers=true Screenshot 2026-08-19 at 09 15 27 Screenshot 2026-08-19 at 09 15 33
isDisabled=true Screenshot 2026-08-19 at 09 21 07 Screenshot 2026-08-19 at 09 21 18
with EuiButtonIcon only Screenshot 2026-08-19 at 09 20 15 Screenshot 2026-08-19 at 09 20 18

HCM

Description Light Dark
display="regular" Screenshot 2026-08-21 at 11 24 29 Screenshot 2026-08-21 at 11 24 33
display="highlighted" Screenshot 2026-08-21 at 11 25 17 Screenshot 2026-08-21 at 11 25 20
display="inverse" Screenshot 2026-08-21 at 11 25 12 Screenshot 2026-08-21 at 11 25 09
Windows HCM Screenshot 2026-08-21 at 11 30 10 Screenshot 2026-08-21 at 11 29 34

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: 🟢 Low

🧪 The changes have been run in Kibana CI (🟢 build)
Requires Kibana changes: commits added here (snapshot update only)

Release Readiness

  • Documentation: {link to docs page(s)}
  • Figma: ideation specs
  • 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

💻 Storybook
💻 Storybook kitchen sink

  • verify variant="selection" renders the component according to the new design specs
  • verify variant="selection" with type="single" renders correctly and only one button is selected at a time
  • verify variant="selection" with type="multi" allows multiple selections simultaneously
  • verify display="regular", display="highlighted", and display="inverse" render as expected
  • verify idSelected correctly selects a single child button based on its id and it applies only for type="single"
  • verify idToSelectedMap correctly selects multiple child buttons based on their id and it applies only for type="multi"
  • verify aria-pressed is correctly set on selected/deselected buttons
  • verify isDisabled on the parent disables all children
  • verify isDisabled can be set on children individually
  • verify there are no visual or functional regressions for EuiButtonGroup used with the Options API
  • verify there are no visual or functional regressions for variant="default" and variant="segmented"

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 19, 2026
@mgadewoll
mgadewoll force-pushed the buttongroup/redesign-part-3 branch from 0d4ae63 to a8a9056 Compare August 19, 2026 07:55
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

📷 32 visual difference(s) found

Look at the visual diff below. If everything is expected, run Approve visual changes to update baselines, re-run the job or make appropriate fixes.

See the visual regression testing wiki for more information.

Expand to review

euisuperdatepicker (6 differences)

StoryDiff %BeforeAfterDiff
time window buttons compressed desktop n/a
time window buttons compressed mobile n/a
time window buttons desktop n/a
time window buttons mobile n/a
time window buttons with zoom in desktop n/a
time window buttons with zoom in mobile n/a

euibuttongroup (22 differences)

StoryDiff %BeforeAfterDiff
disabled with tooltips desktop 0.24%
disabled with tooltips mobile 0.96%
dividers mobile 1.80%
flex layout behavior mobile 0.63%
high contrast desktop n/a
high contrast mobile n/a
icon only desktop n/a
icon only mobile n/a
kitchen sink desktop n/a
kitchen sink mobile n/a
multi selection desktop n/a
multi selection mobile n/a
partially disabled desktop 6.79%
partially disabled mobile 19.30%
single selection desktop n/a
single selection mobile n/a
with children desktop n/a
with children mobile n/a
with mixed children desktop n/a
with mixed children mobile n/a
with tooltips desktop 0.24%
with tooltips mobile 0.96%

euidatagrid (4 differences)

StoryDiff %BeforeAfterDiff
column sorting desktop <0.01%
column sorting mobile 0.59%
display selector desktop 0.07%
display selector mobile 0.19%

@mgadewoll
mgadewoll force-pushed the buttongroup/redesign-part-3 branch from b86ceff to e25a064 Compare August 19, 2026 13:52
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

📷 6 visual difference(s) found

Look at the visual diff below. If everything is expected, run Approve visual changes to update baselines, re-run the job or make appropriate fixes.

See the visual regression testing wiki for more information.

Expand to review

euibuttongroup (6 differences)

StoryDiff %BeforeAfterDiff
dividers mobile 1.80%
flex layout behavior mobile 0.63%
kitchen sink desktop n/a
kitchen sink mobile n/a
partially disabled desktop 6.79%
partially disabled mobile 19.30%

@mgadewoll
mgadewoll requested a review from JoseLuisGJ August 21, 2026 07:19
@mgadewoll
mgadewoll force-pushed the buttongroup/redesign-part-3 branch from 9f63c7b to 39df269 Compare August 21, 2026 07:26
@mgadewoll
mgadewoll marked this pull request as ready for review August 21, 2026 08:26
@mgadewoll
mgadewoll requested a review from a team as a code owner August 21, 2026 08:26
Copilot AI lite review requested due to automatic review settings August 21, 2026 08:26

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

Adds the new variant="selection" to EuiButtonGroup’s Children API as part of the ongoing ButtonGroup redesign, including controlled single/multi selection behavior and updated visuals/semantics.

Changes:

  • Implemented selection-group behavior via context wiring + a new useEuiButtonGroupSelection hook, including aria-pressed semantics.
  • Added/updated styling, Storybook stories, docs, and Jest coverage for the new selection variant.
  • Exported the Children API props type and added a changelog entry.

Reviewed changes

Copilot reviewed 13 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/website/docs/components/navigation/buttons/group.mdx Documents variant="selection" usage and provides interactive examples
packages/release-cli/kibana-prep-commits Adds Kibana commit reference for integration prep
packages/eui/src/components/button/use_button_common_props.ts Wires selection props + merged click handling via button context
packages/eui/src/components/button/index.ts Exports EuiButtonGroupChildrenProps type
packages/eui/src/components/button/button.tsx Passes selection-derived props into EuiButtonDisplay
packages/eui/src/components/button/button_icon/button_icon.tsx Enables selection wiring for EuiButtonIcon (id/onClick/isSelected)
packages/eui/src/components/button/button_group/use_button_group_selection.ts Adds controlled selection helper hook for selection variant
packages/eui/src/components/button/button_group/button_group.tsx Introduces selection variant props + context-driven selection behavior
packages/eui/src/components/button/button_group/button_group.test.tsx Adds Jest coverage for selection variant behavior/attributes
packages/eui/src/components/button/button_group/button_group.styles.ts Adds selection styling (including display variants + HCM handling)
packages/eui/src/components/button/button_group/button_group_children.stories.tsx Adds selection stories and KitchenSink examples
packages/eui/src/components/button/button_context.tsx Extends context with getSelectionProps for selection variant
packages/eui/changelogs/upcoming/9929.md Changelog entry for the Children API selection variant additions
Suppressed comments (7)

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:818

  • This multi-selection KitchenSink example reuses delete/rename/save IDs that appear in other rendered selection examples, creating duplicate DOM IDs. Prefix these IDs (and the idToSelectedMap keys) per example.
              <StatefulButtonGroupSelection
                {...commonProps}
                variant="selection"
                buttonSize={buttonSizeChildren}
                gutterSize={gutterSize}
                type="multi"

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:874

  • This highlighted-display KitchenSink example reuses delete/rename/save IDs that appear in other rendered selection examples, creating duplicate DOM IDs. Prefix these IDs (and the idToSelectedMap keys) per example.
              <StatefulButtonGroupSelection
                {...commonProps}
                variant="selection"
                buttonSize={buttonSizeChildren}
                gutterSize={gutterSize}
                type="multi"

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:932

  • This inverse-display KitchenSink example reuses delete/rename/save IDs that appear in other rendered selection examples, creating duplicate DOM IDs. Prefix these IDs (and the idToSelectedMap keys) per example.
              <StatefulButtonGroupSelection
                {...commonProps}
                variant="selection"
                buttonSize={buttonSizeChildren}
                gutterSize={gutterSize}
                type="multi"

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:836

  • This multi-selection icon example reuses grid/list/map IDs that also appear in other selection examples rendered on the same KitchenSink page. Prefix these IDs (and idToSelectedMap keys) per example to avoid duplicate DOM IDs.
                type="multi"
                idToSelectedMap={{ grid: true, list: true }}
              >
                <EuiToolTip content="Grid view" disableScreenReaderOutput>
                  <EuiButtonIcon
                    id="grid"

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:780

  • This KitchenSink section reuses grid/list/map IDs that also appear in other selection examples below, resulting in duplicate DOM id attributes on the same page. Prefix these IDs per example to keep them globally unique.
                idSelected="grid"
              >
                <EuiToolTip content="Grid view" disableScreenReaderOutput>
                  <EuiButtonIcon
                    id="grid"

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:894

  • This highlighted-display icon example reuses grid/list/map IDs that appear in other selection examples rendered on the same KitchenSink page. Prefix these IDs (and idToSelectedMap keys) per example to avoid duplicate DOM IDs.
                idToSelectedMap={{ grid: true }}
                display="highlighted"
              >
                <EuiToolTip content="Grid view" disableScreenReaderOutput>
                  <EuiButtonIcon
                    id="grid"

packages/eui/src/components/button/button_group/button_group_children.stories.tsx:952

  • This inverse-display icon example reuses grid/list/map IDs that appear in other selection examples rendered on the same KitchenSink page. Prefix these IDs (and idToSelectedMap keys) per example to avoid duplicate DOM IDs.
                idToSelectedMap={{ grid: true }}
                display="inverse"
              >
                <EuiToolTip content="Grid view" disableScreenReaderOutput>
                  <EuiButtonIcon
                    id="grid"

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

Comment thread packages/website/docs/components/navigation/buttons/group.mdx Outdated
Comment thread packages/website/docs/components/navigation/buttons/group.mdx
Comment thread packages/eui/src/components/button/button_group/button_group.test.tsx Outdated
@mgadewoll
mgadewoll marked this pull request as draft August 21, 2026 09:11
@mgadewoll
mgadewoll marked this pull request as ready for review August 21, 2026 11:01
@weronikaolejniczak
weronikaolejniczak self-requested a review August 24, 2026 08:34
@JoseLuisGJ

Copy link
Copy Markdown
Contributor

Great progress Lene. We should change the disabled state to perform like the regular disable state with EuiButtonEmpty as you spotted while implementing it. This way will be more aligned with the already exiting buttons, Append Prepend

DONT DO
image image

@mgadewoll

Copy link
Copy Markdown
Contributor Author

Great progress Lene. We should change the disabled state to perform like the regular disable state with EuiButtonEmpty as you spotted while implementing it. This way will be more aligned with the already exiting buttons, Append Prepend

Thanks! Yes the disabled background change is already included 👍

Comment thread packages/eui/src/components/button/button.tsx 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.

Blocking:

  1. I think there's a type hole because I can do:
<EuiButtonGroup
  legend="Actions"
  variant="segmented"
  display="highlighted"
  idSelected="save"
  onChange={(id) => {}}
>

which is undesired API (?). We should discriminate on variant.

  1. Docs and JSDoc say some props are segmented-only but now they also apply to selection: showDividers, layout, wrap. E.g.:

https://github.com/mgadewoll/eui/blob/415df2ec96fe6cefe620c7a2056c47da7f4d2127/packages/eui/src/components/button/button_group/button_group.tsx#L332-L336

  1. EuiButtonEmpty isn’t wired for selection. Should we wire it or report just like we do for the segmented variant?

Non-blocking:

  1. Every child has to have a valid id but we cannot enforce that through types. Maybe we should add a dev warning at least? Or maybe... extend the existing lint rule: #9890? I prefer warnings in IDE and I think this case is statically parseable.

@mgadewoll

mgadewoll commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author
  1. I think there's a type hole because I can do:
<EuiButtonGroup
  legend="Actions"
  variant="segmented"
  display="highlighted"
  idSelected="save"
  onChange={(id) => {}}
>

which is undesired API (?). We should discriminate on variant.

You're absolutely right, thanks for the catch! That should not be possible.
Updated in 5c30de2

  1. Docs and JSDoc say some props are segmented-only but now they also apply to selection: showDividers, layout, wrap. E.g.:

https://github.com/mgadewoll/eui/blob/415df2ec96fe6cefe620c7a2056c47da7f4d2127/packages/eui/src/components/button/button_group/button_group.tsx#L332-L336

Ah good catch, the JSDoc is wrong. Updated in 5c30de2
ℹ️ variant="selection" is equal to segmented plus additional functionality.

  1. EuiButtonEmpty isn’t wired for selection. Should we wire it or report just like we do for the segmented variant?
    EuiButtonEmpty isn't expected for segemented or selection. We only expect to pass EuiButton or EuiButtonIcon for simplicity.

That being said, we will still need to update the eslint rule to include selection in the checks.

  1. Every child has to have a valid id but we cannot enforce that through types. Maybe we should add a dev warning at least? Or maybe... extend the existing lint rule: [eslint-plugin] update button-group-no-invalid-children rule to support segmented variant #9890? I prefer warnings in IDE and I think this case is statically parseable.

Yes, that was my plan to add an eslint rule for it 👍

@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 making the changes, LGTM 🟢

I'm looking into the stuck changelog check 👀

@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

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.

5 participants