Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor(ActionList): ActionList.Item should render content as a button if parent is not interactive. #3284

Merged
merged 60 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
da778f2
Using button as the underlying tag for ActionList.Item content.
radglob May 9, 2023
6f17814
Fix subnav layout.
radglob May 10, 2023
ea0d12a
Changing ActionList content to button if the top-level is not a butto…
radglob May 11, 2023
ee3e4a1
Fix issue where ActionList.Item content would render as a button insi…
radglob May 12, 2023
c5ae19e
Merge branch 'main' of github.com:primer/react into refactor-actionli…
radglob May 15, 2023
6fa5741
Create purple-crabs-matter.md
radglob May 15, 2023
31d5358
test(vrt): update snapshots
radglob May 15, 2023
3aec9d8
Fix bug with MenuContext not being exported properly.
radglob May 15, 2023
73b9f4f
Fix color violation in ActionList.Item.
radglob May 15, 2023
836ae65
Formatting, update snapshots.
radglob May 15, 2023
08e4496
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 15, 2023
ba6d863
test(vrt): update snapshots
radglob May 15, 2023
e91fb6f
Fix a11y violation in ActionList story.
radglob May 15, 2023
a889d26
Formatting.
radglob May 15, 2023
24463e1
Fix ts-ignore comment.
radglob May 15, 2023
af1d7a0
Adjust line-height when button is rendered.
radglob May 15, 2023
7cb37b9
Revert "test(vrt): update snapshots"
radglob May 15, 2023
dc717d5
Revert "test(vrt): update snapshots"
radglob May 15, 2023
3763951
Update snapshots.
radglob May 15, 2023
0a18def
Remove flexGrow from ActionList.Item to remove extra 1px vertical hei…
radglob May 16, 2023
07630f6
Set padding to 0 and put flexGrow back to fix padding issue for Actio…
radglob May 16, 2023
532c7c2
Fix ActionList text wrap story.
radglob May 16, 2023
5d692fd
Fix underlinenav story by checking ActionList.Item if as prop is a.
radglob May 16, 2023
7beeee3
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 16, 2023
cdc8ebd
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 16, 2023
a1f3abb
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 16, 2023
ef12dc8
Update snapshots and formatting.
radglob May 16, 2023
fabb84c
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 17, 2023
6e6ccdd
Update snapshots.
radglob May 17, 2023
3f03a12
If ActionList.Item content is rendered as a button, remove tabIndex f…
radglob May 19, 2023
ff27e8d
Fix various layout edge cases.
radglob May 22, 2023
6a63ddd
ts-ignore event handlers on ItemWrapper.
radglob May 22, 2023
212bee9
Update snapshots.
radglob May 22, 2023
1a32b5a
Merge branch 'main' of github.com:primer/react into refactor-actionli…
radglob May 22, 2023
bf8602d
Revert fontWeight config that differed from production docs.
radglob May 22, 2023
e12bc40
Pass selectionVariant prop illegally in UnderlineNav story to fix iss…
radglob May 23, 2023
43c1606
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 23, 2023
a7478a5
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 23, 2023
4af4dbf
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 23, 2023
7525135
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 25, 2023
388426e
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 25, 2023
31f965a
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 26, 2023
7020729
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 26, 2023
6d74cbf
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 30, 2023
7e32d58
Merge branch 'main' into refactor-actionlist-item-as-button
radglob May 30, 2023
be3676e
fix: prevent closing menu when `event.preventDefault()` is called on …
radglob May 31, 2023
e8449c8
Merge branch 'main' of github.com:primer/react into refactor-actionli…
radglob May 31, 2023
fe0825a
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 1, 2023
d7fe4a9
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 1, 2023
2a624e6
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 5, 2023
44faa16
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 5, 2023
ecd0a34
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 5, 2023
e0d5c14
Merge branch 'main' of github.com:primer/react into refactor-actionli…
radglob Jun 6, 2023
afd0bec
Check for tabIndex value in isTopLevelInteractive.
radglob Jun 6, 2023
9903c8d
Reference styles in updated menuProps.
radglob Jun 6, 2023
fae7d6c
Updated snapshots.
radglob Jun 6, 2023
9cb4d31
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 6, 2023
3f416be
Fix padding setting instead of using values from styles, which are in…
radglob Jun 6, 2023
8455a42
Merge branch 'main' into refactor-actionlist-item-as-button
radglob Jun 6, 2023
2725253
Update snapshots.
radglob Jun 6, 2023
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
5 changes: 5 additions & 0 deletions .changeset/purple-crabs-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

