/** * External dependencies */ import classnames from 'classnames'; import { __ } from '@wordpress/i18n'; import { useEffect, useRef } from '@wordpress/element'; import { withInstanceId } from '@wordpress/compose'; import { ComboboxControl } from 'wordpress-components'; import { ValidationInputError, useValidationContext, } from '@woocommerce/base-context'; import { isObject } from '@woocommerce/types'; /** * Internal dependencies */ import './style.scss'; export interface ComboboxControlOption { label: string; value: string; } /** * Wrapper for the WordPress ComboboxControl which supports validation. */ const Combobox = ( { id, className, label, onChange, options, value, required = false, errorMessage = __( 'Please select a value.', 'woo-gutenberg-products-block' ), errorId: incomingErrorId, instanceId = '0', autoComplete = 'off', }: { id: string; className: string; label: string; onChange: ( filterValue: string ) => void; options: ComboboxControlOption[]; value: string; required: boolean; errorMessage: string; errorId: string; instanceId: string; autoComplete: string; } ): JSX.Element => { const { getValidationError, setValidationErrors, clearValidationError, } = useValidationContext(); const controlRef = useRef< HTMLDivElement >( null ); const controlId = id || 'control-' + instanceId; const errorId = incomingErrorId || controlId; const error = ( getValidationError( errorId ) || { message: '', hidden: false, } ) as { message: string; hidden: boolean; }; useEffect( () => { if ( ! required || value ) { clearValidationError( errorId ); } else { setValidationErrors( { [ errorId ]: { message: errorMessage, hidden: true, }, } ); } return () => { clearValidationError( errorId ); }; }, [ clearValidationError, value, errorId, errorMessage, required, setValidationErrors, ] ); // @todo Remove patch for ComboboxControl once https://github.com/WordPress/gutenberg/pull/33928 is released return (
{ if ( filterValue.length ) { // If we have a value and the combobox is not focussed, this could be from browser autofill. const activeElement = isObject( controlRef.current ) ? controlRef.current.ownerDocument.activeElement : undefined; if ( activeElement && isObject( controlRef.current ) && controlRef.current.contains( activeElement ) ) { return; } // Try to match. const normalizedFilterValue = filterValue.toLocaleUpperCase(); const foundOption = options.find( ( option ) => option.label .toLocaleUpperCase() .startsWith( normalizedFilterValue ) || option.value.toLocaleUpperCase() === normalizedFilterValue ); if ( foundOption ) { onChange( foundOption.value ); } } } } options={ options } value={ value || '' } allowReset={ false } autoComplete={ autoComplete } />
); }; export default withInstanceId( Combobox );