[Accessibility] Focus coupon input if it has an error (#48738)
* Focus cupon input if it has errors * Add changelog file * Add styles to coupon field with error on cart page * Make coupon errors accessible on the cart page * Add styles to coupon field with error on checkout page * Rename coupon variables on cart * Focus coupon field with error before updating live region on cart page * Focus coupon field with error before updating live region on checkout page * Remove incorrect early return * Update coupon error notice test * Improve coupon error message semantics * Fix coupon errors for block based themes * Update tests to not look for coupon errors on the notice block * Rename coupon-error-message to coupon-error-notice * Fix notice if coupon doesn't exist on tests * FIx invalid coupon notice on classic theme test * Update test for coupon inline notice * Fix code formatting Co-authored-by: Darin Kotter <darin.kotter@gmail.com> * Fix code formatting Co-authored-by: Darin Kotter <darin.kotter@gmail.com> * Fix code formatting Co-authored-by: Darin Kotter <darin.kotter@gmail.com> * Fix code formatting Co-authored-by: Darin Kotter <darin.kotter@gmail.com> * Don't clear coupon input if code doesn't exist * Create coupon error notice mixin * Update coupon error notice styles * Coupon error notice T19 compatibility * Coupon error notice T17 compatibility * Coupon error notice TT1 compatibility * Coupon error notice TT compatibility * Coupon error notice TT2 compatibility * Coupon error notice TT3 compatibility * Add spaces around paramenters Co-authored-by: Darin Kotter <darin.kotter@gmail.com> * Add spaces around paramenters Co-authored-by: Darin Kotter <darin.kotter@gmail.com> * Replace $red SCSS variable with a CSS one * Keep input with the invalid coupon code after notice appears * Fix typo in comment Co-authored-by: Mike Jolley <mike.jolley@me.com> * Fix typo in comment Co-authored-by: Mike Jolley <mike.jolley@me.com> * Break notice message into two lines * Break if statement into two lines --------- Co-authored-by: Darin Kotter <darin.kotter@gmail.com> Co-authored-by: Mike Jolley <mike.jolley@me.com>
This commit is contained in:
parent
b4fd419f74
commit
d773bb483a
|
@ -2,13 +2,14 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState } from '@wordpress/element';
|
||||
import { useState, useRef } from '@wordpress/element';
|
||||
import Button from '@woocommerce/base-components/button';
|
||||
import LoadingMask from '@woocommerce/base-components/loading-mask';
|
||||
import { withInstanceId } from '@wordpress/compose';
|
||||
import {
|
||||
ValidatedTextInput,
|
||||
ValidationInputError,
|
||||
ValidatedTextInputHandle,
|
||||
Panel,
|
||||
} from '@woocommerce/blocks-components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
|
@ -55,6 +56,7 @@ export const TotalsCoupon = ( {
|
|||
validationErrorId: store.getValidationErrorId( textInputId ),
|
||||
};
|
||||
} );
|
||||
const inputRef = useRef< ValidatedTextInputHandle >( null );
|
||||
|
||||
const handleCouponSubmit: MouseEventHandler< HTMLButtonElement > = (
|
||||
e: MouseEvent< HTMLButtonElement >
|
||||
|
@ -65,6 +67,8 @@ export const TotalsCoupon = ( {
|
|||
if ( result ) {
|
||||
setCouponValue( '' );
|
||||
setIsCouponFormVisible( false );
|
||||
} else if ( inputRef.current?.focus ) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
|
@ -104,6 +108,7 @@ export const TotalsCoupon = ( {
|
|||
focusOnMount={ true }
|
||||
validateOnMount={ false }
|
||||
showError={ false }
|
||||
ref={ inputRef }
|
||||
/>
|
||||
<Button
|
||||
className="wc-block-components-totals-coupon__button"
|
||||
|
|
|
@ -26,6 +26,7 @@ import { getValidityMessageForInput } from '../../checkout/utils';
|
|||
import { ValidatedTextInputProps } from './types';
|
||||
|
||||
export type ValidatedTextInputHandle = {
|
||||
focus?: () => void;
|
||||
revalidate: () => void;
|
||||
};
|
||||
|
||||
|
@ -147,6 +148,9 @@ const ValidatedTextInput = forwardRef<
|
|||
forwardedRef,
|
||||
function () {
|
||||
return {
|
||||
focus() {
|
||||
inputRef.current?.focus();
|
||||
},
|
||||
revalidate() {
|
||||
validateInput( ! value );
|
||||
},
|
||||
|
|
|
@ -15,7 +15,10 @@ import {
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { CheckoutPage } from '../checkout/checkout.page';
|
||||
import { REGULAR_PRICED_PRODUCT_NAME } from '../checkout/constants';
|
||||
import {
|
||||
REGULAR_PRICED_PRODUCT_NAME,
|
||||
INVALID_COUPON,
|
||||
} from '../checkout/constants';
|
||||
|
||||
const test = base.extend< { checkoutPageObject: CheckoutPage } >( {
|
||||
checkoutPageObject: async ( { page }, use ) => {
|
||||
|
@ -41,7 +44,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
|
||||
} );
|
||||
|
||||
test( 'default block notice templates are visible', async ( {
|
||||
test( 'default block notice templates, except for coupon errors, are visible', async ( {
|
||||
frontendUtils,
|
||||
page,
|
||||
} ) => {
|
||||
|
@ -70,10 +73,10 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
} )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is visible.
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -89,7 +92,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
).toBeVisible();
|
||||
} );
|
||||
|
||||
test( 'custom block notice templates are visible by template overwrite', async ( {
|
||||
test( 'custom block notice templates, except for coupon errors, are visible by template overwrite', async ( {
|
||||
requestUtils,
|
||||
frontendUtils,
|
||||
page,
|
||||
|
@ -121,10 +124,10 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
page.getByText( 'BLOCK ERROR NOTICE: Coupon code already applied!' )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is visible.
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -140,7 +143,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
await requestUtils.activateTheme( BLOCK_THEME_SLUG );
|
||||
} );
|
||||
|
||||
test( 'classic notice templates are visible by template overwrite', async ( {
|
||||
test( 'classic notice templates, except for coupon errors, are visible by template overwrite', async ( {
|
||||
requestUtils,
|
||||
frontendUtils,
|
||||
page,
|
||||
|
@ -177,7 +180,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
// We're explicitly checking the CSS classes of the classic notices.
|
||||
await expect(
|
||||
page.locator( '.woocommerce-notices-wrapper .woocommerce-error' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -229,10 +232,10 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
} )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes and that the SVG is visible.
|
||||
// We're explicitly checking the CSS classes and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -249,4 +252,26 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
|
||||
await requestUtils.activateTheme( BLOCK_THEME_SLUG );
|
||||
} );
|
||||
|
||||
test( 'coupon inline notice is visible', async ( {
|
||||
frontendUtils,
|
||||
page,
|
||||
} ) => {
|
||||
await frontendUtils.goToCartShortcode();
|
||||
await page.getByPlaceholder( 'Coupon code' ).fill( INVALID_COUPON );
|
||||
await page.getByRole( 'button', { name: 'Apply coupon' } ).click();
|
||||
|
||||
await expect(
|
||||
page.getByText( `Coupon "${ INVALID_COUPON }" does not exist!`, {
|
||||
exact: true,
|
||||
} )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeHidden();
|
||||
|
||||
await expect( page.locator( '.coupon-error-notice' ) ).toBeVisible();
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -15,7 +15,10 @@ import {
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { CheckoutPage } from '../checkout/checkout.page';
|
||||
import { REGULAR_PRICED_PRODUCT_NAME } from '../checkout/constants';
|
||||
import {
|
||||
REGULAR_PRICED_PRODUCT_NAME,
|
||||
INVALID_COUPON,
|
||||
} from '../checkout/constants';
|
||||
|
||||
const test = base.extend< { checkoutPageObject: CheckoutPage } >( {
|
||||
checkoutPageObject: async ( { page }, use ) => {
|
||||
|
@ -43,7 +46,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
|
||||
} );
|
||||
|
||||
test( 'default classic notice templates are visible', async ( {
|
||||
test( 'default classic notice templates, except for coupon errors, are visible', async ( {
|
||||
frontendUtils,
|
||||
page,
|
||||
} ) => {
|
||||
|
@ -73,7 +76,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
// We're explicitly checking the CSS classes of the classic notices.
|
||||
await expect(
|
||||
page.locator( '.woocommerce-notices-wrapper .woocommerce-error' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -87,7 +90,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
).toBeVisible();
|
||||
} );
|
||||
|
||||
test( 'custom classic notice templates are visible by template overwrite', async ( {
|
||||
test( 'custom classic notice templates, except for coupon errors, are visible by template overwrite', async ( {
|
||||
requestUtils,
|
||||
frontendUtils,
|
||||
page,
|
||||
|
@ -124,7 +127,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
// We're explicitly checking the CSS classes of the classic notices.
|
||||
await expect(
|
||||
page.locator( '.woocommerce-notices-wrapper .woocommerce-error' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -142,7 +145,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
await requestUtils.activateTheme( CLASSIC_THEME_SLUG );
|
||||
} );
|
||||
|
||||
test( 'custom block notice templates are visible by template overwrite', async ( {
|
||||
test( 'custom block notice templates, except for coupon errors, are visible by template overwrite', async ( {
|
||||
requestUtils,
|
||||
frontendUtils,
|
||||
page,
|
||||
|
@ -174,10 +177,10 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
page.getByText( 'BLOCK ERROR NOTICE: Coupon code already applied!' )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is visible.
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -193,7 +196,7 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
await requestUtils.activateTheme( CLASSIC_THEME_SLUG );
|
||||
} );
|
||||
|
||||
test( 'default block notice templates are visible by filter', async ( {
|
||||
test( 'default block notice templates, except for coupon errors, are visible by filter', async ( {
|
||||
requestUtils,
|
||||
frontendUtils,
|
||||
page,
|
||||
|
@ -223,10 +226,10 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
page.getByText( 'Coupon code already applied!' )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes and that the SVG is visible.
|
||||
// We're explicitly checking the CSS classes and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeVisible();
|
||||
).toBeHidden();
|
||||
|
||||
await page.getByLabel( 'Remove Polo from cart' ).click();
|
||||
|
||||
|
@ -241,4 +244,26 @@ test.describe( 'Shopper → Notice Templates', () => {
|
|||
|
||||
await requestUtils.activateTheme( CLASSIC_THEME_SLUG );
|
||||
} );
|
||||
|
||||
test( 'coupon inline notice is visible', async ( {
|
||||
frontendUtils,
|
||||
page,
|
||||
} ) => {
|
||||
await frontendUtils.goToCartShortcode();
|
||||
await page.getByPlaceholder( 'Coupon code' ).fill( INVALID_COUPON );
|
||||
await page.getByRole( 'button', { name: 'Apply coupon' } ).click();
|
||||
|
||||
await expect(
|
||||
page.getByText( `Coupon "${ INVALID_COUPON }" does not exist!`, {
|
||||
exact: true,
|
||||
} )
|
||||
).toBeVisible();
|
||||
|
||||
// We're explicitly checking the CSS classes of the block notices, and that the SVG is hidden.
|
||||
await expect(
|
||||
page.locator( '.wc-block-components-notice-banner.is-error svg' )
|
||||
).toBeHidden();
|
||||
|
||||
await expect( page.locator( '.coupon-error-notice' ) ).toBeVisible();
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -6,3 +6,4 @@ export const FLAT_RATE_SHIPPING_NAME = 'Flat rate shipping';
|
|||
export const FLAT_RATE_SHIPPING_PRICE = '$10.00';
|
||||
export const DISCOUNTED_PRODUCT_NAME = 'Cap';
|
||||
export const REGULAR_PRICED_PRODUCT_NAME = 'Polo';
|
||||
export const INVALID_COUPON = 'invalidcoupon';
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: enhancement
|
||||
|
||||
Move focus into coupon input if there is an error
|
|
@ -300,3 +300,22 @@
|
|||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin coupon-error-notice-cart() {
|
||||
clear: left;
|
||||
color: var(--wc-red);
|
||||
flex-basis: 100%;
|
||||
float: none;
|
||||
font-size: 0.75em;
|
||||
margin-bottom: 0;
|
||||
margin-top: 8px;
|
||||
text-align: left;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@mixin coupon-error-notice-checkout() {
|
||||
color: var(--wc-red);
|
||||
display: block;
|
||||
font-size: 0.75em;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
|
|
@ -1353,3 +1353,22 @@ table.variations {
|
|||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coupon error notice
|
||||
*/
|
||||
.woocommerce-cart {
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
}
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1257,3 +1257,22 @@ table.variations {
|
|||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coupon error notice
|
||||
*/
|
||||
.woocommerce-cart {
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
}
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3085,3 +3085,22 @@ a.reset_variations {
|
|||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coupon error notice
|
||||
*/
|
||||
.woocommerce-cart {
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
}
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -630,7 +630,6 @@ ul.wc-tabs {
|
|||
|
||||
#coupon_code {
|
||||
float: left;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1152,3 +1151,23 @@ Notice messages (like 'Added to cart', 'Billing address needs to be filled in',
|
|||
content: '\2713';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coupon error notice
|
||||
*/
|
||||
.woocommerce-cart {
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1197,3 +1197,22 @@ ul.wc-tabs {
|
|||
content: "\2713";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coupon error notice
|
||||
*/
|
||||
.woocommerce-cart {
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
}
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2412,3 +2412,22 @@ a.reset_variations {
|
|||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Coupon error notice
|
||||
*/
|
||||
.woocommerce-cart {
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
}
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,6 +310,7 @@ a.added_to_cart {
|
|||
.coupon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#coupon_code {
|
||||
|
|
|
@ -152,6 +152,16 @@
|
|||
.button.alt {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.coupon-error-notice {
|
||||
clear: left;
|
||||
color: var(--wc-red);
|
||||
float: left;
|
||||
font-size: 0.75em;
|
||||
margin-bottom: 0;
|
||||
text-align: left;
|
||||
width: 48%;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
|
|
|
@ -1306,6 +1306,16 @@ p.demo_store,
|
|||
border-radius: 5px;
|
||||
}
|
||||
|
||||
form.checkout_coupon {
|
||||
.coupon-error-notice {
|
||||
@include coupon-error-notice-checkout();
|
||||
}
|
||||
|
||||
.input-text.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
||||
ul#shipping_method {
|
||||
list-style: none outside;
|
||||
margin: 0;
|
||||
|
@ -1971,6 +1981,14 @@ p.demo_store,
|
|||
padding: 6px 6px 5px;
|
||||
margin: 0 4px 0 0;
|
||||
outline: 0;
|
||||
|
||||
&.has-error:focus {
|
||||
border-color: var(--wc-red);
|
||||
}
|
||||
}
|
||||
|
||||
td.actions .coupon .coupon-error-notice {
|
||||
@include coupon-error-notice-cart();
|
||||
}
|
||||
|
||||
input {
|
||||
|
|
|
@ -104,7 +104,7 @@ jQuery( function ( $ ) {
|
|||
// Remove errors
|
||||
if ( ! preserve_notices ) {
|
||||
$(
|
||||
'.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success'
|
||||
'.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success, .coupon-error-notice'
|
||||
).remove();
|
||||
}
|
||||
|
||||
|
@ -135,12 +135,30 @@ jQuery( function ( $ ) {
|
|||
if ( $( '.woocommerce-checkout' ).length ) {
|
||||
$( document.body ).trigger( 'update_checkout' );
|
||||
}
|
||||
|
||||
// Store the old coupon error message and value before the
|
||||
// .woocommerce-cart-form is replaced with the new form.
|
||||
var $old_coupon_field_val = $( '#coupon_code' ).val();
|
||||
var $old_coupon_error_msg = $( '#coupon_code' )
|
||||
.closest( '.coupon' )
|
||||
.find( '.coupon-error-notice' );
|
||||
|
||||
$( '.woocommerce-cart-form' ).replaceWith( $new_form );
|
||||
$( '.woocommerce-cart-form' )
|
||||
.find( ':input[name="update_cart"]' )
|
||||
.prop( 'disabled', true );
|
||||
|
||||
if ( preserve_notices && $old_coupon_error_msg.length > 0 ) {
|
||||
var $new_coupon_field = $( '.woocommerce-cart-form' ).find( '#coupon_code' );
|
||||
var $new_coupon_field_wrapper = $new_coupon_field.closest( '.coupon' );
|
||||
|
||||
$new_coupon_field.val( $old_coupon_field_val );
|
||||
// The coupon input with error needs to be focused before adding the live region
|
||||
// with the error message, otherwise the screen reader won't read it.
|
||||
$new_coupon_field.focus();
|
||||
show_coupon_error( $old_coupon_error_msg, $new_coupon_field_wrapper, true );
|
||||
}
|
||||
|
||||
if ( $notices.length > 0 ) {
|
||||
show_notice( $notices );
|
||||
}
|
||||
|
@ -176,6 +194,43 @@ jQuery( function ( $ ) {
|
|||
$target.prepend( html_element );
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows coupon form errors.
|
||||
*
|
||||
* @param {string|object} html_element The HTML string response after applying an invalid coupon or a jQuery element.
|
||||
* @param {Object} $target Coupon field wrapper jQuery element.
|
||||
* @param {boolean} is_live_region Whether role="alert" should be added or not.
|
||||
*/
|
||||
var show_coupon_error = function ( html_element, $target, is_live_region ) {
|
||||
if ( $target.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $coupon_error_el = '';
|
||||
|
||||
if ( typeof html_element === 'string' ) {
|
||||
var msg = $( $.parseHTML( html_element ) ).text().trim();
|
||||
|
||||
if ( msg === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$coupon_error_el = $( '<p class="coupon-error-notice" id="coupon-error-notice">' + msg + '</p>' );
|
||||
} else {
|
||||
$coupon_error_el = html_element;
|
||||
}
|
||||
|
||||
if ( is_live_region ) {
|
||||
$coupon_error_el.attr( 'role', 'alert' );
|
||||
}
|
||||
|
||||
$target.find( '#coupon_code' )
|
||||
.addClass( 'has-error' )
|
||||
.attr( 'aria-invalid', 'true' )
|
||||
.attr( 'aria-describedby', 'coupon-error-notice' );
|
||||
$target.append( $coupon_error_el );
|
||||
};
|
||||
|
||||
/**
|
||||
* Object to handle AJAX calls for cart shipping changes.
|
||||
*/
|
||||
|
@ -315,6 +370,7 @@ jQuery( function ( $ ) {
|
|||
this.apply_coupon = this.apply_coupon.bind( this );
|
||||
this.remove_coupon_clicked =
|
||||
this.remove_coupon_clicked.bind( this );
|
||||
this.remove_coupon_error = this.remove_coupon_error.bind( this );
|
||||
this.quantity_update = this.quantity_update.bind( this );
|
||||
this.item_remove_clicked = this.item_remove_clicked.bind( this );
|
||||
this.item_restore_clicked = this.item_restore_clicked.bind( this );
|
||||
|
@ -358,6 +414,11 @@ jQuery( function ( $ ) {
|
|||
'.woocommerce-cart-form .cart_item :input',
|
||||
this.input_changed
|
||||
);
|
||||
$( document ).on(
|
||||
'blur change input',
|
||||
'#coupon_code',
|
||||
this.remove_coupon_error
|
||||
);
|
||||
|
||||
$( '.woocommerce-cart-form :input[name="update_cart"]' ).prop(
|
||||
'disabled',
|
||||
|
@ -525,16 +586,28 @@ jQuery( function ( $ ) {
|
|||
dataType: 'html',
|
||||
success: function ( response ) {
|
||||
$(
|
||||
'.woocommerce-error, .woocommerce-message, .woocommerce-info, .is-error, .is-info, .is-success'
|
||||
'.woocommerce-error, .woocommerce-message, .woocommerce-info, ' +
|
||||
'.is-error, .is-info, .is-success, .coupon-error-notice'
|
||||
).remove();
|
||||
show_notice( response );
|
||||
|
||||
// We only want to show coupon notices if they are not errors.
|
||||
// Coupon errors are shown under the input.
|
||||
if ( response.indexOf( 'woocommerce-error' ) === -1 && response.indexOf( 'is-error' ) === -1 ) {
|
||||
show_notice( response );
|
||||
} else {
|
||||
var $coupon_wrapper = $text_field.closest( '.coupon' );
|
||||
|
||||
if ( $coupon_wrapper.length > 0 ) {
|
||||
show_coupon_error( response, $coupon_wrapper, false );
|
||||
}
|
||||
}
|
||||
|
||||
$( document.body ).trigger( 'applied_coupon', [
|
||||
coupon_code,
|
||||
] );
|
||||
},
|
||||
complete: function () {
|
||||
unblock( $form );
|
||||
$text_field.val( '' );
|
||||
cart.update_cart( true );
|
||||
},
|
||||
} );
|
||||
|
@ -578,6 +651,21 @@ jQuery( function ( $ ) {
|
|||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle when the coupon input loses focus.
|
||||
*
|
||||
* @param {Object} evt The JQuery event
|
||||
*/
|
||||
remove_coupon_error: function ( evt ) {
|
||||
$( evt.currentTarget )
|
||||
.removeClass( 'has-error' )
|
||||
.removeAttr( 'aria-invalid' )
|
||||
.removeAttr( 'aria-describedby' )
|
||||
.closest( '.coupon' )
|
||||
.find( '.coupon-error-notice' )
|
||||
.remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a cart Quantity Update
|
||||
*
|
||||
|
|
|
@ -535,7 +535,8 @@ jQuery( function( $ ) {
|
|||
wc_checkout_form.detachUnloadEventsOnSubmit();
|
||||
|
||||
try {
|
||||
if ( 'success' === result.result && $form.triggerHandler( 'checkout_place_order_success', [ result, wc_checkout_form ] ) !== false ) {
|
||||
if ( 'success' === result.result &&
|
||||
$form.triggerHandler( 'checkout_place_order_success', [ result, wc_checkout_form ] ) !== false ) {
|
||||
if ( -1 === result.redirect.indexOf( 'https://' ) || -1 === result.redirect.indexOf( 'http://' ) ) {
|
||||
window.location = result.redirect;
|
||||
} else {
|
||||
|
@ -614,7 +615,8 @@ jQuery( function( $ ) {
|
|||
init: function() {
|
||||
$( document.body ).on( 'click', 'a.showcoupon', this.show_coupon_form );
|
||||
$( document.body ).on( 'click', '.woocommerce-remove-coupon', this.remove_coupon );
|
||||
$( 'form.checkout_coupon' ).hide().on( 'submit', this.submit );
|
||||
$( document.body ).on( 'blur change input', '#coupon_code', this.remove_coupon_error );
|
||||
$( 'form.checkout_coupon' ).hide().on( 'submit', this.submit.bind( this ) );
|
||||
},
|
||||
show_coupon_form: function() {
|
||||
$( '.checkout_coupon' ).slideToggle( 400, function() {
|
||||
|
@ -622,8 +624,36 @@ jQuery( function( $ ) {
|
|||
});
|
||||
return false;
|
||||
},
|
||||
submit: function() {
|
||||
var $form = $( this );
|
||||
show_coupon_error: function( html_element, $target ) {
|
||||
if ( $target.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = $( $.parseHTML( html_element ) ).text().trim();
|
||||
|
||||
if ( msg === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target.find( '#coupon_code' )
|
||||
.focus()
|
||||
.addClass( 'has-error' )
|
||||
.attr( 'aria-invalid', 'true' )
|
||||
.attr( 'aria-describedby', 'coupon-error-notice' );
|
||||
$target.append( '<span class="coupon-error-notice" id="coupon-error-notice" role="alert">' + msg + '</span>' );
|
||||
},
|
||||
remove_coupon_error: function( evt ) {
|
||||
$( evt.currentTarget )
|
||||
.removeClass( 'has-error' )
|
||||
.removeAttr( 'aria-invalid' )
|
||||
.removeAttr( 'aria-describedby' )
|
||||
.next( '.coupon-error-notice' )
|
||||
.remove();
|
||||
},
|
||||
submit: function( evt ) {
|
||||
var $form = $( evt.currentTarget );
|
||||
var $coupon_field = $form.find( '#coupon_code' );
|
||||
var self = this;
|
||||
|
||||
if ( $form.is( '.processing' ) ) {
|
||||
return false;
|
||||
|
@ -647,13 +677,20 @@ jQuery( function( $ ) {
|
|||
type: 'POST',
|
||||
url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'apply_coupon' ),
|
||||
data: data,
|
||||
success: function( code ) {
|
||||
success: function( response ) {
|
||||
$( '.woocommerce-error, .woocommerce-message, .is-error, .is-success' ).remove();
|
||||
$form.removeClass( 'processing' ).unblock();
|
||||
|
||||
if ( code ) {
|
||||
$form.before( code );
|
||||
$form.slideUp();
|
||||
if ( response ) {
|
||||
// We only want to show coupon notices if they are no errors.
|
||||
// Coupon errors are shown under the input.
|
||||
if ( response.indexOf( 'woocommerce-error' ) === -1 && response.indexOf( 'is-error' ) === -1 ) {
|
||||
$form.slideUp( 400, function() {
|
||||
$form.before( response );
|
||||
} );
|
||||
} else {
|
||||
self.show_coupon_error( response, $coupon_field.parent() );
|
||||
}
|
||||
|
||||
$( document.body ).trigger( 'applied_coupon_in_checkout', [ data.coupon_code ] );
|
||||
$( document.body ).trigger( 'update_checkout', { update_shipping_method: false } );
|
||||
|
@ -699,6 +736,7 @@ jQuery( function( $ ) {
|
|||
|
||||
// Remove coupon code from coupon field
|
||||
$( 'form.checkout_coupon' ).find( 'input[name="coupon_code"]' ).val( '' );
|
||||
$( 'form.checkout_coupon' ).slideUp();
|
||||
}
|
||||
},
|
||||
error: function ( jqXHR ) {
|
||||
|
|
Loading…
Reference in New Issue