Refactor(ActionList): ActionList.Item should render content as a button if parent is not interactive.
1 change: 0 additions & 1 deletion src/ActionList/Description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const Description: React.FC<React.PropsWithChildren<ActionListDescription
sx={merge({...styles, color: disabled ? 'fg.disabled' : 'fg.muted'}, sx as SxProp)}
title={props.children as string}
inline={true}
maxWidth="100%"
>
{props.children}
</Truncate>
Expand Down
59 changes: 48 additions & 11 deletions src/ActionList/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import {defaultSxProp} from '../utils/defaultSxProp'
import {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
import {ActionListContainerContext} from './ActionListContainerContext'
import {Description} from './Description'
import {ActionListProps, ListContext} from './List'
import {ListContext} from './List'
import {Selection} from './Selection'
import {ActionListItemProps, getVariantStyles, ItemContext, TEXT_ROW_HEIGHT} from './shared'
import {LeadingVisual, TrailingVisual} from './Visuals'
import {MenuContext} from '../ActionMenu/ActionMenu'
import {GroupContext} from './Group'

const LiBox = styled.li<SxProp>(sx)
Expand All @@ -38,10 +39,13 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
trailingVisual: TrailingVisual,
description: Description,
})

const {variant: listVariant, showDividers, selectionVariant: listSelectionVariant} = React.useContext(ListContext)
const {container, afterSelect, selectionAttribute} = React.useContext(ActionListContainerContext)
const menuContext = React.useContext(MenuContext)
const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext)

const selectionVariant = groupSelectionVariant ?? listSelectionVariant
const onSelect = React.useCallback(
(
event: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLLIElement>,
Expand All @@ -55,10 +59,6 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
[onSelectUser],
)

const selectionVariant: ActionListProps['selectionVariant'] = groupSelectionVariant
? groupSelectionVariant
: listSelectionVariant

/** Infer item role based on the container */
let itemRole: ActionListItemProps['role']
if (container === 'ActionMenu' || container === 'DropdownMenu') {
Expand All @@ -84,12 +84,21 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
},
}

const isTopLevelInteractive = () =>
radglob marked this conversation as resolved.
Show resolved Hide resolved
radglob marked this conversation as resolved.
Show resolved Hide resolved
_PrivateItemWrapper !== undefined ||
// @ts-ignore props.as may be defined, may not.
props.as === 'button' ||
// @ts-ignore props.as may be defined, may not.
props.as === 'a' ||
menuContext.anchorId !== undefined ||
role?.match(/menuitem/)

const styles = {
position: 'relative',
display: 'flex',
paddingX: 2,
paddingX: isTopLevelInteractive() ? 2 : 0,
fontSize: 1,
paddingY: '6px', // custom value off the scale
paddingY: isTopLevelInteractive() ? '6px' : 0, // custom value off the scale
lineHeight: TEXT_ROW_HEIGHT,
minHeight: 5,
marginX: listVariant === 'inset' ? 2 : 0,
Expand Down Expand Up @@ -145,6 +154,10 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
borderTopWidth: showDividers ? `1px` : '0',
borderColor: 'var(--divider-color, transparent)',
},
'button[data-component="ActionList.Item--DividerContainer"]': {
textAlign: 'left',
padding: 0,
},
// show between 2 items
':not(:first-of-type)': {'--divider-color': theme?.colors.actionListItem.inlineDivider},
// hide divider after dividers & group header, with higher importance!
Expand Down Expand Up @@ -182,13 +195,13 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
const inlineDescriptionId = useId(id && `${id}--inline-description`)
const blockDescriptionId = useId(id && `${id}--block-description`)

