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:
parent
96a1e9cd7b
commit
19a119f0a8
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Apply wccom experimental select control changes
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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( {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue