Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions packages/react/src/ActionList/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -28,11 +29,7 @@ const Heading: React.FC<HeadingProps & React.HTMLAttributes<HTMLHeadingElement>>
id,
...rest
}) => {
return (
<Component className={className} id={id} {...rest}>
{children}
</Component>
)
return <Component {...mergeProps({className, id}, rest)}>{children}</Component>
}

type HeadingWrapProps = {
Expand Down Expand Up @@ -98,10 +95,14 @@ export const Group: FCWithSlotMarker<React.PropsWithChildren<ActionListGroupProp

return (
<li
className={clsx(className, groupClasses.Group)}
data-component="ActionList.Group"
role={listRole ? 'none' : undefined}
{...props}
{...mergeProps(
{
className: clsx(groupClasses.Group, className),
'data-component': 'ActionList.Group',
role: listRole ? 'none' : undefined,
},
props,
)}
>
<GroupContext.Provider value={{selectionVariant, groupHeadingId}}>
{title && !slots.groupHeading ? (
Expand Down
23 changes: 14 additions & 9 deletions packages/react/src/ActionList/GroupHeadingTrailingAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -21,17 +22,21 @@ export type ActionListGroupHeadingTrailingActionProps = Omit<ActionListTrailingA
const GroupHeadingTrailingActionImpl = forwardRef(
({as = 'button', icon, label, href = null, tooltipDirection = 'w', ...props}, forwardedRef) => (
<IconButton
as={as}
aria-label={label}
icon={icon}
variant="invisible"
size="small"
tooltipDirection={tooltipDirection}
href={href}
// @ts-expect-error StyledButton wants both Anchor and Button refs
ref={forwardedRef}
data-component="ActionList.GroupHeading.TrailingAction"
{...props}
{...mergeProps(
{
as,
'aria-label': label,
icon,
variant: 'invisible' as const,
size: 'small' as const,
tooltipDirection,
href,
'data-component': 'ActionList.GroupHeading.TrailingAction',
},
props,
)}
/>
),
) as PolymorphicForwardRefComponent<'button' | 'a', ActionListGroupHeadingTrailingActionProps> & {
Expand Down
33 changes: 21 additions & 12 deletions packages/react/src/ActionList/Heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -34,19 +35,27 @@ export const Heading = forwardRef(({as, size, children, visuallyHidden = false,

return (
<HeadingComponent
as={as}
variant={size}
ref={mergedRef}
// use custom id if it is provided. Otherwise, use the id from the context
id={props.id ?? headingId}
// Apply the visually-hidden styles directly to the heading rather than wrapping
// it in a span (a heading isn't valid phrasing content inside a span).
className={clsx(className, classes.ActionListHeader, {
[visuallyHiddenClasses.InternalVisuallyHidden]: visuallyHidden,
})}
data-component="ActionList.Heading"
data-list-variant={listVariant}
{...props}
as={as}
{...mergeProps(
{
variant: size,
// use custom id if it is provided. Otherwise, use the id from the context
id: props.id ?? headingId,
// Apply the visually-hidden styles directly to the heading rather than wrapping
// it in a span (a heading isn't valid phrasing content inside a span).
className: clsx(
classes.ActionListHeader,
{
[visuallyHiddenClasses.InternalVisuallyHidden]: visuallyHidden,
},
className,
),
'data-component': 'ActionList.Heading',
'data-list-variant': listVariant,
},
props,
)}
>
{children}
</HeadingComponent>
Expand Down
11 changes: 11 additions & 0 deletions packages/react/src/ActionList/Item.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,22 @@ 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')
expect(listItems.length).toBe(2)
})
it('allows consumers to override the button type', async () => {
const props = {type: 'submit'} as const
const {container} = HTMLRender(
<ActionList>
<ActionList.Item {...props}>Item 1</ActionList.Item>
</ActionList>,
)

expect(container.querySelector('button')).toHaveAttribute('type', 'submit')
})
it('should render ActionList.Item as li when item has proper aria role', async () => {
const {container} = HTMLRender(
<ActionList role="listbox">
Expand Down
7 changes: 4 additions & 3 deletions packages/react/src/ActionList/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -50,10 +51,10 @@ export const SubItem: React.FC<ActionListSubItemProps> = ({children}) => {

SubItem.displayName = 'ActionList.SubItem'

const ButtonItemContainer = React.forwardRef<HTMLButtonElement, React.HTMLAttributes<HTMLButtonElement>>(
({children, style, ...props}, forwardedRef) => {
const ButtonItemContainer = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
({children, ...props}, forwardedRef) => {
return (
<button type="button" ref={forwardedRef as React.Ref<HTMLButtonElement>} style={style} {...props}>
<button ref={forwardedRef as React.Ref<HTMLButtonElement>} type="button" {...mergeProps({}, props)}>
{children}
</button>
)
Expand Down
Loading