diff --git a/assets/js/base/components/button/index.tsx b/assets/js/base/components/button/index.tsx index 5a6df73ab76..014cdbc25dc 100644 --- a/assets/js/base/components/button/index.tsx +++ b/assets/js/base/components/button/index.tsx @@ -15,6 +15,9 @@ interface ButtonProps extends WPButton.ButtonProps { className?: string; showSpinner?: boolean; children?: ReactNode; + disabled?: boolean; + onClick?: ( e: React.MouseEvent< HTMLButtonElement, MouseEvent > ) => void; + type?: 'input' | 'submit'; } /** diff --git a/assets/js/base/components/cart-checkout/address-form/address-form.tsx b/assets/js/base/components/cart-checkout/address-form/address-form.tsx index b054ca2ca96..e90fec28c07 100644 --- a/assets/js/base/components/cart-checkout/address-form/address-form.tsx +++ b/assets/js/base/components/cart-checkout/address-form/address-form.tsx @@ -57,28 +57,27 @@ const validateShippingCountry = ( }; interface AddressFormProps { - id: string; + // Id for component. + id?: string; + // Unique id for form. instanceId: string; + // Array of fields in form. fields: ( keyof AddressFields )[]; - fieldConfig: Record< keyof AddressFields, Partial< AddressField > >; + // Field configuration for fields in form. + fieldConfig?: Record< keyof AddressFields, Partial< AddressField > >; + // Function to all for an form onChange event. onChange: ( newValue: EnteredAddress ) => void; - type: AddressType; + // Type of form. + type?: AddressType; + // Values for fields. values: EnteredAddress; } + /** * Checkout address form. - * - * @param {Object} props Incoming props for component. - * @param {string} props.id Id for component. - * @param {Array} props.fields Array of fields in form. - * @param {Object} props.fieldConfig Field configuration for fields in form. - * @param {string} props.instanceId Unique id for form. - * @param {function(any):any} props.onChange Function to all for an form onChange event. - * @param {string} props.type Type of form. - * @param {Object} props.values Values for fields. */ const AddressForm = ( { - id, + id = '', fields = ( Object.keys( defaultAddressFields ) as unknown ) as ( keyof AddressFields )[], diff --git a/assets/js/base/components/text-input/validated-text-input.tsx b/assets/js/base/components/text-input/validated-text-input.tsx index 9818f37169b..042d92d71fa 100644 --- a/assets/js/base/components/text-input/validated-text-input.tsx +++ b/assets/js/base/components/text-input/validated-text-input.tsx @@ -7,7 +7,6 @@ import classnames from 'classnames'; import { ValidationInputError, useValidationContext, - useCheckoutContext, } from '@woocommerce/base-context'; import { withInstanceId } from '@wordpress/compose'; import { isString } from '@woocommerce/types'; @@ -64,9 +63,6 @@ const ValidatedTextInput = ( { clearValidationError, getValidationErrorId, } = useValidationContext(); - - const { isBeforeProcessing } = useCheckoutContext(); - const textInputId = typeof id !== 'undefined' ? id : 'textinput-' + instanceId; const errorIdString = errorId !== undefined ? errorId : textInputId; @@ -99,6 +95,18 @@ const ValidatedTextInput = ( { [ clearValidationError, errorIdString, setValidationErrors ] ); + /** + * Runs validation on change if the current element is not in focus. This is because autofilled elements do not + * trigger the blur() event. + */ + const maybeValidateOnChange = useCallback( () => { + if ( + inputRef.current?.ownerDocument.activeElement !== inputRef.current + ) { + validateInput( true ); + } + }, [ validateInput ] ); + useEffect( () => { if ( isPristine ) { if ( focusOnMount ) { @@ -117,14 +125,6 @@ const ValidatedTextInput = ( { } }, [ isPristine, setIsPristine, validateOnMount, validateInput ] ); - /** - * @todo Remove extra validation call after refactoring the validation system. - */ - useEffect( () => { - if ( isBeforeProcessing ) { - validateInput(); - } - }, [ isBeforeProcessing, validateInput ] ); // Remove validation errors when unmounted. useEffect( () => { return () => { @@ -137,9 +137,11 @@ const ValidatedTextInput = ( { message?: string; hidden?: boolean; }; + if ( isString( passedErrorMessage ) && passedErrorMessage !== '' ) { errorMessage.message = passedErrorMessage; } + const hasError = errorMessage.message && ! errorMessage.hidden; const describedBy = showError && hasError && getValidationErrorId( errorIdString ) @@ -168,6 +170,7 @@ const ValidatedTextInput = ( { onChange={ ( val ) => { hideValidationError( errorIdString ); onChange( val ); + maybeValidateOnChange(); } } ariaDescribedBy={ describedBy } { ...rest }