/** * External dependencies */ import { createElement, Component } from '@wordpress/element'; import { noop } from 'lodash'; import PropTypes from 'prop-types'; import classnames from 'classnames'; /** * Internal dependencies */ import SelectControl from '../select-control'; import { attributes, countries, coupons, customers, downloadIps, emails, orders, product, productCategory, taxes, usernames, variableProduct, variations, } from './autocompleters'; /** * A search box which autocompletes results while typing, allowing for the user to select an existing object * (product, order, customer, etc). Currently only products are supported. */ export class Search extends Component { constructor( props ) { super( props ); this.state = { options: [], }; this.appendFreeTextSearch = this.appendFreeTextSearch.bind( this ); this.fetchOptions = this.fetchOptions.bind( this ); this.updateSelected = this.updateSelected.bind( this ); } getAutocompleter() { switch ( this.props.type ) { case 'attributes': return attributes; case 'categories': return productCategory; case 'countries': return countries; case 'coupons': return coupons; case 'customers': return customers; case 'downloadIps': return downloadIps; case 'emails': return emails; case 'orders': return orders; case 'products': return product; case 'taxes': return taxes; case 'usernames': return usernames; case 'variableProducts': return variableProduct; case 'variations': return variations; case 'custom': if ( ! this.props.autocompleter || typeof this.props.autocompleter !== 'object' ) { throw new Error( "Invalid autocompleter provided to Search component, it requires a completer object when using 'custom' type." ); } return this.props.autocompleter; default: return {}; } } getFormattedOptions( options, query ) { const autocompleter = this.getAutocompleter(); const formattedOptions = []; options.forEach( ( option ) => { const formattedOption = { key: autocompleter.getOptionIdentifier( option ), label: autocompleter.getOptionLabel( option, query ), keywords: autocompleter .getOptionKeywords( option ) .filter( Boolean ), value: option, }; formattedOptions.push( formattedOption ); } ); return formattedOptions; } fetchOptions( previousOptions, query ) { if ( ! query ) { return []; } const autocompleterOptions = this.getAutocompleter().options; // 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 ); this.setState( { options } ); return options; } ); } updateSelected( selected ) { const { onChange } = this.props; const autocompleter = this.getAutocompleter(); const formattedSelections = selected.map( ( option ) => { return option.value ? autocompleter.getOptionCompletion( option.value ) : option; } ); onChange( formattedSelections ); } appendFreeTextSearch( options, query ) { const { allowFreeTextSearch } = this.props; if ( ! query || ! query.length ) { return []; } if ( ! allowFreeTextSearch ) { return options; } const autocompleter = this.getAutocompleter(); return [ ...autocompleter.getFreeTextOptions( query ), ...options ]; } render() { const autocompleter = this.getAutocompleter(); const { className, inlineTags, placeholder, selected, showClearButton, staticResults, disabled, multiple, } = this.props; const { options } = this.state; const inputType = autocompleter.inputType ? autocompleter.inputType : 'text'; return (