* Update notices to say `card` instead of `card's`

* Track isEmpty per field so placeholders are not visible when focusing and defocusing other fields

* Make payment notices non-dissmissable, and removed on payment method switch

* Don't add validation notices

* implement nullish coalescing operator

Co-authored-by: Darren Ethier <darren@roughsmootheng.in>
This commit is contained in:
Mike Jolley 2020-05-06 19:58:31 +01:00 committed by GitHub
parent 74a4e55075
commit dd811b1491
6 changed files with 62 additions and 38 deletions

View File

@ -4,6 +4,8 @@
import {
usePaymentMethods,
usePaymentMethodInterface,
useStoreNotices,
useEmitResponse,
} from '@woocommerce/base-hooks';
import {
useCallback,
@ -63,6 +65,8 @@ const PaymentMethods = () => {
} = usePaymentMethodInterface();
const currentPaymentMethodInterface = useRef( paymentMethodInterface );
const [ selectedToken, setSelectedToken ] = useState( 0 );
const { noticeContexts } = useEmitResponse();
const { removeNotice } = useStoreNotices();
// update ref on change.
useEffect( () => {
@ -98,7 +102,10 @@ const PaymentMethods = () => {
const renderedTabs = (
<Tabs
className="wc-block-components-checkout-payment-methods"
onSelect={ ( tabName ) => setActivePaymentMethod( tabName ) }
onSelect={ ( tabName ) => {
setActivePaymentMethod( tabName );
removeNotice( 'wc-payment-error', noticeContexts.PAYMENTS );
} }
tabs={ Object.keys( currentPaymentMethods.current ).map(
( name ) => {
const { label, ariaLabel } = currentPaymentMethods.current[

View File

@ -290,22 +290,30 @@ export const PaymentMethodDataProvider = ( { children } ) => {
response?.meta?.shippingData
);
} else if ( isFailResponse( response ) ) {
addErrorNotice( response?.message, {
id: 'wc-payment-error',
context:
response?.messageContext || noticeContexts.PAYMENTS,
} );
if ( response.message && response.message.length ) {
addErrorNotice( response.message, {
id: 'wc-payment-error',
isDismissible: false,
context:
response?.messageContext ||
noticeContexts.PAYMENTS,
} );
}
setPaymentStatus().failed(
response?.message,
response?.meta?.paymentMethodData,
response?.meta?.billingData
);
} else if ( isErrorResponse( response ) ) {
addErrorNotice( response?.message, {
id: 'wc-payment-error',
context:
response?.messageContext || noticeContexts.PAYMENTS,
} );
if ( response.message && response.message.length ) {
addErrorNotice( response.message, {
id: 'wc-payment-error',
isDismissible: false,
context:
response?.messageContext ||
noticeContexts.PAYMENTS,
} );
}
setPaymentStatus().error( response.message );
setValidationErrors( response?.validationErrors );
} else {

View File

@ -67,7 +67,11 @@ export const CardElements = ( {
onChange,
inputErrorComponent: ValidationInputError,
} ) => {
const [ isEmpty, setIsEmpty ] = useState( true );
const [ isEmpty, setIsEmpty ] = useState( {
cardNumber: true,
cardExpiry: true,
cardCvc: true,
} );
const {
options: cardNumOptions,
onActive: cardNumOnActive,
@ -86,25 +90,25 @@ export const CardElements = ( {
error: cardCvcError,
setError: cardCvcSetError,
} = useElementOptions();
const errorCallback = ( errorSetter ) => ( event ) => {
const errorCallback = ( errorSetter, elementId ) => ( event ) => {
if ( event.error ) {
errorSetter( event.error.message );
} else {
errorSetter( '' );
}
setIsEmpty( event.empty );
setIsEmpty( { ...isEmpty, [ elementId ]: event.empty } );
onChange( event );
};
return (
<div className="wc-block-card-elements">
<div className="wc-block-gateway-container wc-card-number-element">
<CardNumberElement
onChange={ errorCallback( cardNumSetError ) }
onChange={ errorCallback( cardNumSetError, 'cardNumber' ) }
options={ cardNumOptions }
className={ baseTextInputStyles }
id="wc-stripe-card-number-element"
onFocus={ () => cardNumOnActive( isEmpty ) }
onBlur={ () => cardNumOnActive( isEmpty ) }
onFocus={ () => cardNumOnActive( isEmpty.cardNumber ) }
onBlur={ () => cardNumOnActive( isEmpty.cardNumber ) }
/>
<label htmlFor="wc-stripe-card-number-element">
{ __( 'Card Number', 'woo-gutenberg-product-blocks' ) }
@ -113,11 +117,14 @@ export const CardElements = ( {
</div>
<div className="wc-block-gateway-container wc-card-expiry-element">
<CardExpiryElement
onChange={ errorCallback( cardExpirySetError ) }
onChange={ errorCallback(
cardExpirySetError,
'cardExpiry'
) }
options={ cardExpiryOptions }
className={ baseTextInputStyles }
onFocus={ () => cardExpiryOnActive( isEmpty ) }
onBlur={ () => cardExpiryOnActive( isEmpty ) }
onFocus={ () => cardExpiryOnActive( isEmpty.cardExpiry ) }
onBlur={ () => cardExpiryOnActive( isEmpty.cardExpiry ) }
id="wc-stripe-card-expiry-element"
/>
<label htmlFor="wc-stripe-card-expiry-element">
@ -127,11 +134,11 @@ export const CardElements = ( {
</div>
<div className="wc-block-gateway-container wc-card-cvc-element">
<CardCvcElement
onChange={ errorCallback( cardCvcSetError ) }
onChange={ errorCallback( cardCvcSetError, 'cardCvc' ) }
options={ cardCvcOptions }
className={ baseTextInputStyles }
onFocus={ () => cardCvcOnActive( isEmpty ) }
onBlur={ () => cardCvcOnActive( isEmpty ) }
onFocus={ () => cardCvcOnActive( isEmpty.cardCvc ) }
onBlur={ () => cardCvcOnActive( isEmpty.cardCvc ) }
id="wc-stripe-card-code-element"
/>
<label htmlFor="wc-stripe-card-code-element">

View File

@ -61,8 +61,9 @@ export const useCheckoutSubscriptions = (
onStripeError.current = ( event ) => {
const type = event.error.type;
const code = event.error.code || '';
let message = getErrorMessageForTypeAndCode( type, code );
message = message || event.error.message;
const message =
getErrorMessageForTypeAndCode( type, code ) ??
event.error.message;
setError( error );
return message;
};

View File

@ -74,9 +74,9 @@
* @property {string} country Two-letter ISO code for
* the country on the card.
* @property {number} exp_month Two-digit number for
* card's expiry month.
* card expiry month.
* @property {number} exp_year Two-digit number for
* card's expiry year.
* card expiry year.
* @property {string} fingerprint Uniquely identifies this
* particular card number
* @property {string} funding The card funding type

View File

@ -174,15 +174,15 @@ const getErrorMessageForCode = ( code ) => {
'woocommerce-gateway-stripe'
),
[ errorCodes.INVALID_EXPIRY_MONTH ]: __(
"The card's expiration month is invalid.",
'The card expiration month is invalid.',
'woocommerce-gateway-stripe'
),
[ errorCodes.INVALID_EXPIRY_YEAR ]: __(
"The card's expiration year is invalid.",
'The card expiration year is invalid.',
'woocommerce-gateway-stripe'
),
[ errorCodes.INVALID_CVC ]: __(
"The card's security code is invalid.",
'The card security code is invalid.',
'woocommerce-gateway-stripe'
),
[ errorCodes.INCORRECT_NUMBER ]: __(
@ -194,11 +194,11 @@ const getErrorMessageForCode = ( code ) => {
'woocommerce-gateway-stripe'
),
[ errorCodes.INCOMPLETE_CVC ]: __(
"The card's security code is incomplete.",
'The card security code is incomplete.',
'woocommerce-gateway-stripe'
),
[ errorCodes.INCOMPLETE_EXPIRY ]: __(
"The card's expiration date is incomplete.",
'The card expiration date is incomplete.',
'woocommerce-gateway-stripe'
),
[ errorCodes.EXPIRED_CARD ]: __(
@ -206,15 +206,15 @@ const getErrorMessageForCode = ( code ) => {
'woocommerce-gateway-stripe'
),
[ errorCodes.INCORRECT_CVC ]: __(
"The card's security code is incorrect.",
'The card security code is incorrect.',
'woocommerce-gateway-stripe'
),
[ errorCodes.INCORRECT_ZIP ]: __(
"The card's zip code failed validation.",
'The card zip code failed validation.',
'woocommerce-gateway-stripe'
),
[ errorCodes.INVALID_EXPIRY_YEAR_PAST ]: __(
"The card's expiration year is in the past",
'The card expiration year is in the past',
'woocommerce-gateway-stripe'
),
[ errorCodes.CARD_DECLINED ]: __(
@ -230,7 +230,7 @@ const getErrorMessageForCode = ( code ) => {
'woocommerce-gateway-stripe'
),
};
return messages[ code ] || '';
return messages[ code ] || null;
};
const getErrorMessageForTypeAndCode = ( type, code = '' ) => {
@ -246,10 +246,11 @@ const getErrorMessageForTypeAndCode = ( type, code = '' ) => {
'woo-gutenberg-product-blocks'
);
case errorTypes.CARD_ERROR:
case errorTypes.VALIDATION_ERROR:
return getErrorMessageForCode( code );
case errorTypes.VALIDATION_ERROR:
return ''; // These are shown inline.
}
return '';
return null;
};
export {