Make `Search` accept sync `autocompleter.options.` (https://github.com/woocommerce/woocommerce-admin/pull/6884)
Co-authored-by: Jeff Stieler <jeff.m.stieler@gmail.com> Make `Search` component accept `autocompleter.options` that meet the requirements stated in [the docs](https://github.com/WordPress/gutenberg/tree/trunk/packages/components/src/autocomplete#options): > May be an array, a function that returns an array, or a function that returns a promise for an array. Fixes https://github.com/woocommerce/woocommerce-admin/issues/6061.
This commit is contained in:
parent
0f4c102c51
commit
6aa78cbdb9
|
@ -1,5 +1,6 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
- Make `Search` accept synchronous `autocompleter.options`. #6884
|
||||||
- Add new (experimental) collapsible list item to collapse list items. #6869
|
- Add new (experimental) collapsible list item to collapse list items. #6869
|
||||||
- SelectControl: fix display of multiple selections without inline tags. #6862
|
- SelectControl: fix display of multiple selections without inline tags. #6862
|
||||||
- Add new (experimental) list, and add depreciation notice for the current list. #6787
|
- Add new (experimental) list, and add depreciation notice for the current list. #6787
|
||||||
|
|
|
@ -108,8 +108,15 @@ export class Search extends Component {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const autocompleter = this.getAutocompleter();
|
const autocompleterOptions = this.getAutocompleter().options;
|
||||||
return autocompleter.options( query ).then( async ( response ) => {
|
|
||||||
|
// Support arrays, sync- & async functions that returns an array.
|
||||||
|
const resolvedOptions = Promise.resolve(
|
||||||
|
typeof autocompleterOptions === 'function'
|
||||||
|
? autocompleterOptions( query )
|
||||||
|
: autocompleterOptions || []
|
||||||
|
);
|
||||||
|
return resolvedOptions.then( async ( response ) => {
|
||||||
const options = this.getFormattedOptions( response, query );
|
const options = this.getFormattedOptions( response, query );
|
||||||
this.setState( { options } );
|
this.setState( { options } );
|
||||||
return options;
|
return options;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { render } from '@testing-library/react';
|
import { render, waitFor } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,8 +10,11 @@ import userEvent from '@testing-library/user-event';
|
||||||
import { Search } from '../index';
|
import { Search } from '../index';
|
||||||
import { computeSuggestionMatch } from '../autocompleters/utils';
|
import { computeSuggestionMatch } from '../autocompleters/utils';
|
||||||
|
|
||||||
|
const delay = ( timeout ) =>
|
||||||
|
new Promise( ( resolve ) => setTimeout( resolve, timeout ) );
|
||||||
|
|
||||||
describe( 'Search', () => {
|
describe( 'Search', () => {
|
||||||
it( 'shows the free text search option', async () => {
|
it( 'shows the free text search option', () => {
|
||||||
const { getByRole, queryAllByRole } = render(
|
const { getByRole, queryAllByRole } = render(
|
||||||
<Search type="products" allowFreeTextSearch />
|
<Search type="products" allowFreeTextSearch />
|
||||||
);
|
);
|
||||||
|
@ -22,6 +25,103 @@ describe( 'Search', () => {
|
||||||
expect( queryAllByRole( 'option' ) ).toHaveLength( 0 );
|
expect( queryAllByRole( 'option' ) ).toHaveLength( 0 );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
describe( 'with `type="custom"`', () => {
|
||||||
|
let sampleOptions, sampleAutocompleter;
|
||||||
|
beforeEach( () => {
|
||||||
|
sampleOptions = [
|
||||||
|
{ name: 'Apple', id: 1 },
|
||||||
|
{ name: 'Orange', id: 2 },
|
||||||
|
{ name: 'Grapes', id: 3 },
|
||||||
|
];
|
||||||
|
sampleAutocompleter = {
|
||||||
|
options: sampleOptions,
|
||||||
|
getOptionIdentifier: ( fruit ) => fruit.id,
|
||||||
|
getOptionLabel: ( option ) => (
|
||||||
|
<nicer-label>{ option.name }</nicer-label>
|
||||||
|
),
|
||||||
|
getOptionKeywords: ( option ) => [ option.name ],
|
||||||
|
getOptionCompletion: ( attribute ) => ( {
|
||||||
|
key: attribute.id,
|
||||||
|
label: attribute.name,
|
||||||
|
} ),
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
describe( 'renders options given in `autocompleter.options`', () => {
|
||||||
|
it( 'as a static array', async () => {
|
||||||
|
const { getByRole, queryAllByRole } = render(
|
||||||
|
<Search
|
||||||
|
type="custom"
|
||||||
|
autocompleter={ sampleAutocompleter }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
// Emulate typing to render available options.
|
||||||
|
userEvent.type( getByRole( 'combobox' ), 'A' );
|
||||||
|
// Wait for async options procesing.
|
||||||
|
await waitFor( () => {
|
||||||
|
expect( queryAllByRole( 'option' ) ).toHaveLength( 3 );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'being a function that for the given query returns an array', async () => {
|
||||||
|
const optionsSpy = jest
|
||||||
|
.fn()
|
||||||
|
.mockName( 'autocompleter.options' );
|
||||||
|
|
||||||
|
const customAutocompleter = {
|
||||||
|
...sampleAutocompleter,
|
||||||
|
// Set the options as a function that returns an array.
|
||||||
|
options: ( ...args ) => {
|
||||||
|
optionsSpy( ...args );
|
||||||
|
return sampleOptions;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { getByRole, queryAllByRole } = render(
|
||||||
|
<Search
|
||||||
|
type="custom"
|
||||||
|
autocompleter={ customAutocompleter }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
// Emulate typing to render available options.
|
||||||
|
userEvent.type( getByRole( 'combobox' ), 'A' );
|
||||||
|
// Wait for async options procesing.
|
||||||
|
await waitFor( () => {
|
||||||
|
expect( optionsSpy ).toBeCalledWith( 'A' );
|
||||||
|
expect( queryAllByRole( 'option' ) ).toHaveLength( 3 );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'being a function that for the given query returns a promise for an array', async () => {
|
||||||
|
const optionsSpy = jest
|
||||||
|
.fn()
|
||||||
|
.mockName( 'autocompleter.options' );
|
||||||
|
|
||||||
|
const customAutocompleter = {
|
||||||
|
...sampleAutocompleter,
|
||||||
|
// Set the options as a function that returns a promise for an array.
|
||||||
|
options: async ( ...args ) => {
|
||||||
|
optionsSpy( ...args );
|
||||||
|
await delay( 1 );
|
||||||
|
return sampleOptions;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { getByRole, queryAllByRole } = render(
|
||||||
|
<Search
|
||||||
|
type="custom"
|
||||||
|
autocompleter={ customAutocompleter }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
// Emulate typing to render available options.
|
||||||
|
userEvent.type( getByRole( 'combobox' ), 'A' );
|
||||||
|
// Wait for async options procesing.
|
||||||
|
await waitFor( () => {
|
||||||
|
expect( optionsSpy ).toBeCalledWith( 'A' );
|
||||||
|
expect( queryAllByRole( 'option' ) ).toHaveLength( 3 );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
it( 'returns an object with decoded text', () => {
|
it( 'returns an object with decoded text', () => {
|
||||||
const decodedText = computeSuggestionMatch(
|
const decodedText = computeSuggestionMatch(
|
||||||
'A test & a test',
|
'A test & a test',
|
||||||
|
|
|
@ -75,6 +75,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt
|
||||||
|
|
||||||
== Unreleased ==
|
== Unreleased ==
|
||||||
|
|
||||||
|
- Fix: Make `Search` accept synchronous `autocompleter.options`. #6884
|
||||||
- Add: Consume remote payment methods on frontend #6867
|
- Add: Consume remote payment methods on frontend #6867
|
||||||
- Add: Add plugin installer to allow installation of plugins via URL #6805
|
- Add: Add plugin installer to allow installation of plugins via URL #6805
|
||||||
- Add: Optional children prop to SummaryNumber component #6748
|
- Add: Optional children prop to SummaryNumber component #6748
|
||||||
|
|
Loading…
Reference in New Issue