From 1dc21a872972d95d56ec064e4f6e5f9e8a154a59 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 11 Aug 2026 16:35:07 -0500 Subject: [PATCH 1/2] Migrate ActionList roots to mergeProps Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9541ca7f-0281-4223-a839-39d75393d922 --- .changeset/calm-action-lists-merge.md | 5 ++++ packages/react/src/ActionList/Group.tsx | 20 ++++++------- .../ActionList/GroupHeadingTrailingAction.tsx | 23 +++++++++------ packages/react/src/ActionList/Heading.tsx | 29 +++++++++++-------- packages/react/src/ActionList/Item.test.tsx | 1 + packages/react/src/ActionList/Item.tsx | 5 ++-- 6 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 .changeset/calm-action-lists-merge.md diff --git a/.changeset/calm-action-lists-merge.md b/.changeset/calm-action-lists-merge.md new file mode 100644 index 00000000000..ac258866b05 --- /dev/null +++ b/.changeset/calm-action-lists-merge.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +ActionList: Preserve component behavior and styling when merging consumer props diff --git a/packages/react/src/ActionList/Group.tsx b/packages/react/src/ActionList/Group.tsx index 5d981a41947..e508e7893f1 100644 --- a/packages/react/src/ActionList/Group.tsx +++ b/packages/react/src/ActionList/Group.tsx @@ -11,6 +11,7 @@ import type {FCWithSlotMarker} from '../utils/types/Slots' import {GroupHeadingTrailingAction} from './GroupHeadingTrailingAction' import {useFeatureFlag} from '../FeatureFlags' import {GroupContext} from './GroupContext' +import {mergeProps} from '../utils/mergeProps' const GROUP_HEADING_TRAILING_ACTION_FEATURE_FLAG = 'primer_react_action_list_group_heading_trailing_action' @@ -28,11 +29,7 @@ const Heading: React.FC> id, ...rest }) => { - return ( - - {children} - - ) + return {children} } type HeadingWrapProps = { @@ -73,7 +70,6 @@ export const Group: FCWithSlotMarker { @@ -98,10 +94,14 @@ export const Group: FCWithSlotMarker {title && !slots.groupHeading ? ( diff --git a/packages/react/src/ActionList/GroupHeadingTrailingAction.tsx b/packages/react/src/ActionList/GroupHeadingTrailingAction.tsx index a9b809c7466..9dc25e90921 100644 --- a/packages/react/src/ActionList/GroupHeadingTrailingAction.tsx +++ b/packages/react/src/ActionList/GroupHeadingTrailingAction.tsx @@ -4,6 +4,7 @@ import {IconButton} from '../Button' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import type {SlotMarker} from '../utils/types/Slots' import type {ActionListTrailingActionProps} from './TrailingAction' +import {mergeProps} from '../utils/mergeProps' /** * Props for `ActionList.GroupHeading.TrailingAction`. @@ -21,17 +22,21 @@ export type ActionListGroupHeadingTrailingActionProps = Omit ( ), ) as PolymorphicForwardRefComponent<'button' | 'a', ActionListGroupHeadingTrailingActionProps> & { diff --git a/packages/react/src/ActionList/Heading.tsx b/packages/react/src/ActionList/Heading.tsx index b8dd90e305d..3c5920a00bf 100644 --- a/packages/react/src/ActionList/Heading.tsx +++ b/packages/react/src/ActionList/Heading.tsx @@ -8,6 +8,7 @@ import {invariant} from '../utils/invariant' import {clsx} from 'clsx' import classes from './Heading.module.css' import visuallyHiddenClasses from '../_VisuallyHidden.module.css' +import {mergeProps} from '../utils/mergeProps' type HeadingLevels = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' type HeadingVariants = 'large' | 'medium' | 'small' @@ -19,7 +20,7 @@ export type ActionListHeadingProps = { style?: React.CSSProperties } -export const Heading = forwardRef(({as, size, children, visuallyHidden = false, className, ...props}, forwardedRef) => { +export const Heading = forwardRef(({as, size, children, visuallyHidden = false, ...props}, forwardedRef) => { const innerRef = React.useRef(null) const mergedRef = useMergedRefs(forwardedRef, innerRef) @@ -35,18 +36,22 @@ export const Heading = forwardRef(({as, size, children, visuallyHidden = false, return ( {children} diff --git a/packages/react/src/ActionList/Item.test.tsx b/packages/react/src/ActionList/Item.test.tsx index 1dbfb0d453b..21d3556e2c9 100644 --- a/packages/react/src/ActionList/Item.test.tsx +++ b/packages/react/src/ActionList/Item.test.tsx @@ -208,6 +208,7 @@ describe('ActionList.Item', () => { ) const button = container.querySelector('button') expect(button).toHaveTextContent('Item 1') + expect(button).toHaveAttribute('type', 'button') // Ensure passed prop "disabled" is applied to the button expect(button).toHaveAttribute('aria-disabled', 'true') const listItems = container.querySelectorAll('li') diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx index 1606952e3e1..88dce805b95 100644 --- a/packages/react/src/ActionList/Item.tsx +++ b/packages/react/src/ActionList/Item.tsx @@ -17,6 +17,7 @@ import {clsx} from 'clsx' import {fixedForwardRef} from '../utils/modern-polymorphic' import {Tooltip} from '../TooltipV2' import {TooltipContext} from '../TooltipV2/TooltipContext' +import {mergeProps} from '../utils/mergeProps' type ActionListSubItemProps = { children?: React.ReactNode @@ -51,9 +52,9 @@ export const SubItem: React.FC = ({children}) => { SubItem.displayName = 'ActionList.SubItem' const ButtonItemContainer = React.forwardRef>( - ({children, style, ...props}, forwardedRef) => { + ({children, ...props}, forwardedRef) => { return ( - ) From 1e0c5a4997acb7daee3987eca42fd6cc71c5654a Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 11 Aug 2026 16:55:25 -0500 Subject: [PATCH 2/2] Preserve ActionList prop behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9541ca7f-0281-4223-a839-39d75393d922 --- .changeset/calm-action-lists-merge.md | 5 ----- packages/react/src/ActionList/Group.tsx | 3 ++- .../src/ActionList/GroupHeadingTrailingAction.tsx | 4 ++-- packages/react/src/ActionList/Heading.tsx | 14 +++++++++----- packages/react/src/ActionList/Item.test.tsx | 10 ++++++++++ packages/react/src/ActionList/Item.tsx | 4 ++-- 6 files changed, 25 insertions(+), 15 deletions(-) delete mode 100644 .changeset/calm-action-lists-merge.md diff --git a/.changeset/calm-action-lists-merge.md b/.changeset/calm-action-lists-merge.md deleted file mode 100644 index ac258866b05..00000000000 --- a/.changeset/calm-action-lists-merge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -ActionList: Preserve component behavior and styling when merging consumer props diff --git a/packages/react/src/ActionList/Group.tsx b/packages/react/src/ActionList/Group.tsx index e508e7893f1..7f18e8a9d76 100644 --- a/packages/react/src/ActionList/Group.tsx +++ b/packages/react/src/ActionList/Group.tsx @@ -70,6 +70,7 @@ export const Group: FCWithSlotMarker { @@ -96,7 +97,7 @@ export const Group: FCWithSlotMarker ( ), ) as PolymorphicForwardRefComponent<'button' | 'a', ActionListGroupHeadingTrailingActionProps> & { diff --git a/packages/react/src/ActionList/Heading.tsx b/packages/react/src/ActionList/Heading.tsx index 3c5920a00bf..3bb1ec3ec26 100644 --- a/packages/react/src/ActionList/Heading.tsx +++ b/packages/react/src/ActionList/Heading.tsx @@ -20,7 +20,7 @@ export type ActionListHeadingProps = { style?: React.CSSProperties } -export const Heading = forwardRef(({as, size, children, visuallyHidden = false, ...props}, forwardedRef) => { +export const Heading = forwardRef(({as, size, children, visuallyHidden = false, className, ...props}, forwardedRef) => { const innerRef = React.useRef(null) const mergedRef = useMergedRefs(forwardedRef, innerRef) @@ -35,6 +35,7 @@ export const Heading = forwardRef(({as, size, children, visuallyHidden = false, return ( {children} diff --git a/packages/react/src/ActionList/Item.test.tsx b/packages/react/src/ActionList/Item.test.tsx index 21d3556e2c9..5a2835e7b8d 100644 --- a/packages/react/src/ActionList/Item.test.tsx +++ b/packages/react/src/ActionList/Item.test.tsx @@ -214,6 +214,16 @@ describe('ActionList.Item', () => { const listItems = container.querySelectorAll('li') expect(listItems.length).toBe(2) }) + it('allows consumers to override the button type', async () => { + const props = {type: 'submit'} as const + const {container} = HTMLRender( + + Item 1 + , + ) + + expect(container.querySelector('button')).toHaveAttribute('type', 'submit') + }) it('should render ActionList.Item as li when item has proper aria role', async () => { const {container} = HTMLRender( diff --git a/packages/react/src/ActionList/Item.tsx b/packages/react/src/ActionList/Item.tsx index 88dce805b95..441f120363e 100644 --- a/packages/react/src/ActionList/Item.tsx +++ b/packages/react/src/ActionList/Item.tsx @@ -51,10 +51,10 @@ export const SubItem: React.FC = ({children}) => { SubItem.displayName = 'ActionList.SubItem' -const ButtonItemContainer = React.forwardRef>( +const ButtonItemContainer = React.forwardRef>( ({children, ...props}, forwardedRef) => { return ( - )