diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 0e8026b8115aad..b5b52985043fe2 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -18,6 +18,7 @@ ### Bug Fix +- CustomSelectControl: Update to use a Popover component for rendering the menu ([#37272](https://github.com/WordPress/gutenberg/pull/37272)). - Fixed spacing between `BaseControl` fields and help text within the `ToolsPanel` ([#36334](https://github.com/WordPress/gutenberg/pull/36334)) - Replaced hardcoded blue in `ColorPicker` with UI theme color ([#36153](https://github.com/WordPress/gutenberg/pull/36153)). - Fixed empty `ToolsPanel` height by correcting menu button line-height ([#36895](https://github.com/WordPress/gutenberg/pull/36895)). diff --git a/packages/components/src/custom-select-control/index.js b/packages/components/src/custom-select-control/index.js index 90b5bbf2115e20..8c2ca45ad86c4d 100644 --- a/packages/components/src/custom-select-control/index.js +++ b/packages/components/src/custom-select-control/index.js @@ -7,12 +7,32 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { useResizeObserver } from '@wordpress/compose'; +import { useRef } from '@wordpress/element'; import { Icon, check, chevronDown } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies */ -import { Button, VisuallyHidden } from '../'; +import { Button, Popover, VisuallyHidden } from '../'; + +const OptionList = ( { anchorRef, isOpen, children } ) => { + if ( ! isOpen ) { + return children; + } + + return ( + + { children } + + ); +}; const itemToString = ( item ) => item?.name; // This is needed so that in Windows, where @@ -81,6 +101,14 @@ export default function CustomSelectControl( { stateReducer, } ); + const anchorRef = useRef(); + + // Calculate Popover width based on the size of the component's container. + const [ + containerResizeListener, + { width: containerWidth }, + ] = useResizeObserver(); + function getDescribedBy() { if ( describedBy ) { return describedBy; @@ -97,20 +125,36 @@ export default function CustomSelectControl( { const menuProps = getMenuProps( { className: 'components-custom-select-control__menu', 'aria-hidden': ! isOpen, + style: { + width: containerWidth, + }, } ); + // We need this here, because the null active descendant is not fully ARIA compliant. if ( menuProps[ 'aria-activedescendant' ]?.startsWith( 'downshift-null' ) ) { delete menuProps[ 'aria-activedescendant' ]; } + + const toggleButtonProps = getToggleButtonProps( { + // This is needed because some speech recognition software don't support `aria-labelledby`. + 'aria-label': label, + 'aria-labelledby': undefined, + className: 'components-custom-select-control__button', + isSmall: true, + describedBy: getDescribedBy(), + } ); + return (
+ { containerResizeListener } { hideLabelFromVision ? ( { label } @@ -125,58 +169,51 @@ export default function CustomSelectControl( { { label } ) } - - + +
    + { isOpen && + items.map( ( item, index ) => ( + // eslint-disable-next-line react/jsx-key +
  • + { item.name } + { item.__experimentalHint && ( + + { item.__experimentalHint } + + ) } + { item === selectedItem && ( + + ) } +
  • + ) ) } +
+
); } diff --git a/packages/components/src/custom-select-control/style.scss b/packages/components/src/custom-select-control/style.scss index f3175331336bfd..5499827c1094dc 100644 --- a/packages/components/src/custom-select-control/style.scss +++ b/packages/components/src/custom-select-control/style.scss @@ -41,18 +41,12 @@ display: none; } - // Block UI appearance. - border: $border-width solid $gray-900; - background-color: $white; - border-radius: $radius-block-ui; outline: none; transition: none; - max-height: 400px; - min-width: 100%; overflow: auto; padding: 0; - position: absolute; + margin: 0 (-$border-width); // Allow width value to include the Popover border width. z-index: z-index(".components-popover"); } @@ -84,3 +78,13 @@ margin-bottom: 0; } } + +.components-custom-select-control__popover { + &.components-popover { + .components-popover__content { + // Prevent the Popover scrollbars so that + // the scrollbars are only handled by the Menu. + overflow: hidden; + } + } +}