diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/address-form.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/address-form.tsx
index 0d425371a49..84b45a8df64 100644
--- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/address-form.tsx
+++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/address-form.tsx
@@ -112,6 +112,18 @@ const AddressForm = ( {
);
}, [ currentFields, fieldConfig, values.country ] );
+ // Clear values for hidden fields.
+ useEffect( () => {
+ addressFormFields.forEach( ( field ) => {
+ if ( field.hidden && values[ field.key ] ) {
+ onChange( {
+ ...values,
+ [ field.key ]: '',
+ } );
+ }
+ } );
+ }, [ addressFormFields, onChange, values ] );
+
useEffect( () => {
if ( type === 'shipping' ) {
validateShippingCountry(
@@ -161,8 +173,6 @@ const AddressForm = ( {
...values,
country: newValue,
state: '',
- city: '',
- postcode: '',
} )
}
errorId={
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/test/index.js b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/test/index.js
index 975611cb6d0..252dc376722 100644
--- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/test/index.js
+++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/address-form/test/index.js
@@ -21,7 +21,7 @@ const renderInCheckoutProvider = ( ui, options = {} ) => {
// Countries used in testing addresses must be in the wcSettings global.
// See: tests/js/setup-globals.js
const primaryAddress = {
- country: 'United Kingdom (UK)',
+ country: 'United Kingdom',
countryKey: 'GB',
city: 'London',
state: 'Greater London',
@@ -46,25 +46,22 @@ const cityRegExp = /city/i;
const stateRegExp = /county|province|state/i;
const postalCodeRegExp = /postal code|postcode|zip/i;
-const inputAddress = ( {
+const inputAddress = async ( {
country = null,
city = null,
state = null,
postcode = null,
} ) => {
if ( country ) {
- const countryButton = screen.getByRole( 'button', {
- name: countryRegExp,
- } );
- userEvent.click( countryButton );
- userEvent.click( screen.getByRole( 'option', { name: country } ) );
+ const countryInput = screen.getByLabelText( countryRegExp );
+ userEvent.type( countryInput, country + '{arrowdown}{enter}' );
}
if ( city ) {
const cityInput = screen.getByLabelText( cityRegExp );
userEvent.type( cityInput, city );
}
if ( state ) {
- const stateButton = screen.queryByRole( 'button', {
+ const stateButton = screen.queryByRole( 'combobox', {
name: stateRegExp,
} );
// State input might be a select or a text input.
@@ -162,17 +159,11 @@ describe( 'AddressForm Component', () => {
inputAddress( secondaryAddress );
// Only update `country` to verify other values are reset.
inputAddress( { country: primaryAddress.country } );
-
- expect( screen.getByLabelText( cityRegExp ).value ).toBe( '' );
expect( screen.getByLabelText( stateRegExp ).value ).toBe( '' );
- expect( screen.getByLabelText( postalCodeRegExp ).value ).toBe( '' );
// Repeat the test with an address which has a select for the state.
inputAddress( tertiaryAddress );
inputAddress( { country: primaryAddress.country } );
-
- expect( screen.getByLabelText( cityRegExp ).value ).toBe( '' );
expect( screen.getByLabelText( stateRegExp ).value ).toBe( '' );
- expect( screen.getByLabelText( postalCodeRegExp ).value ).toBe( '' );
} );
} );
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/combobox/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/combobox/index.tsx
new file mode 100644
index 00000000000..144ef7fb249
--- /dev/null
+++ b/plugins/woocommerce-blocks/assets/js/base/components/combobox/index.tsx
@@ -0,0 +1,150 @@
+/**
+ * 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 );
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/combobox/style.scss b/plugins/woocommerce-blocks/assets/js/base/components/combobox/style.scss
new file mode 100644
index 00000000000..83a5d175bde
--- /dev/null
+++ b/plugins/woocommerce-blocks/assets/js/base/components/combobox/style.scss
@@ -0,0 +1,156 @@
+.wc-block-components-form .wc-block-components-combobox,
+.wc-block-components-combobox {
+ .wc-block-components-combobox-control {
+ @include reset-typography();
+ @include reset-box();
+
+ .components-base-control__field {
+ @include reset-box();
+ }
+ .components-combobox-control__suggestions-container {
+ @include reset-typography();
+ @include reset-box();
+ position: relative;
+ }
+ input.components-combobox-control__input {
+ @include reset-typography();
+ @include font-size(regular);
+
+ box-sizing: border-box;
+ outline: inherit;
+ border: 1px solid $input-border-gray;
+ background: #fff;
+ box-shadow: none;
+ color: $input-text-active;
+ font-family: inherit;
+ font-weight: normal;
+ height: 3em;
+ letter-spacing: inherit;
+ line-height: 1;
+ padding: em($gap-large) $gap em($gap-smallest);
+ text-align: left;
+ text-overflow: ellipsis;
+ text-transform: none;
+ white-space: nowrap;
+ width: 100%;
+ opacity: initial;
+ border-radius: 4px;
+
+ &[aria-expanded="true"],
+ &:focus {
+ background-color: #fff;
+ color: $input-text-active;
+ }
+
+ &[aria-expanded="true"] {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ .has-dark-controls & {
+ background-color: $input-background-dark;
+ border-color: $input-border-dark;
+ color: $input-text-dark;
+
+ &:focus {
+ background-color: $input-background-dark;
+ color: $input-text-dark;
+ }
+ }
+ }
+ .components-form-token-field__suggestions-list {
+ position: absolute;
+ z-index: 10;
+ background-color: $select-dropdown-light;
+ border: 1px solid $input-border-gray;
+ border-top: 0;
+ margin: 3em 0 0 0;
+ padding: 0;
+ max-height: 300px;
+ min-width: 100%;
+ overflow: auto;
+ color: $input-text-active;
+
+ .has-dark-controls & {
+ background-color: $select-dropdown-dark;
+ color: $input-text-dark;
+ }
+
+ .components-form-token-field__suggestion {
+ @include font-size(regular);
+ color: $gray-700;
+ cursor: default;
+ list-style: none;
+ margin: 0;
+ padding: em($gap-smallest) $gap;
+
+ &.is-selected {
+ background-color: $gray-300;
+ .has-dark-controls & {
+ background-color: $select-item-dark;
+ }
+ }
+
+ &:hover,
+ &:focus,
+ &.is-highlighted,
+ &:active {
+ background-color: #00669e;
+ color: #fff;
+
+ }
+ }
+ }
+
+ label.components-base-control__label {
+ @include reset-typography();
+ @include font-size(regular);
+ line-height: 1.375; // =22px when font-size is 16px.
+ position: absolute;
+ transform: translateY(0.75em);
+ transform-origin: top left;
+ transition: all 200ms ease;
+ color: $gray-700;
+ z-index: 1;
+ margin: 0 0 0 #{$gap + 1px};
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: calc(100% - #{2 * $gap});
+ white-space: nowrap;
+
+ .has-dark-controls & {
+ color: $input-placeholder-dark;
+ }
+ @media screen and (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+ }
+ }
+
+ &.is-active,
+ &:focus-within {
+ .wc-block-components-combobox-control label.components-base-control__label {
+ transform: translateY(#{$gap-smallest}) scale(0.75);
+ }
+ }
+
+ &.has-error {
+ .wc-block-components-combobox-control {
+ label.components-base-control__label {
+ color: $alert-red;
+ }
+ input.components-combobox-control__input {
+ &,
+ &:hover,
+ &:focus,
+ &:active {
+ border-color: $alert-red;
+ }
+ &:focus {
+ outline: 1px dotted $alert-red;
+ outline-offset: 2px;
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx b/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx
index d8f8486d2ed..d8eba8a8e6e 100644
--- a/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx
+++ b/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx
@@ -9,7 +9,7 @@ import classnames from 'classnames';
/**
* Internal dependencies
*/
-import { ValidatedSelect } from '../select';
+import Combobox from '../combobox';
import './style.scss';
import type { CountryInputWithCountriesProps } from './CountryInputProps';
@@ -31,8 +31,8 @@ const CountryInput = ( {
const options = useMemo(
() =>
Object.keys( countries ).map( ( key ) => ( {
- key,
- name: decodeEntities( countries[ key ] ),
+ value: key,
+ label: decodeEntities( countries[ key ] ),
} ) ),
[ countries ]
);
@@ -44,15 +44,16 @@ const CountryInput = ( {
'wc-block-components-country-input'
) }
>
- option.key === value ) }
+ value={ value }
errorId={ errorId }
errorMessage={ errorMessage }
required={ required }
+ autoComplete={ autoComplete }
/>
{ autoComplete !== 'off' && (
{
- const textValue = event.target.value;
+ const textValue = event.target.value.toLocaleUpperCase();
const foundOption = options.find(
- ( option ) => option.key === textValue
+ ( option ) =>
+ ( textValue.length !== 2 &&
+ option.label.toLocaleUpperCase() ===
+ textValue ) ||
+ ( textValue.length === 2 &&
+ option.value.toLocaleUpperCase() ===
+ textValue )
);
- onChange( foundOption ? foundOption.key : '' );
+ onChange( foundOption ? foundOption.value : '' );
} }
style={ {
minHeight: '0',
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/select/index.js b/plugins/woocommerce-blocks/assets/js/base/components/select/index.js
deleted file mode 100644
index 1fff441e2ac..00000000000
--- a/plugins/woocommerce-blocks/assets/js/base/components/select/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default as Select } from './select';
-export { default as ValidatedSelect } from './validated';
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/select/select.js b/plugins/woocommerce-blocks/assets/js/base/components/select/select.js
deleted file mode 100644
index 75cff2e0dd8..00000000000
--- a/plugins/woocommerce-blocks/assets/js/base/components/select/select.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * External dependencies
- */
-import PropTypes from 'prop-types';
-import classnames from 'classnames';
-import { CustomSelectControl } from 'wordpress-components';
-
-/**
- * Internal dependencies
- */
-import './style.scss';
-
-const Select = ( {
- className,
- feedback,
- id,
- label,
- onChange,
- options,
- value,
-} ) => {
- return (
-
- {
- onChange( selectedItem.key );
- } }
- options={ options }
- value={ value || null }
- />
- { feedback }
-
- );
-};
-
-Select.propTypes = {
- onChange: PropTypes.func.isRequired,
- options: PropTypes.arrayOf(
- PropTypes.shape( {
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- } ).isRequired
- ).isRequired,
- className: PropTypes.string,
- feedback: PropTypes.node,
- id: PropTypes.string,
- label: PropTypes.string,
- value: PropTypes.shape( {
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- } ),
-};
-
-export default Select;
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/select/style.scss b/plugins/woocommerce-blocks/assets/js/base/components/select/style.scss
deleted file mode 100644
index 0514754d5d6..00000000000
--- a/plugins/woocommerce-blocks/assets/js/base/components/select/style.scss
+++ /dev/null
@@ -1,162 +0,0 @@
-.wc-block-components-form .wc-block-components-select,
-.wc-block-components-select {
- height: 3em;
- position: relative;
-
- label.components-custom-select-control__label {
- @include reset-typography();
- @include font-size(regular);
- line-height: 1.375; // =22px when font-size is 16px.
- position: absolute;
- transform: translateY(0.75em);
- transform-origin: top left;
- transition: all 200ms ease;
- color: $gray-700;
- z-index: 1;
- margin: 0 0 0 #{$gap + 1px};
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: calc(100% - #{2 * $gap});
- white-space: nowrap;
-
- .has-dark-controls & {
- color: $input-placeholder-dark;
- }
- @media screen and (prefers-reduced-motion: reduce) {
- transition: none;
- }
- }
-
- &.is-active label {
- transform: translateY(#{$gap-smallest}) scale(0.75);
- }
-
- &.has-error {
- .components-custom-select-control__button {
- &,
- &:hover,
- &:focus,
- &:active {
- border-color: $alert-red;
- }
- &:focus {
- outline: 1px dotted $alert-red;
- outline-offset: 2px;
- }
- }
- }
-
- &.has-error label {
- color: $alert-red;
- }
-
- .components-custom-select-control__button {
- &,
- &:hover,
- &:focus,
- &:active {
- @include font-size(regular);
- background: #fff;
- box-shadow: none;
- color: $input-text-active;
- font-family: inherit;
- font-weight: normal;
- height: 3em;
- letter-spacing: inherit;
- line-height: 1;
- overflow: hidden;
- padding: em($gap-large) $gap em($gap-smallest);
- text-align: left;
- text-overflow: ellipsis;
- text-transform: none;
- white-space: nowrap;
- width: 100%;
- opacity: initial;
- border-radius: 4px;
- .has-dark-controls & {
- background: $input-background-dark;
- border-color: $input-border-dark;
- color: $input-text-dark;
- }
- }
- }
-
- .components-custom-select-control__button-icon {
- right: #{$gap - 4px};
- .has-dark-controls & {
- fill: $input-text-dark;
- }
- }
-
- .components-custom-select-control__menu {
- background-color: $select-dropdown-light;
- margin: 0;
- max-height: 300px;
- overflow: auto;
- color: $input-text-active;
-
- // Required by IE11.
- &:empty {
- display: none;
- }
- .has-dark-controls & {
- background-color: $select-dropdown-dark;
- color: $input-text-dark;
- }
- }
-
- .components-custom-select-control__item {
- @include font-size(regular);
- margin-left: 0;
- padding-left: $gap;
- &:hover,
- &:focus,
- &.is-highlighted {
- .has-dark-controls & {
- background-color: $select-item-dark;
- }
- }
- }
-
- .components-custom-select-control__item-icon {
- display: none;
- }
-}
-
-.theme-twentytwentyone {
- // Extra classes for specificity.
- &.theme-twentytwentyone.theme-twentytwentyone .components-custom-select-control__button {
- background-color: #fff;
- color: $input-text-active;
- }
-
- &.is-dark-theme {
- // If the theme is in dark mode, as well as the block, then this selector will match.
- .has-dark-controls {
- .components-custom-select-control__item {
- color: $input-text-dark;
- }
- }
-
- // If the theme is in dark mode, but the block isn't, then this selector will match.
- .components-custom-select-control__item {
- color: $input-text-active;
- }
- }
-}
-
-.theme-twentyseventeen {
- // Extra classes for specificity.
- &.theme-twentyseventeen.theme-twentyseventeen {
- .components-custom-select-control__button {
- background-color: $select-dropdown-light;
- color: $input-text-active;
- }
- .has-dark-controls {
- .components-custom-select-control__button {
- background-color: $select-dropdown-dark;
- color: $input-text-dark;
- }
- }
- }
-}
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/select/validated.js b/plugins/woocommerce-blocks/assets/js/base/components/select/validated.js
deleted file mode 100644
index 22c8bcbb34d..00000000000
--- a/plugins/woocommerce-blocks/assets/js/base/components/select/validated.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * External dependencies
- */
-import { __ } from '@wordpress/i18n';
-import { useEffect } from 'react';
-import { useShallowEqual } from '@woocommerce/base-hooks';
-import PropTypes from 'prop-types';
-import classnames from 'classnames';
-import { withInstanceId } from '@wordpress/compose';
-import {
- ValidationInputError,
- useValidationContext,
-} from '@woocommerce/base-context';
-
-/**
- * Internal dependencies
- */
-import Select from './select';
-import './style.scss';
-
-const ValidatedSelect = ( {
- className,
- id,
- value,
- instanceId,
- required,
- errorId,
- errorMessage = __(
- 'Please select a value.',
- 'woo-gutenberg-products-block'
- ),
- ...rest
-} ) => {
- const selectId = id || 'select-' + instanceId;
- errorId = errorId || selectId;
-
- // Prevents re-renders when value is an object, e.g. {key: "NY", name: "New York"}
- const currentValue = useShallowEqual( value );
-
- const {
- getValidationError,
- setValidationErrors,
- clearValidationError,
- } = useValidationContext();
-
- useEffect( () => {
- if ( ! required || currentValue ) {
- clearValidationError( errorId );
- } else {
- setValidationErrors( {
- [ errorId ]: {
- message: errorMessage,
- hidden: true,
- },
- } );
- }
- }, [
- clearValidationError,
- currentValue,
- errorId,
- errorMessage,
- required,
- setValidationErrors,
- ] );
-
- // Remove validation errors when unmounted.
- useEffect( () => {
- return () => {
- clearValidationError( errorId );
- };
- }, [ clearValidationError, errorId ] );
-
- const error = getValidationError( errorId ) || {};
-
- return (
- }
- value={ currentValue }
- { ...rest }
- />
- );
-};
-
-ValidatedSelect.propTypes = {
- className: PropTypes.string,
- errorId: PropTypes.string,
- errorMessage: PropTypes.string,
- id: PropTypes.string,
- required: PropTypes.bool,
- value: PropTypes.shape( {
- key: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- } ),
-};
-
-export default withInstanceId( ValidatedSelect );
diff --git a/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx b/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx
index 098357bde19..b83f4fc660e 100644
--- a/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx
+++ b/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx
@@ -10,7 +10,7 @@ import classnames from 'classnames';
* Internal dependencies
*/
import { ValidatedTextInput } from '../text-input';
-import { ValidatedSelect } from '../select';
+import Combobox from '../combobox';
import './style.scss';
import type { StateInputWithStatesProps } from './StateInputProps';
@@ -30,8 +30,8 @@ const StateInput = ( {
() =>
countryStates
? Object.keys( countryStates ).map( ( key ) => ( {
- key,
- name: decodeEntities( countryStates[ key ] ),
+ value: key,
+ label: decodeEntities( countryStates[ key ] ),
} ) )
: [],
[ countryStates ]
@@ -47,10 +47,12 @@ const StateInput = ( {
if ( options.length > 0 ) {
const foundOption = options.find(
( option ) =>
- option.key === stateValue || option.name === stateValue
+ option.label.toLocaleUpperCase() ===
+ stateValue.toLocaleUpperCase() ||
+ option.value.toLocaleUpperCase() ===
+ stateValue.toLocaleUpperCase()
);
-
- onChange( foundOption ? foundOption.key : '' );
+ onChange( foundOption ? foundOption.value : '' );
return;
}
onChange( stateValue );
@@ -61,7 +63,7 @@ const StateInput = ( {
if ( options.length > 0 ) {
return (
<>
- option.key === value ) }
+ value={ value }
errorMessage={ __(
'Please select a state.',
'woo-gutenberg-products-block'
) }
required={ required }
+ autoComplete={ autoComplete }
/>
{ autoComplete !== 'off' && (
option.value === value);
++ const currentLabel = (_currentOption$label = currentOption === null || currentOption === void 0 ? void 0 : currentOption.label) !== null && _currentOption$label !== void 0 ? _currentOption$label : '';
++ const instanceId = useInstanceId(ComboboxControl);
++ const [selectedSuggestion, setSelectedSuggestion] = useState(currentOption || null);
++ const [isExpanded, setIsExpanded] = useState(false);
++ const [inputValue, setInputValue] = useState('');
++ const inputContainer = useRef();
++ const matchingSuggestions = useMemo(() => {
++ const startsWithMatch = [];
++ const containsMatch = [];
++ const match = deburr(inputValue.toLocaleLowerCase());
++ options.forEach(option => {
++ const index = deburr(option.label).toLocaleLowerCase().indexOf(match);
+
+ if (index === 0) {
+ startsWithMatch.push(option);
+@@ -115,7 +75,7 @@ function ComboboxControl(_ref) {
+ return startsWithMatch.concat(containsMatch);
+ }, [inputValue, options, value]);
+
+- var onSuggestionSelected = function onSuggestionSelected(newSelectedSuggestion) {
++ const onSuggestionSelected = newSelectedSuggestion => {
+ onChange(newSelectedSuggestion.value);
+ speak(messages.selected, 'assertive');
+ setSelectedSuggestion(newSelectedSuggestion);
+@@ -123,10 +83,9 @@ function ComboboxControl(_ref) {
+ setIsExpanded(false);
+ };
+
+- var handleArrowNavigation = function handleArrowNavigation() {
+- var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+- var index = matchingSuggestions.indexOf(selectedSuggestion);
+- var nextIndex = index + offset;
++ const handleArrowNavigation = (offset = 1) => {
++ const index = matchingSuggestions.indexOf(selectedSuggestion);
++ let nextIndex = index + offset;
+
+ if (nextIndex < 0) {
+ nextIndex = matchingSuggestions.length - 1;
+@@ -138,8 +97,12 @@ function ComboboxControl(_ref) {
+ setIsExpanded(true);
+ };
+
+- var onKeyDown = function onKeyDown(event) {
+- var preventDefault = false;
++ const onKeyDown = event => {
++ let preventDefault = false;
++
++ if (event.defaultPrevented) {
++ return;
++ }
+
+ switch (event.keyCode) {
+ case ENTER:
+@@ -164,7 +127,6 @@ function ComboboxControl(_ref) {
+ setIsExpanded(false);
+ setSelectedSuggestion(null);
+ preventDefault = true;
+- event.stopPropagation();
+ break;
+
+ default:
+@@ -176,34 +138,44 @@ function ComboboxControl(_ref) {
+ }
+ };
+
+- var onFocus = function onFocus() {
++ const onFocus = () => {
+ setIsExpanded(true);
+ onFilterValueChange('');
+ setInputValue('');
+ };
+
+- var onFocusOutside = function onFocusOutside() {
++ const onFocusOutside = () => {
+ setIsExpanded(false);
+ };
+
+- var onInputChange = function onInputChange(event) {
+- var text = event.value;
++ const onInputChange = event => {
++ const text = event.value;
+ setInputValue(text);
+ onFilterValueChange(text);
+ setIsExpanded(true);
+ };
+
+- var handleOnReset = function handleOnReset() {
++ const handleOnReset = () => {
+ onChange(null);
+ inputContainer.current.input.focus();
+- }; // Announcements
++ }; // Update current selections when the filter input changes.
+
+
+- useEffect(function () {
+- var hasMatchingSuggestions = matchingSuggestions.length > 0;
++ useEffect(() => {
++ const hasMatchingSuggestions = matchingSuggestions.length > 0;
++ const hasSelectedMatchingSuggestions = matchingSuggestions.indexOf(selectedSuggestion) > 0;
++
++ if (hasMatchingSuggestions && !hasSelectedMatchingSuggestions) {
++ // If the current selection isn't present in the list of suggestions, then automatically select the first item from the list of suggestions.
++ setSelectedSuggestion(matchingSuggestions[0]);
++ }
++ }, [matchingSuggestions, selectedSuggestion]); // Announcements
++
++ useEffect(() => {
++ const hasMatchingSuggestions = matchingSuggestions.length > 0;
+
+ if (isExpanded) {
+- var message = hasMatchingSuggestions ? sprintf(
++ const message = hasMatchingSuggestions ? sprintf(
+ /* translators: %d: number of results. */
+ _n('%d result found, use up and down arrow keys to navigate.', '%d results found, use up and down arrow keys to navigate.', matchingSuggestions.length), matchingSuggestions.length) : __('No results.');
+ speak(message, 'polite');
+@@ -220,7 +192,7 @@ function ComboboxControl(_ref) {
+ className: classnames(className, 'components-combobox-control'),
+ tabIndex: "-1",
+ label: label,
+- id: "components-form-token-input-".concat(instanceId),
++ id: `components-form-token-input-${instanceId}`,
+ hideLabelFromVision: hideLabelFromVision,
+ help: help
+ }, createElement("div", {
+@@ -232,7 +204,7 @@ function ComboboxControl(_ref) {
+ instanceId: instanceId,
+ ref: inputContainer,
+ value: isExpanded ? inputValue : currentLabel,
+- "aria-label": currentLabel ? "".concat(currentLabel, ", ").concat(label) : null,
++ "aria-label": currentLabel ? `${currentLabel}, ${label}` : null,
+ onFocus: onFocus,
+ isExpanded: isExpanded,
+ selectedSuggestionIndex: matchingSuggestions.indexOf(selectedSuggestion),
+@@ -248,9 +220,7 @@ function ComboboxControl(_ref) {
+ match: {
+ label: inputValue
+ },
+- displayTransform: function displayTransform(suggestion) {
+- return suggestion.label;
+- },
++ displayTransform: suggestion => suggestion.label,
+ suggestions: matchingSuggestions,
+ selectedIndex: matchingSuggestions.indexOf(selectedSuggestion),
+ onHover: setSelectedSuggestion,