Skip to content

ActionList: support TrailingAction in GroupHeading - #7829

Merged
adierkens merged 5 commits into
mainfrom
adierkens/action-list-group-heading-trailing-action
May 12, 2026
Merged

ActionList: support TrailingAction in GroupHeading#7829
adierkens merged 5 commits into
mainfrom
adierkens/action-list-group-heading-trailing-action

Conversation

@adierkens

@adierkensadierkens commented May 11, 2026

Copy link
Copy Markdown
Contributor

Closes#2043

Adds a new ActionList.GroupHeading.TrailingAction sub-component for header-level actions on grouped action lists, behind a feature flag:

<ActionList><ActionList.Group><ActionList.GroupHeadingas="h3">
Custom fields
<ActionList.GroupHeading.TrailingActionlabel="New field"icon={PlusIcon}/></ActionList.GroupHeading><ActionList.Item>...</ActionList.Item></ActionList.Group></ActionList>

When the primer_react_action_list_group_heading_trailing_action feature flag is enabled, this renders a small invisible IconButton as a sibling of the heading element (inside HeadingWrap), giving group headings a uniform square action target — visually equivalent to the CreateSavedView pattern in github-ui.

When the flag is disabled (the default), the slot isn't consumed and <ActionList.GroupHeading.TrailingAction> passes through as a regular child of the heading. There's no behavioral change for any existing consumer.

Why a dedicated sub-component instead of reusing ActionList.TrailingAction?

  • icon can be required at the type level — group headings aren't interactive on their own, so the action must always render as a square IconButton with a uniform hit target. A text-only button doesn't make sense here.
  • The component owns its own visual defaults (size="small", variant="invisible") — no cloneElement magic in GroupHeading to inject props.
  • ActionList.TrailingAction (used inside ActionList.Item) stays untouched and continues to work exactly as before — no risk of cross-context regressions.
  • The nested namespace (GroupHeading.TrailingAction) mirrors the visual nesting in JSX, making the API self-documenting.

Constraints

Trailing actions on group headings are only supported in lists with the default list role. Using <ActionList.GroupHeading.TrailingAction> inside ActionMenu (role="menu") or with role="listbox" will throw an invariant — the focus-management and accessibility patterns of those components require a different design pattern.

Changelog

New

  • ActionList.GroupHeading.TrailingAction — a small invisible IconButton for header-level actions on grouped lists. Available behind the primer_react_action_list_group_heading_trailing_action feature flag.
  • ActionListGroupHeadingTrailingActionProps type exported from @primer/react.

Changed

  • None when the feature flag is off.

Removed

  • N/A

Rollout strategy

Behind the primer_react_action_list_group_heading_trailing_action feature flag (default false).

  1. Merge with flag off (zero risk to existing consumers).
  2. Enable in github/github via DevPortal for one actor → staffship → GA.
  3. Once stable, flip the default to true, deprecate the flag.
  4. Future major: remove the flag entirely.
  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

  • New Storybook story: Components/ActionList/Features → With TrailingAction on GroupHeading (behind feature flag) — wraps the example in <FeatureFlags flags={{primer_react_action_list_group_heading_trailing_action: true}}> so reviewers see the new layout immediately. The Storybook flag toolbar also exposes the flag globally.
  • 4 new unit tests in Group.test.tsx:
    • Flag-on: trailing action renders as a sibling of the <h2>, not inside it
    • Flag-off: behavior preserved (action passes through into the heading children)
    • Flag-on + ActionMenu (role="menu"): throws invariant
    • Flag-on + role="listbox": throws invariant

Merge checklist

