diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 9a3beb03423964..3fb47c5e3b3ec6 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -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 diff --git a/packages/components/src/dropdown-menu-v2-ariakit/README.md b/packages/components/src/dropdown-menu-v2-ariakit/README.md index f74098efff4103..2902b541169766 100644 --- a/packages/components/src/dropdown-menu-v2-ariakit/README.md +++ b/packages/components/src/dropdown-menu-v2-ariakit/README.md @@ -284,9 +284,9 @@ 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 @@ -294,13 +294,27 @@ 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 diff --git a/packages/components/src/dropdown-menu-v2-ariakit/index.tsx b/packages/components/src/dropdown-menu-v2-ariakit/index.tsx index 10b93d8c552c13..37d4a1f9cfcc5e 100644 --- a/packages/components/src/dropdown-menu-v2-ariakit/index.tsx +++ b/packages/components/src/dropdown-menu-v2-ariakit/index.tsx @@ -30,7 +30,6 @@ import type { DropdownMenuContext as DropdownMenuContextType, DropdownMenuProps, DropdownMenuGroupProps, - DropdownMenuGroupLabelProps, DropdownMenuItemProps, DropdownMenuCheckboxItemProps, DropdownMenuRadioItemProps, @@ -55,16 +54,23 @@ export const DropdownMenuItem = forwardRef< - { prefix && ( - { prefix } - ) } - { children } - { suffix && ( - { suffix } - ) } + { prefix } + + + + { children } + + + { suffix && ( + + { suffix } + + ) } + ); } ); @@ -82,20 +88,30 @@ export const DropdownMenuCheckboxItem = forwardRef< } + // Override some ariakit inline styles + style={ { width: 'auto', height: 'auto' } } > - { children } - { suffix && ( - { suffix } - ) } + + + { children } + + + { suffix && ( + + { suffix } + + ) } + ); } ); @@ -119,17 +135,30 @@ export const DropdownMenuRadioItem = forwardRef< } + // Override some ariakit inline styles + style={ { width: 'auto', height: 'auto' } } > - { children } - { suffix } + + + + { children } + + + { suffix && ( + + { suffix } + + ) } + ); } ); @@ -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 ( - - ); -} ); - const UnconnectedDropdownMenu = ( props: WordPressComponentProps< DropdownMenuProps, 'div', false >, ref: React.ForwardedRef< HTMLDivElement > @@ -280,12 +295,16 @@ const UnconnectedDropdownMenu = ( dropdownMenuStore.parent ? cloneElement( trigger, { // Add submenu arrow, unless a `suffix` is explicitly specified - suffix: trigger.props.suffix ?? ( - + suffix: ( + <> + { trigger.props.suffix } + + > ), } ) : trigger @@ -297,8 +316,12 @@ const UnconnectedDropdownMenu = ( { ...otherProps } modal={ modal } store={ dropdownMenuStore } - gutter={ gutter ?? ( dropdownMenuStore.parent ? 16 : 8 ) } - shift={ shift ?? ( dropdownMenuStore.parent ? -8 : 0 ) } + // Root menu has an 8px distance from its trigger, + // otherwise 0 (which causes the submenu to slightly overlap) + gutter={ gutter ?? ( dropdownMenuStore.parent ? 0 : 8 ) } + // Align nested menu by the same (but opposite) amount + // as the menu container's padding. + shift={ shift ?? ( dropdownMenuStore.parent ? -4 : 0 ) } hideOnHoverOutside={ false } data-side={ appliedPlacementSide } variant={ variant } @@ -332,3 +355,29 @@ export const DropdownMenuSeparator = forwardRef< /> ); } ); + +export const DropdownMenuItemLabel = forwardRef< + HTMLSpanElement, + WordPressComponentProps< { children: React.ReactNode }, 'span', true > +>( function DropdownMenuItemLabel( props, ref ) { + return ( + + ); +} ); + +export const DropdownMenuItemHelpText = forwardRef< + HTMLSpanElement, + WordPressComponentProps< { children: React.ReactNode }, 'span', true > +>( function DropdownMenuItemHelpText( props, ref ) { + return ( + + ); +} ); diff --git a/packages/components/src/dropdown-menu-v2-ariakit/stories/index.story.tsx b/packages/components/src/dropdown-menu-v2-ariakit/stories/index.story.tsx index a6319c6cfdc932..fcc8f76294bc6b 100644 --- a/packages/components/src/dropdown-menu-v2-ariakit/stories/index.story.tsx +++ b/packages/components/src/dropdown-menu-v2-ariakit/stories/index.story.tsx @@ -2,28 +2,28 @@ * External dependencies */ import type { Meta, StoryFn } from '@storybook/react'; -import styled from '@emotion/styled'; import { css } from '@emotion/react'; /** * WordPress dependencies */ -import { wordpress } from '@wordpress/icons'; +import { customLink, formatCapitalize } from '@wordpress/icons'; import { useState, useMemo, useContext } from '@wordpress/element'; /** * Internal dependencies */ -import { COLORS, useCx } from '../../utils'; +import { useCx } from '../../utils'; import { DropdownMenu, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuGroup, - DropdownMenuGroupLabel, DropdownMenuSeparator, DropdownMenuContext, DropdownMenuRadioItem, + DropdownMenuItemLabel, + DropdownMenuItemHelpText, } from '..'; import Icon from '../../icon'; import Button from '../../button'; @@ -37,6 +37,20 @@ const meta: Meta< typeof DropdownMenu > = { subcomponents: { // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 DropdownMenuItem, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuCheckboxItem, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuGroup, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuSeparator, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuContext, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuRadioItem, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuItemLabel, + // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170 + DropdownMenuItemHelpText, }, argTypes: { children: { control: { type: null } }, @@ -65,54 +79,48 @@ const meta: Meta< typeof DropdownMenu > = { }; export default meta; -const ItemHelpText = styled.span` - font-size: 12px; - color: ${ COLORS.gray[ '700' ] }; - - /* when the immediate parent item is hovered / focused */ - [data-active-item] > * > &, - /* when the parent item is a submenu trigger and the submenu is open */ - [aria-expanded='true'] > &, - /* when the parent item is disabled */ - [aria-disabled='true'] > & { - color: inherit; - } -`; - export const Default: StoryFn< typeof DropdownMenu > = ( props ) => ( - Default item + + Label + + + Label + Help text + + + Label + + The menu item help text is automatically truncated when there + are more than two lines of text + + - - Other item - - Won't close the menu when clicked - - + Label + + This item doesn't close the menu on click + Disabled item - Prefix and suffix } + prefix={ } > - With prefix - - ⌘S }> - With suffix + With prefix + With suffix } - suffix={ ⌥⌘T } + prefix={ } + suffix="⌥⌘T" > - Disabled with prefix and suffix + + Disabled with prefix and suffix + + + And help text + @@ -129,15 +137,35 @@ export const WithSubmenu: StoryFn< typeof DropdownMenu > = ( props ) => ( Level 1 item Submenu trigger } + trigger={ + + + Submenu trigger item with a long label + + + } > - Level 2 item - Level 2 item + + Level 2 item + + + Level 2 item + Submenu trigger } + trigger={ + + + Submenu trigger + + + } > - Level 3 item - Level 3 item + + Level 3 item + + + Level 3 item + @@ -167,35 +195,45 @@ export const WithCheckboxes: StoryFn< typeof DropdownMenu > = ( props ) => { return ( - - Individual, uncontrolled checkboxes - - Checkbox item A (initially unchecked) + + Checkbox item A + + + Uncontrolled + - Checkbox item B (initially checked) + + Checkbox item B + + + Uncontrolled, initially checked + - - Individual, controlled checkboxes - setAChecked( e.target.checked ) } > - Checkbox item A + + Checkbox item A + + + Controlled + = ( props ) => { checked={ isBChecked } onChange={ ( e ) => setBChecked( e.target.checked ) } > - Checkbox item B (initially checked) + + Checkbox item B + + + Controlled, initially checked + - - Multiple, uncontrolled checkboxes - - Checkbox item A (initially unchecked) + + Checkbox item A + + + Uncontrolled, multiple selection + - Checkbox item B (initially checked) + + Checkbox item B + + + Uncontrolled, multiple selection, initially checked + - - Multiple, controlled checkboxes - - Checkbox item A (initially unchecked) + + Checkbox item A + + + Controlled, multiple selection + = ( props ) => { checked={ multipleCheckboxesValue.includes( 'b' ) } onChange={ onMultipleCheckboxesCheckedChange } > - Checkbox item B (initially checked) + + Checkbox item B + + + Controlled, multiple selection, initially checked + @@ -263,32 +320,35 @@ export const WithRadios: StoryFn< typeof DropdownMenu > = ( props ) => { return ( - - Uncontrolled radios - - Radio item 1 + Radio item 1 + + Uncontrolled + - Radio item 2 (initially checked) + Radio item 2 + + Uncontrolled, initially checked + - - Controlled radios - - Radio item 1 + Radio item 1 + + Controlled + = ( props ) => { checked={ radioValue === 'two' } onChange={ onRadioChange } > - Radio item 2 (initially checked) + Radio item 2 + + Controlled, initially checked + @@ -327,13 +390,17 @@ export const WithModals: StoryFn< typeof DropdownMenu > = ( props ) => { onClick={ () => setOuterModalOpen( true ) } hideOnClick={ false } > - Open outer modal + + Open outer modal + setInnerModalOpen( true ) } hideOnClick={ false } > - Open inner modal + + Open inner modal + { isInnerModalOpen && ( { [ dropdownMenuContext ] ); - return ; + return ( + + ); }; type ForwardedContextTuple< P = {} > = [ @@ -414,18 +487,32 @@ export const WithSlotFill: StoryFn< typeof DropdownMenu > = ( props ) => { return ( - Item + + Item + - Item from fill + + + Item from fill + + Submenu from fill + + + Submenu from fill + + } > - Submenu item from fill + + + Submenu item from fill + + @@ -441,15 +528,28 @@ const toolbarVariantContextValue = { }, }; export const ToolbarVariant: StoryFn< typeof DropdownMenu > = ( props ) => ( + // TODO: add toolbar - Level 1 item - Level 1 item + + Level 1 item + + + Level 1 item + Submenu trigger } + trigger={ + + + Submenu trigger + + + } > - Level 2 item + + Level 2 item + @@ -472,17 +572,31 @@ export const InsideModal: StoryFn< typeof DropdownMenu > = ( props ) => { { isModalOpen && ( setModalOpen( false ) }> - Level 1 item - Level 1 item + + + Level 1 item + + + + + Level 1 item + + - Submenu trigger + + Submenu trigger + } > - Level 2 item + + + Level 2 item + + setModalOpen( false ) }> diff --git a/packages/components/src/dropdown-menu-v2-ariakit/styles.ts b/packages/components/src/dropdown-menu-v2-ariakit/styles.ts index 0858bba4289efd..465bdb1aebb30e 100644 --- a/packages/components/src/dropdown-menu-v2-ariakit/styles.ts +++ b/packages/components/src/dropdown-menu-v2-ariakit/styles.ts @@ -12,6 +12,7 @@ import styled from '@emotion/styled'; import { COLORS, font, rtl, CONFIG } from '../utils'; import { space } from '../utils/space'; import Icon from '../icon'; +import { Truncate } from '../truncate'; import type { DropdownMenuContext } from './types'; const ANIMATION_PARAMS = { @@ -20,13 +21,16 @@ const ANIMATION_PARAMS = { EASING: 'cubic-bezier( 0.16, 1, 0.3, 1 )', }; -const CONTENT_WRAPPER_PADDING = space( 2 ); -const ITEM_PREFIX_WIDTH = space( 7 ); -const ITEM_PADDING_INLINE_START = space( 2 ); -const ITEM_PADDING_INLINE_END = space( 2.5 ); +const CONTENT_WRAPPER_PADDING = space( 1 ); +const ITEM_PADDING_BLOCK = space( 2 ); +const ITEM_PADDING_INLINE = space( 3 ); -// TODO: should bring this into the config, and make themeable -const DEFAULT_BORDER_COLOR = COLORS.ui.borderDisabled; +// TODO: +// - those values are different from saved variables? +// - should bring this into the config, and make themeable +// - border color and divider color are different? +const DEFAULT_BORDER_COLOR = COLORS.gray[ 300 ]; +const DIVIDER_COLOR = COLORS.gray[ 200 ]; const TOOLBAR_VARIANT_BORDER_COLOR = COLORS.gray[ '900' ]; const DEFAULT_BOX_SHADOW = `0 0 0 ${ CONFIG.borderWidth } ${ DEFAULT_BORDER_COLOR }, ${ CONFIG.popoverShadow }`; const TOOLBAR_VARIANT_BOX_SHADOW = `0 0 0 ${ CONFIG.borderWidth } ${ TOOLBAR_VARIANT_BORDER_COLOR }`; @@ -71,12 +75,18 @@ export const DropdownMenu = styled( Ariakit.Menu )< /* TODO: is there a way to read the sass variable? */ z-index: 1000000; - min-width: 220px; + display: grid; + grid-template-columns: minmax( 0, max-content ) 1fr; + grid-template-rows: auto; + + box-sizing: border-box; + min-width: 160px; + max-width: 320px; max-height: var( --popover-available-height ); padding: ${ CONTENT_WRAPPER_PADDING }; background-color: ${ COLORS.ui.background }; - border-radius: ${ CONFIG.radiusBlockUi }; + border-radius: 4px; ${ ( props ) => css` box-shadow: ${ props.variant === 'toolbar' ? TOOLBAR_VARIANT_BOX_SHADOW @@ -110,102 +120,55 @@ export const DropdownMenu = styled( Ariakit.Menu )< } `; -const itemPrefix = css` - /* !important is to override some inline styles set by Ariakit */ - width: ${ ITEM_PREFIX_WIDTH } !important; - /* !important is to override some inline styles set by Ariakit */ - height: auto !important; - display: inline-flex; - align-items: center; - justify-content: center; - /* Prefixes don't get affected by the item's inline start padding */ - margin-inline-start: calc( -1 * ${ ITEM_PADDING_INLINE_START } ); - /* - Negative margin allows the suffix to be as tall as the whole item - (incl. padding) before increasing the items' height. This can be useful, - e.g., when using icons that are bigger than 20x20 px - */ - margin-top: ${ space( -2 ) }; - margin-bottom: ${ space( -2 ) }; -`; - -const itemSuffix = css` - width: max-content; - display: inline-flex; - align-items: center; - justify-content: center; - /* Push prefix to the inline-end of the item */ - margin-inline-start: auto; - /* Minimum space between the item's content and suffix */ - padding-inline-start: ${ space( 6 ) }; - /* - Negative margin allows the suffix to be as tall as the whole item - (incl. padding) before increasing the items' height. This can be useful, - e.g., when using icons that are bigger than 20x20 px - */ - margin-top: ${ space( -2 ) }; - margin-bottom: ${ space( -2 ) }; - - /* - Override color in normal conditions, but inherit the item's color - for altered conditions. - - TODO: - - For now, used opacity like for disabled item, which makes it work - regardless of the theme - - how do we translate this for themes? Should we have a new variable - for "secondary" text? - */ - opacity: 0.6; +const baseItem = css` + all: unset; - /* when the parent item is hovered / focused */ - [data-active-item] > &, - /* when the parent item is a submenu trigger and the submenu is open */ - [aria-expanded='true'] > &, - /* when the parent item is disabled */ - [aria-disabled='true'] > & { - opacity: 1; - } -`; + position: relative; + min-height: ${ space( 10 ) }; + box-sizing: border-box; -export const ItemPrefixWrapper = styled.span` - ${ itemPrefix } -`; + /* Occupy the width of all grid columns (ie. full width) */ + grid-column: 1 / -1; -export const ItemSuffixWrapper = styled.span` - ${ itemSuffix } -`; + /* + * Define a grid layout which inherits the same columns configuration + * from the parent layout (ie. subgrid). + */ + display: grid; + grid-template-columns: subgrid; + align-items: center; -const baseItem = css` - all: unset; font-size: ${ font( 'default.fontSize' ) }; font-family: inherit; font-weight: normal; line-height: 20px; + color: ${ COLORS.gray[ 900 ] }; border-radius: ${ CONFIG.radiusBlockUi }; - display: flex; - align-items: center; - padding: ${ space( 2 ) } ${ ITEM_PADDING_INLINE_END } ${ space( 2 ) } - ${ ITEM_PADDING_INLINE_START }; - position: relative; + + padding-block: ${ ITEM_PADDING_BLOCK }; + padding-inline: ${ ITEM_PADDING_INLINE }; + + /* + * Make sure that, when an item is scrolled into view (eg. while using the + * keyboard to move focus), the whole item comes into view + */ + scroll-margin: ${ CONTENT_WRAPPER_PADDING }; + user-select: none; outline: none; &[aria-disabled='true'] { - /* - TODO: - - we need a disabled color in the Theme variables - - design specs use opacity instead of setting a new text color - */ - opacity: 0.5; - pointer-events: none; + color: ${ COLORS.ui.textDisabled }; + cursor: not-allowed; } /* Hover */ - &[data-active-item] { - /* TODO: reconcile with global focus styles */ - background-color: ${ COLORS.gray[ '100' ] }; + &[data-active-item]:not( [data-focus-visible] ):not( + [aria-disabled='true'] + ) { + background-color: ${ COLORS.theme.accent }; + color: ${ COLORS.white }; } /* Keyboard focus (focus-visible) */ @@ -224,74 +187,147 @@ const baseItem = css` /* When the item is the trigger of an open submenu */ ${ DropdownMenu }:not(:focus) &:not(:focus)[aria-expanded="true"] { - /* TODO: should we style submenu triggers any different? */ + background-color: ${ COLORS.gray[ 100 ] }; + color: ${ COLORS.gray[ 900 ] }; } svg { fill: currentColor; } - - &:not( :has( ${ ItemPrefixWrapper } ) ) { - padding-inline-start: ${ ITEM_PREFIX_WIDTH }; - } `; export const DropdownMenuItem = styled( Ariakit.MenuItem )` - ${ baseItem } + ${ baseItem }; `; export const DropdownMenuCheckboxItem = styled( Ariakit.MenuItemCheckbox )` - ${ baseItem } + ${ baseItem }; `; export const DropdownMenuRadioItem = styled( Ariakit.MenuItemRadio )` - ${ baseItem } + ${ baseItem }; `; -export const DropdownMenuGroup = styled( Ariakit.MenuGroup )``; +export const ItemPrefixWrapper = styled.span` + /* Always occupy the first column, even when auto-collapsing */ + grid-column: 1; + + &:not( :empty ) { + margin-inline-end: ${ space( 2 ) }; + } -export const DropdownMenuGroupLabel = styled( Ariakit.MenuGroupLabel )` - box-sizing: border-box; display: flex; align-items: center; - min-height: ${ space( 8 ) }; - - padding: ${ space( 2 ) } ${ ITEM_PADDING_INLINE_END } ${ space( 2 ) } - ${ ITEM_PREFIX_WIDTH }; - /* TODO: color doesn't match available UI variables */ - color: ${ COLORS.gray[ 700 ] }; - - /* TODO: font size doesn't match available ones via "font" utils */ - font-size: 11px; - line-height: 1.4; - font-weight: 500; - text-transform: uppercase; + justify-content: center; + + color: ${ COLORS.gray[ '700' ] }; + + /* + * When the parent menu item is active, except when it's a non-focused/hovered + * submenu trigger (in that case, color should not be inherited) + */ + [data-active-item]:not( [data-focus-visible] ) > &, + /* When the parent menu item is disabled */ + [aria-disabled='true'] > & { + color: inherit; + } +`; + +export const DropdownMenuItemContentWrapper = styled.div` + /* + * Always occupy the second column, since the first column + * is taken by the prefix wrapper (when displayed). + */ + grid-column: 2; + + display: flex; + align-items: center; + justify-content: space-between; + gap: ${ space( 3 ) }; + + pointer-events: none; +`; + +export const DropdownMenuItemChildrenWrapper = styled.div` + flex: 1; + + display: inline-flex; + flex-direction: column; + gap: ${ space( 1 ) }; +`; + +export const ItemSuffixWrapper = styled.span` + flex: 0; + width: max-content; + + display: flex; + align-items: center; + justify-content: center; + gap: ${ space( 3 ) }; + + color: ${ COLORS.gray[ '700' ] }; + + /* + * When the parent menu item is active, except when it's a non-focused/hovered + * submenu trigger (in that case, color should not be inherited) + */ + [data-active-item]:not( [data-focus-visible] ) *:not(${ DropdownMenu }) &, + /* When the parent menu item is disabled */ + [aria-disabled='true'] *:not(${ DropdownMenu }) & { + color: inherit; + } +`; + +export const DropdownMenuGroup = styled( Ariakit.MenuGroup )` + /* Ignore this element when calculating the layout. Useful for subgrid */ + display: contents; `; export const DropdownMenuSeparator = styled( Ariakit.MenuSeparator )< Pick< DropdownMenuContext, 'variant' > >` + /* Occupy the width of all grid columns (ie. full width) */ + grid-column: 1 / -1; + border: none; height: ${ CONFIG.borderWidth }; - /* TODO: doesn't match border color from variables */ background-color: ${ ( props ) => props.variant === 'toolbar' ? TOOLBAR_VARIANT_BORDER_COLOR - : DEFAULT_BORDER_COLOR }; - /* Negative horizontal margin to make separator go from side to side */ - margin: ${ space( 2 ) } calc( -1 * ${ CONTENT_WRAPPER_PADDING } ); + : DIVIDER_COLOR }; + /* Align with menu items' content */ + margin-block: ${ space( 2 ) }; + margin-inline: ${ ITEM_PADDING_INLINE }; /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; `; export const SubmenuChevronIcon = styled( Icon )` + width: ${ space( 1.5 ) }; ${ rtl( { - transform: `scaleX(1) translateX(${ space( 2 ) })`, + transform: `scaleX(1)`, }, { - transform: `scaleX(-1) translateX(${ space( 2 ) })`, + transform: `scaleX(-1)`, } - ) } + ) }; +`; + +export const DropdownMenuItemLabel = styled( Truncate )` + font-size: ${ font( 'default.fontSize' ) }; + line-height: 20px; + color: inherit; +`; + +export const DropdownMenuItemHelpText = styled( Truncate )` + font-size: ${ font( 'helpText.fontSize' ) }; + line-height: 16px; + color: ${ COLORS.gray[ '700' ] }; + + [data-active-item]:not( [data-focus-visible] ) *:not( ${ DropdownMenu } ) &, + [aria-disabled='true'] *:not( ${ DropdownMenu } ) & { + color: inherit; + } `; diff --git a/packages/components/src/dropdown-menu-v2-ariakit/test/index.tsx b/packages/components/src/dropdown-menu-v2-ariakit/test/index.tsx index 297bc0dec9390f..f58639a545a056 100644 --- a/packages/components/src/dropdown-menu-v2-ariakit/test/index.tsx +++ b/packages/components/src/dropdown-menu-v2-ariakit/test/index.tsx @@ -16,7 +16,6 @@ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuItem, - DropdownMenuGroupLabel, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuGroup, @@ -134,8 +133,9 @@ describe( 'DropdownMenu', () => { await press.ArrowDown(); // DropdownMenu open, focus is on the first focusable item + // (disabled items are still focusable and accessible) expect( - screen.getByRole( 'menuitem', { name: 'Second item' } ) + screen.getByRole( 'menuitem', { name: 'First item' } ) ).toHaveFocus(); } ); @@ -163,8 +163,9 @@ describe( 'DropdownMenu', () => { await press.Space(); // DropdownMenu open, focus is on the first focusable item + // (disabled items are still focusable and accessible expect( - screen.getByRole( 'menuitem', { name: 'Second item' } ) + screen.getByRole( 'menuitem', { name: 'First item' } ) ).toHaveFocus(); } ); @@ -452,9 +453,6 @@ describe( 'DropdownMenu', () => { return ( Open dropdown }> - - Radio group label - { render( Open dropdown }> - - Radio group label - { // The contents of the suffix are rendered after the item's children expect( screen.getByRole( 'menuitemradio', { - name: 'Radio item oneRadio suffix', + name: 'Radio item one Radio suffix', } ) ).toBeInTheDocument(); } ); diff --git a/packages/components/src/dropdown-menu-v2-ariakit/types.ts b/packages/components/src/dropdown-menu-v2-ariakit/types.ts index 27d3b1e8c43393..478b89c67f136a 100644 --- a/packages/components/src/dropdown-menu-v2-ariakit/types.ts +++ b/packages/components/src/dropdown-menu-v2-ariakit/types.ts @@ -88,13 +88,6 @@ export interface DropdownMenuGroupProps { children: React.ReactNode; } -export interface DropdownMenuGroupLabelProps { - /** - * The contents of the dropdown menu group label. - */ - children: React.ReactNode; -} - export interface DropdownMenuItemProps { /** * The contents of the menu item. diff --git a/packages/components/src/private-apis.ts b/packages/components/src/private-apis.ts index e39ccbdacc2f54..fb4679dbc34234 100644 --- a/packages/components/src/private-apis.ts +++ b/packages/components/src/private-apis.ts @@ -32,11 +32,12 @@ import { import { DropdownMenu as DropdownMenuV2Ariakit, DropdownMenuGroup as DropdownMenuGroupV2Ariakit, - DropdownMenuGroupLabel as DropdownMenuGroupLabelV2Ariakit, DropdownMenuItem as DropdownMenuItemV2Ariakit, DropdownMenuCheckboxItem as DropdownMenuCheckboxItemV2Ariakit, DropdownMenuRadioItem as DropdownMenuRadioItemV2Ariakit, DropdownMenuSeparator as DropdownMenuSeparatorV2Ariakit, + DropdownMenuItemLabel as DropdownMenuItemLabelV2Ariakit, + DropdownMenuItemHelpText as DropdownMenuItemHelpTextV2Ariakit, } from './dropdown-menu-v2-ariakit'; import { ComponentsContext } from './context/context-system-provider'; import Theme from './theme'; @@ -74,9 +75,10 @@ lock( privateApis, { Theme, DropdownMenuV2Ariakit, DropdownMenuGroupV2Ariakit, - DropdownMenuGroupLabelV2Ariakit, DropdownMenuItemV2Ariakit, DropdownMenuCheckboxItemV2Ariakit, DropdownMenuRadioItemV2Ariakit, DropdownMenuSeparatorV2Ariakit, + DropdownMenuItemLabelV2Ariakit, + DropdownMenuItemHelpTextV2Ariakit, } );