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

DropdownMenu V2 tweaks #56041

Merged
merged 23 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

## Unreleased

### Enhancements
### Enhancements

- `Button`: Add focus rings to focusable disabled buttons ([#56383](https://github.com/WordPress/gutenberg/pull/56383)).

### Experimental

- `Tabs`: Memoize and expose the component context ([#56224](https://github.com/WordPress/gutenberg/pull/56224)).
- `DropdownMenuV2`: Design tweaks ([#56041](https://github.com/WordPress/gutenberg/pull/56041))

### Internal

Expand Down
24 changes: 19 additions & 5 deletions packages/components/src/dropdown-menu-v2-ariakit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,23 +284,37 @@ Event handler called when the checked radio menu item changes.

- Required: no

### `DropdownMenuGroup`
### `DropdownMenuItemLabel`

Used to group menu items.
Used to render the menu item's label.

#### Props

The component accepts the following props:

##### `children`: `React.ReactNode`

The contents of the group.
The label contents.

- Required: yes

### `DropdownMenuItemHelpText`

Used to render the menu item's help text.

#### Props

The component accepts the following props:

##### `children`: `React.ReactNode`

The help text contents.

- Required: yes

### `DropdownMenuGroupLabel`
### `DropdownMenuGroup`

Used to render a group label.
Used to group menu items.

#### Props

Expand Down
117 changes: 82 additions & 35 deletions packages/components/src/dropdown-menu-v2-ariakit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type {
DropdownMenuContext as DropdownMenuContextType,
DropdownMenuProps,
DropdownMenuGroupProps,
DropdownMenuGroupLabelProps,
DropdownMenuItemProps,
DropdownMenuCheckboxItemProps,
DropdownMenuRadioItemProps,
Expand All @@ -55,16 +54,23 @@ export const DropdownMenuItem = forwardRef<
<Styled.DropdownMenuItem
ref={ ref }
{ ...props }
accessibleWhenDisabled
hideOnClick={ hideOnClick }
store={ dropdownMenuContext?.store }
>
{ prefix && (
<Styled.ItemPrefixWrapper>{ prefix }</Styled.ItemPrefixWrapper>
) }
{ children }
{ suffix && (
<Styled.ItemSuffixWrapper>{ suffix }</Styled.ItemSuffixWrapper>
) }
<Styled.ItemPrefixWrapper>{ prefix }</Styled.ItemPrefixWrapper>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prefix wrapper is always rendered (even when empty) in order to correctly render in the first column of the grid layout.


<Styled.DropdownMenuItemContentWrapper>
<Styled.DropdownMenuItemChildrenWrapper>
{ children }
</Styled.DropdownMenuItemChildrenWrapper>

{ suffix && (
<Styled.ItemSuffixWrapper>
{ suffix }
</Styled.ItemSuffixWrapper>
) }
</Styled.DropdownMenuItemContentWrapper>
</Styled.DropdownMenuItem>
);
} );
Expand All @@ -82,20 +88,30 @@ export const DropdownMenuCheckboxItem = forwardRef<
<Styled.DropdownMenuCheckboxItem
ref={ ref }
{ ...props }
accessibleWhenDisabled
hideOnClick={ hideOnClick }
store={ dropdownMenuContext?.store }
>
<Ariakit.MenuItemCheck
store={ dropdownMenuContext?.store }
render={ <Styled.ItemPrefixWrapper /> }
// Override some ariakit inline styles
style={ { width: 'auto', height: 'auto' } }
>
<Icon icon={ check } size={ 24 } />
</Ariakit.MenuItemCheck>

{ children }
{ suffix && (
<Styled.ItemSuffixWrapper>{ suffix }</Styled.ItemSuffixWrapper>
) }
<Styled.DropdownMenuItemContentWrapper>
<Styled.DropdownMenuItemChildrenWrapper>
{ children }
</Styled.DropdownMenuItemChildrenWrapper>

{ suffix && (
<Styled.ItemSuffixWrapper>
{ suffix }
</Styled.ItemSuffixWrapper>
) }
</Styled.DropdownMenuItemContentWrapper>
</Styled.DropdownMenuCheckboxItem>
);
} );
Expand All @@ -119,17 +135,30 @@ export const DropdownMenuRadioItem = forwardRef<
<Styled.DropdownMenuRadioItem
ref={ ref }
{ ...props }
accessibleWhenDisabled
hideOnClick={ hideOnClick }
store={ dropdownMenuContext?.store }
>
<Ariakit.MenuItemCheck
store={ dropdownMenuContext?.store }
render={ <Styled.ItemPrefixWrapper /> }
// Override some ariakit inline styles
style={ { width: 'auto', height: 'auto' } }
>
<Icon icon={ radioCheck } size={ 24 } />
</Ariakit.MenuItemCheck>
{ children }
{ suffix }

<Styled.DropdownMenuItemContentWrapper>
<Styled.DropdownMenuItemChildrenWrapper>
{ children }
</Styled.DropdownMenuItemChildrenWrapper>

{ suffix && (
<Styled.ItemSuffixWrapper>
{ suffix }
</Styled.ItemSuffixWrapper>
) }
</Styled.DropdownMenuItemContentWrapper>
</Styled.DropdownMenuRadioItem>
);
} );
Expand All @@ -148,20 +177,6 @@ export const DropdownMenuGroup = forwardRef<
);
} );

export const DropdownMenuGroupLabel = forwardRef<
HTMLDivElement,
WordPressComponentProps< DropdownMenuGroupLabelProps, 'div', false >
>( function DropdownMenuGroupLabel( props, ref ) {
const dropdownMenuContext = useContext( DropdownMenuContext );
return (
<Styled.DropdownMenuGroupLabel
ref={ ref }
{ ...props }
store={ dropdownMenuContext?.store }
/>
);
} );

const UnconnectedDropdownMenu = (
props: WordPressComponentProps< DropdownMenuProps, 'div', false >,
ref: React.ForwardedRef< HTMLDivElement >
Expand Down Expand Up @@ -280,12 +295,16 @@ const UnconnectedDropdownMenu = (
dropdownMenuStore.parent
? cloneElement( trigger, {
// Add submenu arrow, unless a `suffix` is explicitly specified
suffix: trigger.props.suffix ?? (
<Styled.SubmenuChevronIcon
aria-hidden="true"
icon={ chevronRightSmall }
size={ 24 }
/>
suffix: (
<>
{ trigger.props.suffix }
<Styled.SubmenuChevronIcon
aria-hidden="true"
icon={ chevronRightSmall }
size={ 24 }
preserveAspectRatio="xMidYMid slice"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preserveAspectRatio is used to resize the width of the icon while preserving its real size (ie. slicing the SVG viewport)

/>
</>
),
} )
: trigger
Expand All @@ -297,7 +316,9 @@ const UnconnectedDropdownMenu = (
{ ...otherProps }
modal={ modal }
store={ dropdownMenuStore }
gutter={ gutter ?? ( dropdownMenuStore.parent ? 16 : 8 ) }
// Nested menus overlap by 8px
gutter={ gutter ?? ( dropdownMenuStore.parent ? 0 : 8 ) }
// Nested menus have their items aligned horizontally
shift={ shift ?? ( dropdownMenuStore.parent ? -8 : 0 ) }
hideOnHoverOutside={ false }
data-side={ appliedPlacementSide }
Expand Down Expand Up @@ -332,3 +353,29 @@ export const DropdownMenuSeparator = forwardRef<
/>
);
} );

export const DropdownMenuItemLabel = forwardRef<
HTMLSpanElement,
WordPressComponentProps< { children: React.ReactNode }, 'span', true >
>( function DropdownMenuItemLabel( props, ref ) {
return (
<Styled.DropdownMenuItemLabel
numberOfLines={ 1 }
ref={ ref }
{ ...props }
/>
);
} );

export const DropdownMenuItemHelpText = forwardRef<
HTMLSpanElement,
WordPressComponentProps< { children: React.ReactNode }, 'span', true >
>( function DropdownMenuItemHelpText( props, ref ) {
return (
<Styled.DropdownMenuItemHelpText
numberOfLines={ 2 }
ref={ ref }
{ ...props }
/>
);
} );
Loading
Loading