Upstream changes from experimental-select-control back to @woocommerce/components (#36521)

* Add scrollIntoViewOnOpen and position props to menu

* Add ability to customize active item style

* Add menu toggle button

* Add changelog

* Fix changelog

* Rename toggle button classname

* Change default menu position

* Update toggle button story

* Add default value to getToggleButtonProps
This commit is contained in:
Chi-Hsuan Huang 2023-03-29 17:18:43 +08:00 committed by GitHub
parent 96a1e9cd7b
commit 19a119f0a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 5 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Apply wccom experimental select control changes

View File

@ -44,3 +44,18 @@
.woocommerce-experimental-select-control__suffix {
align-self: stretch;
}
.woocommerce-experimental-select-control__combox-box-toggle-button {
all: unset;
position: absolute;
right: 6px;
top: 50%;
transform: translateY( -50% );
> svg {
margin-top: 4px;
}
}
.woocommerce-experimental-select-control:not( .is-focused ) .woocommerce-experimental-select-control__combox-box-toggle-button {
pointer-events: none; // Prevents the icon from being clickable when the combobox is not focused, because otherwise we get a race condition when clicking on the icon, because focussing the combobox opens the menu, then sequentially the icon toggles it back closed
}

View File

@ -1,21 +1,42 @@
/**
* External dependencies
*/
import { createElement, MouseEvent, useRef } from 'react';
import { createElement, MouseEvent, useRef, forwardRef } from 'react';
import classNames from 'classnames';
import { Icon, chevronDown } from '@wordpress/icons';
type ComboBoxProps = {
children?: JSX.Element | JSX.Element[] | null;
comboBoxProps: JSX.IntrinsicElements[ 'div' ];
inputProps: JSX.IntrinsicElements[ 'input' ];
getToggleButtonProps?: () => Omit<
JSX.IntrinsicElements[ 'button' ],
'ref'
>;
suffix?: JSX.Element | null;
showToggleButton?: boolean;
};
const ToggleButton = forwardRef< HTMLButtonElement >( ( props, ref ) => {
// using forwardRef here because getToggleButtonProps injects a ref prop
return (
<button
className="woocommerce-experimental-select-control__combox-box-toggle-button"
{ ...props }
ref={ ref }
>
<Icon icon={ chevronDown } />
</button>
);
} );
export const ComboBox = ( {
children,
comboBoxProps,
getToggleButtonProps = () => ( {} ),
inputProps,
suffix,
showToggleButton,
}: ComboBoxProps ) => {
const inputRef = useRef< HTMLInputElement | null >( null );
@ -72,6 +93,9 @@ export const ComboBox = ( {
{ suffix }
</div>
) }
{ showToggleButton && (
<ToggleButton { ...getToggleButtonProps() } />
) }
</div>
);
};

View File

@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { createElement, ReactElement } from 'react';
import { createElement, CSSProperties, ReactElement } from 'react';
/**
* Internal dependencies
@ -14,6 +14,7 @@ export type MenuItemProps< ItemType > = {
item: ItemType;
children: ReactElement | string;
getItemProps: getItemPropsType< ItemType >;
activeStyle?: CSSProperties;
};
export const MenuItem = < ItemType, >( {
@ -21,11 +22,12 @@ export const MenuItem = < ItemType, >( {
getItemProps,
index,
isActive,
activeStyle = { backgroundColor: '#bde4ff' },
item,
}: MenuItemProps< ItemType > ) => {
return (
<li
style={ isActive ? { backgroundColor: '#bde4ff' } : {} }
style={ isActive ? activeStyle : {} }
{ ...getItemProps( { item, index } ) }
className="woocommerce-experimental-select-control__menu-item"
>

View File

@ -22,6 +22,8 @@ type MenuProps = {
getMenuProps: getMenuPropsType;
isOpen: boolean;
className?: string;
position?: Popover.Position;
scrollIntoViewOnOpen?: boolean;
};
export const Menu = ( {
@ -29,6 +31,8 @@ export const Menu = ( {
getMenuProps,
isOpen,
className,
position = 'bottom right',
scrollIntoViewOnOpen = false,
}: MenuProps ) => {
const [ boundingRect, setBoundingRect ] = useState< DOMRect >();
const selectControlMenuRef = useRef< HTMLDivElement >( null );
@ -41,6 +45,13 @@ export const Menu = ( {
}
}, [ selectControlMenuRef.current ] );
// Scroll the selected item into view when the menu opens.
useEffect( () => {
if ( isOpen && scrollIntoViewOnOpen ) {
selectControlMenuRef.current?.scrollIntoView();
}
}, [ isOpen, scrollIntoViewOnOpen ] );
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */
/* Disabled because of the onmouseup on the ul element below. */
return (
@ -60,7 +71,7 @@ export const Menu = ( {
'has-results': Children.count( children ) > 0,
}
) }
position="bottom right"
position={ position }
animate={ false }
>
<ul

View File

@ -68,6 +68,7 @@ export type SelectControlProps< ItemType > = {
disabled?: boolean;
inputProps?: GetInputPropsOptions;
suffix?: JSX.Element | null;
showToggleButton?: boolean;
/**
* This is a feature already implemented in downshift@7.0.0 through the
* reducer. In order for us to use it this prop is added temporarily until
@ -123,6 +124,7 @@ function SelectControl< ItemType = DefaultItemType >( {
disabled,
inputProps = {},
suffix = <SuffixIcon icon={ search } />,
showToggleButton = false,
__experimentalOpenMenuOnFocus = false,
}: SelectControlProps< ItemType > ) {
const [ isFocused, setIsFocused ] = useState( false );
@ -154,12 +156,13 @@ function SelectControl< ItemType = DefaultItemType >( {
}
setInputValue( getItemLabel( singleSelectedItem ) );
}, [ singleSelectedItem ] );
}, [ getItemLabel, multiple, singleSelectedItem ] );
const {
isOpen,
getLabelProps,
getMenuProps,
getToggleButtonProps,
getInputProps,
getComboboxProps,
highlightedIndex,
@ -256,6 +259,7 @@ function SelectControl< ItemType = DefaultItemType >( {
{ /* eslint-enable jsx-a11y/label-has-for */ }
<ComboBox
comboBoxProps={ getComboboxProps() }
getToggleButtonProps={ getToggleButtonProps }
inputProps={ getInputProps( {
...getDropdownProps( {
preventKeyAction: isOpen,
@ -274,6 +278,7 @@ function SelectControl< ItemType = DefaultItemType >( {
...inputProps,
} ) }
suffix={ suffix }
showToggleButton={ showToggleButton }
>
<>
{ children( {

View File

@ -573,6 +573,24 @@ export const CustomSuffix: React.FC = () => {
);
};
export const ToggleButton: React.FC = () => {
const [ selected, setSelected ] =
useState< SelectedType< DefaultItemType > >();
return (
<SelectControl
items={ sampleItems }
label="Has toggle button"
selected={ selected }
onSelect={ ( item ) => item && setSelected( item ) }
onRemove={ () => setSelected( null ) }
suffix={ null }
showToggleButton={ true }
__experimentalOpenMenuOnFocus={ true }
/>
);
};
export default {
title: 'WooCommerce Admin/experimental/SelectControl',
component: SelectControl,