Improve experimental select control accessibility (#34510)

* Use downshift built in methods to handle selection of items

* Fix menu styling

* Fix removal a11y announcement

* Don't show menu without results

* Fix async example a11y

* Add changelog entry

* Deselect item in state on remove

* Fix formatting issues

* Update lock file

* Update lock file after pnpm7

* Skip lib check breaking oclif build in package-release

* Rebase again and fix up lock file

* Skip lib check in monorepo-merge

* Fix the lock

* Ignore lint

Co-authored-by: Sam Seay <samueljseay@gmail.com>
Co-authored-by: Jonathan Sadowski <sadowski@automattic.com>
This commit is contained in:
Joshua T Flowers 2022-09-20 14:29:27 -07:00 committed by GitHub
parent d2b20dc993
commit f39d8b6b39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 22 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Improve experimental SelectControl accessibility

View File

@ -4,14 +4,11 @@
top: 100%; top: 100%;
box-sizing: border-box; box-sizing: border-box;
display: none; display: none;
&.is-open {
display: block;
}
.woocommerce-experimental-select-control__menu-inner {
background: $studio-white; background: $studio-white;
border: 1px solid $studio-gray-5; border: 1px solid $studio-gray-5;
border-radius: 3px; border-radius: 3px;
&.is-open.has-results {
display: block;
} }
} }

View File

@ -17,21 +17,19 @@ type MenuProps = {
export const Menu = ( { children, getMenuProps, isOpen }: MenuProps ) => { export const Menu = ( { children, getMenuProps, isOpen }: MenuProps ) => {
return ( return (
<div <ul
{ ...getMenuProps() } { ...getMenuProps() }
className={ classnames( className={ classnames(
'woocommerce-experimental-select-control__menu', 'woocommerce-experimental-select-control__menu',
{ {
'is-open': isOpen, 'is-open': isOpen,
'has-results': Array.isArray( children )
? children.length
: Boolean( children ),
} }
) } ) }
> >
{ isOpen && { isOpen && children }
( ! Array.isArray( children ) || !! children.length ) && (
<ul className="woocommerce-experimental-select-control__menu-inner">
{ children }
</ul> </ul>
) }
</div>
); );
}; };

View File

@ -84,7 +84,14 @@ function SelectControl< ItemType = DefaultItemType >( {
}: SelectControlProps< ItemType > ) { }: SelectControlProps< ItemType > ) {
const [ isFocused, setIsFocused ] = useState( false ); const [ isFocused, setIsFocused ] = useState( false );
const [ inputValue, setInputValue ] = useState( '' ); const [ inputValue, setInputValue ] = useState( '' );
const { getSelectedItemProps, getDropdownProps } = useMultipleSelection(); const {
addSelectedItem,
getSelectedItemProps,
getDropdownProps,
removeSelectedItem,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
} = useMultipleSelection( { itemToString } );
let selectedItems = selected === null ? [] : selected; let selectedItems = selected === null ? [] : selected;
selectedItems = Array.isArray( selectedItems ) selectedItems = Array.isArray( selectedItems )
? selectedItems ? selectedItems
@ -104,11 +111,14 @@ function SelectControl< ItemType = DefaultItemType >( {
getComboboxProps, getComboboxProps,
highlightedIndex, highlightedIndex,
getItemProps, getItemProps,
} = useCombobox( { selectItem,
selectedItem: singleSelectedItem,
} = useCombobox< ItemType | null >( {
inputValue, inputValue,
items: filteredItems, items: filteredItems,
itemToString: getItemLabel, itemToString: getItemLabel,
selectedItem: null, // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
onStateChange: ( { inputValue: value, type, selectedItem } ) => { onStateChange: ( { inputValue: value, type, selectedItem } ) => {
switch ( type ) { switch ( type ) {
case useCombobox.stateChangeTypes.InputChange: case useCombobox.stateChangeTypes.InputChange:
@ -121,9 +131,18 @@ function SelectControl< ItemType = DefaultItemType >( {
case useCombobox.stateChangeTypes.InputBlur: case useCombobox.stateChangeTypes.InputBlur:
if ( selectedItem ) { if ( selectedItem ) {
onSelect( selectedItem ); onSelect( selectedItem );
setInputValue( if ( multiple ) {
multiple ? '' : getItemLabel( selectedItem ) addSelectedItem( selectedItem );
); setInputValue( '' );
break;
}
selectItem( selectedItem );
setInputValue( getItemLabel( selectedItem ) );
}
if ( ! selectedItem && ! multiple ) {
setInputValue( getItemLabel( singleSelectedItem ) );
} }
break; break;
@ -133,6 +152,12 @@ function SelectControl< ItemType = DefaultItemType >( {
}, },
} ); } );
const onRemoveItem = ( item: ItemType ) => {
selectItem( null );
removeSelectedItem( item );
onRemove( item );
};
return ( return (
<div <div
className={ classnames( 'woocommerce-experimental-select-control', { className={ classnames( 'woocommerce-experimental-select-control', {
@ -150,7 +175,7 @@ function SelectControl< ItemType = DefaultItemType >( {
getItemLabel={ getItemLabel } getItemLabel={ getItemLabel }
getItemValue={ getItemValue } getItemValue={ getItemValue }
getSelectedItemProps={ getSelectedItemProps } getSelectedItemProps={ getSelectedItemProps }
onRemove={ onRemove } onRemove={ onRemoveItem }
/> />
) } ) }
<ComboBox <ComboBox

View File

@ -106,6 +106,7 @@ export const Async: React.FC = () => {
const fetchItems = ( value: string | undefined ) => { const fetchItems = ( value: string | undefined ) => {
setIsFetching( true ); setIsFetching( true );
setFetchedItems( [] );
setTimeout( () => { setTimeout( () => {
const results = sampleItems.sort( () => 0.5 - Math.random() ); const results = sampleItems.sort( () => 0.5 - Math.random() );
setFetchedItems( results ); setFetchedItems( results );
@ -117,6 +118,9 @@ export const Async: React.FC = () => {
<> <>
<SelectControl <SelectControl
label="Async" label="Async"
getFilteredItems={ ( allItems ) => {
return allItems;
} }
items={ fetchedItems } items={ fetchedItems }
onInputChange={ fetchItems } onInputChange={ fetchItems }
selected={ selectedItem } selected={ selectedItem }

View File

@ -10,6 +10,7 @@
"typeRoots": [ "typeRoots": [
"./node_modules/@types" "./node_modules/@types"
], ],
"skipLibCheck": true
}, },
"include": [ "include": [
"src/**/*" "src/**/*"

View File

@ -10,6 +10,7 @@
"typeRoots": [ "typeRoots": [
"./node_modules/@types" "./node_modules/@types"
], ],
"skipLibCheck": true
}, },
"include": [ "include": [
"src/**/*" "src/**/*"