Skip to content

Commit

Permalink
SelectTree: keep the focus on the input while navigating between menu…
Browse files Browse the repository at this point in the history
… items (woocommerce#49989)

* Commit at a functional state

* Change role to 'listbox'

* Add --highlighted class rules

* Fix overflow in create category modal

* Add countNumberOfItems
Fix multiple bugs
Refactor
Rename and move use-linked-tree file to linked-tree-utils

* Add comments

* Escape regExp

* Allow to select/remove with the enter key

* Add changelogs

* Fix unit tests

* Fix bug on css selector, since role was changed

* Fix bug in index calculation and handle focus on checkboxes and expander button correctly

* Only add activedescendant when something is highlighted

preventDefault when pressing arrowUp

* Fix bug: items array was being used instead of using linked tree

* Call onSelect when pressing enter

* Add guards to prevent tests breaking

* Add additional tests for SelectTree

* Add comments and rename some functions in linked-tree-utils
  • Loading branch information
nathanss authored Jul 31, 2024
1 parent 3dd6a40 commit d3bd80f
Show file tree
Hide file tree
Showing 17 changed files with 585 additions and 137 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: update

Update SelectTree and Tree controls to allow highlighting items without focus
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ const PrivateSelectedItems = < ItemType, >(
);
}

const focusSibling = ( event: React.KeyboardEvent< HTMLDivElement > ) => {
const selectedItem = ( event.target as HTMLElement ).closest(
'.woocommerce-experimental-select-control__selected-item'
);
const sibling =
event.key === 'ArrowLeft' || event.key === 'Backspace'
? selectedItem?.previousSibling
: selectedItem?.nextSibling;
if ( sibling ) {
(
( sibling as HTMLElement ).querySelector(
'.woocommerce-tag__remove'
) as HTMLElement
)?.focus();
return true;
}
return false;
};

return (
<div className={ classes }>
{ items.map( ( item, index ) => {
Expand All @@ -102,24 +121,9 @@ const PrivateSelectedItems = < ItemType, >(
event.key === 'ArrowLeft' ||
event.key === 'ArrowRight'
) {
const selectedItem = (
event.target as HTMLElement
).closest(
'.woocommerce-experimental-select-control__selected-item'
);
const sibling =
event.key === 'ArrowLeft'
? selectedItem?.previousSibling
: selectedItem?.nextSibling;
if ( sibling ) {
(
(
sibling as HTMLElement
).querySelector(
'.woocommerce-tag__remove'
) as HTMLElement
)?.focus();
} else if (
const focused = focusSibling( event );
if (
! focused &&
event.key === 'ArrowRight' &&
onSelectedItemsEnd
) {
Expand All @@ -130,6 +134,9 @@ const PrivateSelectedItems = < ItemType, >(
event.key === 'ArrowDown'
) {
event.preventDefault(); // prevent unwanted scroll
} else if ( event.key === 'Backspace' ) {
onRemove( item );
focusSibling( event );
}
} }
onBlur={ onBlur }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useLayoutEffect,
useState,
} from '@wordpress/element';
import { escapeRegExp } from 'lodash';

/**
* Internal dependencies
Expand All @@ -26,6 +27,7 @@ type MenuProps = {
isLoading?: boolean;
position?: Popover.Position;
scrollIntoViewOnOpen?: boolean;
highlightedIndex?: number;
items: LinkedTree[];
treeRef?: React.ForwardedRef< HTMLOListElement >;
onClose?: () => void;
Expand All @@ -44,6 +46,7 @@ export const SelectTreeMenu = ( {
onEscape,
shouldShowCreateButton,
onFirstItemLoop,
onExpand,
...props
}: MenuProps ) => {
const [ boundingRect, setBoundingRect ] = useState< DOMRect >();
Expand All @@ -66,17 +69,18 @@ export const SelectTreeMenu = ( {
// Scroll the selected item into view when the menu opens.
useEffect( () => {
if ( isOpen && scrollIntoViewOnOpen ) {
selectControlMenuRef.current?.scrollIntoView();
selectControlMenuRef.current?.scrollIntoView?.();
}
}, [ isOpen, scrollIntoViewOnOpen ] );

const shouldItemBeExpanded = ( item: LinkedTree ): boolean => {
if ( ! props.createValue || ! item.children?.length ) return false;
return item.children.some( ( child ) => {
if (
new RegExp( props.createValue || '', 'ig' ).test(
child.data.label
)
new RegExp(
escapeRegExp( props.createValue || '' ),
'ig'
).test( child.data.label )
) {
return true;
}
Expand Down Expand Up @@ -130,6 +134,7 @@ export const SelectTreeMenu = ( {
ref={ ref }
items={ items }
onTreeBlur={ onClose }
onExpand={ onExpand }
shouldItemBeExpanded={
shouldItemBeExpanded
}
Expand Down
Loading

0 comments on commit d3bd80f

Please sign in to comment.