const ItemWrapper = _PrivateItemWrapper || React.Fragment
const ItemWrapper = _PrivateItemWrapper || Box

const menuItemProps = {
onClick: clickHandler,
onKeyPress: keyPressHandler,
'aria-disabled': disabled ? true : undefined,
tabIndex: disabled ? undefined : 0,
tabIndex: disabled || !isTopLevelInteractive() ? undefined : 0,
'aria-labelledby': `${labelId} ${
slots.description && slots.description.props.variant !== 'block' ? inlineDescriptionId : ''
}`,
Expand All @@ -199,17 +212,41 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(

const containerProps = _PrivateItemWrapper ? {role: role || itemRole ? 'none' : undefined} : menuItemProps

const wrapperProps = _PrivateItemWrapper ? menuItemProps : {}
const wrapperProps = _PrivateItemWrapper
? menuItemProps
: {
sx: {
display: 'flex',
paddingX: isTopLevelInteractive() ? 0 : 2,
paddingY: isTopLevelInteractive() ? 0 : '6px',
radglob marked this conversation as resolved.
Show resolved Hide resolved
flexGrow: 1,
},
}

return (
<ItemContext.Provider value={{variant, disabled, inlineDescriptionId, blockDescriptionId}}>
<LiBox ref={forwardedRef} sx={merge<BetterSystemStyleObject>(styles, sxProp)} {...containerProps} {...props}>
{/* @ts-ignore onClick prop is only passed when _PrivateItemWrapper is set by ActionList.LinkItem. */}
radglob marked this conversation as resolved.
Show resolved Hide resolved
<ItemWrapper {...wrapperProps}>
<Selection selected={selected} />
{slots.leadingVisual}
<Box
data-component="ActionList.Item--DividerContainer"
sx={{display: 'flex', flexDirection: 'column', flexGrow: 1, minWidth: 0}}
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
minWidth: 0,
borderStyle: 'none',
backgroundColor: 'transparent',
cursor: 'inherit',
fontSize: 'inherit',
color: getVariantStyles(variant, disabled).color,
lineHeight: '20px',
}}
// @ts-ignore `as` prop may be passed to ActionList.Item, even if it isn't defined in ActionListItemProps.
// If this item is inside an ActionMenu, don't render an interactive button.
as={isTopLevelInteractive() ? 'div' : 'button'}
radglob marked this conversation as resolved.
Show resolved Hide resolved
>
<ConditionalBox if={Boolean(slots.trailingVisual)} sx={{display: 'flex', flexGrow: 1}}>
<ConditionalBox
Expand Down
138 changes: 0 additions & 138 deletions src/ActionMenu.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'
import {axe, toHaveNoViolations} from 'jest-axe'
import React from 'react'
import theme from '../theme'
import {ActionMenu, ActionList, BaseStyles, ThemeProvider, SSRProvider} from '..'
import {ActionMenu, MenuContext, ActionList, BaseStyles, ThemeProvider, SSRProvider} from '..'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {SingleSelect} from '../ActionMenu/ActionMenu.features.stories'
import {MixedSelection} from '../ActionMenu/ActionMenu.examples.stories'
Expand Down Expand Up @@ -47,6 +47,7 @@ describe('ActionMenu', () => {
checkExports('ActionMenu', {
default: undefined,
ActionMenu,
MenuContext,
})

it('should open Menu on MenuButton click', async () => {
Expand Down
3 changes: 2 additions & 1 deletion src/ActionMenu/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export type MenuContextProps = Pick<
> & {
onClose?: (gesture: 'anchor-click' | 'click-outside' | 'escape' | 'tab') => void
}
const MenuContext = React.createContext<MenuContextProps>({renderAnchor: null, open: false})

export const MenuContext = React.createContext<MenuContextProps>({renderAnchor: null, open: false})

export type ActionMenuProps = {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/NavList/NavList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ describe('NavList.Item with NavList.SubNav', () => {
</ThemeProvider>,
)

const button = getByRole('button')
const button = getByRole('button', {name: 'Item'})

// Starts open
expect(queryByRole('list', {name: 'Item'})).toBeVisible()
Expand Down
Loading