Tree Select Control: Allow parents to be searchable (#41559)

This commit is contained in:
Paul Sealock 2023-11-21 14:25:46 +13:00 committed by GitHub
parent 2d44b1f94b
commit b7ba45d419
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 10 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Update Tree Select Control to allow for searching parent values when `individuallySelectParent` is turned on.

View File

@ -260,12 +260,52 @@ const TreeSelectControl = ( {
}
return (
! this.checked &&
this.leaves.some(
this.children.some(
( opt ) => opt.checked || opt.partialChecked
)
);
},
},
isVisible: {
/**
* Returns whether this option should be visible based on search.
* All options are visible when not searching. Otherwise, true if this option is
* a search result or it has a descendent that is being searched for.
*
* @return {boolean} True if option should be visible, false otherwise.
*/
get() {
// everything is visible when not searching.
if ( ! isSearching ) {
return true;
}
// Exit true if this is searched result.
if ( this.isSearchResult ) {
return true;
}
// If any children are search results, remain visible.
if ( this.hasChildren ) {
return this.children.some( ( opt ) => opt.isVisible );
}
return this.leaves.some( ( opt ) => opt.isSearchResult );
},
},
isSearchResult: {
/**
* Returns whether this option is a searched result.
*
* @return {boolean} True if option is being searched, false otherwise.
*/
get() {
if ( ! isSearching ) {
return false;
}
return !! this.filterMatch;
},
},
expanded: {
/**
* Returns whether this option is expanded.
@ -275,7 +315,7 @@ const TreeSelectControl = ( {
*/
get() {
return (
isSearching ||
( isSearching && this.isVisible ) ||
this.value === ROOT_VALUE ||
cache.expandedValues.includes( this.value )
);
@ -294,20 +334,18 @@ const TreeSelectControl = ( {
const reduceOptions = ( acc, { children = [], ...option } ) => {
if ( children.length ) {
option.children = children.reduce( reduceOptions, [] );
}
if ( ! option.children.length ) {
return acc;
}
} else if ( isSearching ) {
if ( isSearching ) {
const labelWithAccentsRemoved = removeAccents( option.label );
const filterWithAccentsRemoved = removeAccents( filter );
const match = labelWithAccentsRemoved
.toLowerCase()
.indexOf( filterWithAccentsRemoved );
if ( match === -1 ) {
return acc;
if ( match > -1 ) {
option.label = highlightOptionLabel( option.label, match );
option.filterMatch = true;
}
option.label = highlightOptionLabel( option.label, match );
}
Object.defineProperties( option, descriptors );

View File

@ -58,6 +58,7 @@ const Options = ( {
const { hasChildren, checked, partialChecked, expanded } = option;
if ( ! option?.value ) return null;
if ( ! isRoot && ! option?.isVisible ) return null;
return (
<div

View File

@ -29,7 +29,7 @@ const treeSelectControlOptions = [
children: [
{
value: 'TO',
label: 'Tokio',
label: 'Tokyo',
children: [
{ value: 'SI', label: 'Shibuya' },
{ value: 'GI', label: 'Ginza' },
@ -98,6 +98,7 @@ Base.args = {
includeParent: false,
alwaysShowPlaceholder: false,
individuallySelectParent: false,
clearOnSelect: true,
};
Base.argTypes = {

View File

@ -168,6 +168,22 @@ describe( 'TreeSelectControl Component', () => {
expect( queryByLabelText( 'Asia' ) ).toBeFalsy(); // doesn't match pain
} );
it( 'Filters Option categories on Search', () => {
const { queryByLabelText, queryByRole } = render(
<TreeSelectControl options={ options } />
);
const control = queryByRole( 'combobox' );
fireEvent.click( control );
expect( queryByLabelText( 'Europe' ) ).toBeTruthy();
expect( queryByLabelText( 'Asia' ) ).toBeTruthy();
fireEvent.change( control, { target: { value: 'eur' } } );
expect( queryByLabelText( 'Europe' ) ).toBeTruthy(); // match 'eur'
expect( queryByLabelText( 'Asia' ) ).toBeFalsy(); // doesn't match 'eur'
} );
it( 'should call onInputChange when input field changed', () => {
const onInputChangeMock = jest.fn();
const { queryByRole } = render(