Adds support for ActionList.TrailingAction as a child of
ActionList.GroupHeading. When the
primer_react_action_list_group_heading_trailing_action feature flag is
enabled, a single TrailingAction child of GroupHeading is rendered as a
sibling of the heading element (inside HeadingWrap) rather than nested
inside it. This unblocks header-level actions on grouped action lists
(see #2043).
The behavior is opt-in via feature flag so existing consumers are
unaffected. Using a TrailingAction inside a GroupHeading within a list
that has role 'menu' or 'listbox' (e.g. ActionMenu, SelectPanel) throws
an invariant.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@changeset-bot

changeset-botBot commented May 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e184e42

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
NameType
@primer/reactMinor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actionsgithub-actionsBot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label May 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Action required

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Check the integration testing docs for step-by-step instructions. Or, apply the integration-tests: skipped manually label to skip these checks.

To publish a canary release for integration testing, apply the Canary Release label to this PR.

Replaces the previous approach of overloading ActionList.TrailingAction
with a dedicated ActionList.GroupHeading.TrailingAction component. This
gives header actions:
- Type-level icon enforcement (icon is now a required prop)
- A small invisible IconButton native default (no cloneElement magic)
- A self-documenting API via the GroupHeading.TrailingAction namespace
- Complete isolation from ActionList.TrailingAction (which is reverted
to its original shape)
The feature flag, render position, and a11y invariants for menu/listbox
roles are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…onProps
Borrow the prop shape from ActionList.TrailingAction (label, as, href,
loading, className, style) and only override icon to be required. This
gives the new component a consistent API with ActionList.TrailingAction
while removing duplicated prop definitions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
github-actionsBottemporarily deployed to storybook-preview-7829 May 11, 2026 16:04 Inactive
@adierkensadierkens added the Canary Release Apply this label when you want CI to create a canary release of the current PR label May 11, 2026
@adierkens
adierkens requested a review from CopilotMay 11, 2026 18:58

CopilotAI 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

This PR adds a new ActionList.GroupHeading.TrailingAction sub-component (behind the primer_react_action_list_group_heading_trailing_action feature flag) to support header-level trailing actions on grouped ActionLists, while preserving existing behavior when the flag is disabled.

Changes:

  • Introduces ActionList.GroupHeading.TrailingAction (renders an IconButton) and wires it into ActionList.GroupHeading via slot extraction when the feature flag is enabled.
  • Adds unit tests and a Storybook “features” story covering flag-on/flag-off behavior and invalid ARIA role usage (menu, listbox).
  • Adds the new feature flag to DefaultFeatureFlags and includes a changeset for a minor release.
Show a summary per file
FileDescription
packages/react/src/FeatureFlags/DefaultFeatureFlags.tsRegisters the new feature flag with a default of false.
packages/react/src/ActionList/index.tsRe-exports the new props type from the ActionList module.
packages/react/src/ActionList/GroupHeadingTrailingAction.tsxAdds the new GroupHeading.TrailingAction component and props type.
packages/react/src/ActionList/Group.tsxImplements feature-flagged slot extraction + invariant enforcement + layout changes for group heading trailing actions.
packages/react/src/ActionList/Group.test.tsxAdds coverage for flag-on/flag-off rendering and role-based invariants.
packages/react/src/ActionList/Group.module.cssAdds styling hooks for headings that have a trailing action sibling.
packages/react/src/ActionList/ActionList.features.stories.tsxAdds a Storybook example demonstrating the new API behind the feature flag.
.changeset/action-list-group-heading-trailing-action.mdDeclares a minor release and documents the new API/flag behavior.

Copilot's findings

  • Files reviewed: 8/8 changed files
  • Comments generated: 2

Comment threadpackages/react/src/ActionList/index.ts
Comment threadpackages/react/src/ActionList/Group.test.tsx Outdated
- Re-export ActionListGroupHeadingTrailingActionProps from @primer/react
root index so consumers can import the type. Also re-export the
pre-existing ActionListTrailingActionProps which was missing from the
root index.
- Rename and tighten the flag-disabled test: it previously claimed the
trailing action was not rendered, but the test really verifies that
the action passes through as a child of the heading element. Update
the assertion to match.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds ActionListGroupHeadingTrailingActionProps and the previously
missing ActionListTrailingActionProps to the @primer/react exports
snapshot, matching the additions in packages/react/src/index.ts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@adierkensadierkens changed the title ActionList: support TrailingAction in GroupHeading (behind feature flag)ActionList: support TrailingAction in GroupHeadingMay 11, 2026
@github-actions
github-actionsBottemporarily deployed to storybook-preview-7829 May 11, 2026 19:33 Inactive
@primer-integration

Copy link
Copy Markdown

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/20421

@adierkens
adierkens marked this pull request as ready for review May 11, 2026 21:10
@adierkens
adierkens requested a review from a team as a code ownerMay 11, 2026 21:10
@adierkens
adierkens requested a review from liuliu-devMay 11, 2026 21:10

@liuliu-devliuliu-dev 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.

:shipit:

@adierkens
adierkens enabled auto-merge May 11, 2026 23:45
@primer
primerBot disabled auto-merge May 12, 2026 14:30
@primer
primerBot enabled auto-merge May 12, 2026 14:30
@primer
primerBot disabled auto-merge May 12, 2026 14:30
@primer
primerBot enabled auto-merge May 12, 2026 14:30
@adierkens
adierkens disabled auto-merge May 12, 2026 14:57
@primer-integration

Copy link
Copy Markdown

Integration test results from github/github-ui:

Passed CI  Passed
Passed VRT  Passed
Passed Projects  Passed

All checks passed!

@adierkens
adierkens added this pull request to the merge queueMay 12, 2026
Merged via the queue into main with commit 228019cMay 12, 2026
54 checks passed
@adierkens
adierkens deleted the adierkens/action-list-group-heading-trailing-action branch May 12, 2026 15:09
@primerprimerBot mentioned this pull request May 12, 2026
@joshblackjoshblack mentioned this pull request May 14, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Canary ReleaseApply this label when you want CI to create a canary release of the current PRintegration-tests: recommendedThis change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ActionList.Group should allow for TrailingActions

3 participants

@adierkens@liuliu-dev