Enhancement: Tax settings conflict warning (#36010)

* Add WIP error handling when conflicting settings chosen

* add: enhance tax settings conflict warning

* Fix WC_Settings_Tax_Test::test_get_settings_for_default_section

* Wrap texts in i18n method

* Add snackbar

Co-authored-by: Adrian Duffell <9312929+adrianduffell@users.noreply.github.com>
Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
This commit is contained in:
RJ 2022-12-15 21:04:28 +08:00 committed by GitHub
parent c79de94c0f
commit cc9f4b65fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 274 additions and 1 deletions

View File

@ -17,6 +17,7 @@ import { PageLayout, EmbedLayout, PrimaryLayout as NoticeArea } from './layout';
import { CustomerEffortScoreTracksContainer } from './customer-effort-score-tracks'; import { CustomerEffortScoreTracksContainer } from './customer-effort-score-tracks';
import { EmbeddedBodyLayout } from './embedded-body-layout'; import { EmbeddedBodyLayout } from './embedded-body-layout';
import { WcAdminPaymentsGatewaysBannerSlot } from './payments/payments-settings-banner-slotfill'; import { WcAdminPaymentsGatewaysBannerSlot } from './payments/payments-settings-banner-slotfill';
import { WcAdminConflictErrorSlot } from './settings/conflict-error-slotfill.js';
// Modify webpack pubilcPath at runtime based on location of WordPress Plugin. // Modify webpack pubilcPath at runtime based on location of WordPress Plugin.
// eslint-disable-next-line no-undef,camelcase // eslint-disable-next-line no-undef,camelcase
@ -94,6 +95,12 @@ if ( appRoot ) {
); );
} }
const isTaxPage = document.getElementById( 'wc_conflict_error_slotfill' );
if ( isTaxPage ) {
render( WcAdminConflictErrorSlot(), isTaxPage );
}
const wrap = const wrap =
wpBody.querySelector( '.wrap.woocommerce' ) || wpBody.querySelector( '.wrap.woocommerce' ) ||
document.querySelector( '#wpbody-content > .woocommerce' ) || document.querySelector( '#wpbody-content > .woocommerce' ) ||

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.57465 3.21635L1.51632 14.9997C1.37079 15.2517 1.29379 15.5374 1.29298 15.8284C1.29216 16.1195 1.36756 16.4056 1.51167 16.6585C1.65579 16.9113 1.86359 17.122 2.11441 17.2696C2.36523 17.4171 2.65032 17.4965 2.94132 17.4997H17.058C17.349 17.4965 17.6341 17.4171 17.8849 17.2696C18.1357 17.122 18.3435 16.9113 18.4876 16.6585C18.6317 16.4056 18.7071 16.1195 18.7063 15.8284C18.7055 15.5374 18.6285 15.2517 18.483 14.9997L11.4247 3.21635C11.2761 2.97144 11.0669 2.76895 10.8173 2.62842C10.5677 2.48789 10.2861 2.41406 9.99965 2.41406C9.71321 2.41406 9.43159 2.48789 9.18199 2.62842C8.93238 2.76895 8.72321 2.97144 8.57465 3.21635V3.21635Z" stroke="#E65054" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 7.5V10.8333" stroke="#E65054" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 14.168H10.0083" stroke="#E65054" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,200 @@
/**
* External dependencies
*/
import { registerPlugin, PluginArea } from '@wordpress/plugins';
import { __ } from '@wordpress/i18n';
import interpolateComponents from '@automattic/interpolate-components';
import {
Button,
Card,
CardBody,
createSlotFill,
SlotFillProvider,
} from '@wordpress/components';
import { Icon, closeSmall } from '@wordpress/icons';
import { useEffect, useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
/**
* Internal dependencies
*/
import './conflict-error-slotfill.scss';
import warningIcon from './alert-triangle-icon.svg';
const { Fill, Slot } = createSlotFill( '__EXPERIMENTAL__WcAdminConflictError' );
const LearnMore = () => (
<Button
href="https://woocommerce.com/document/setting-up-taxes-in-woocommerce/"
target="_blank"
>
{ __( 'Learn more', 'woocommerce' ) }
</Button>
);
const SettingsErrorFill = () => {
const [ dismissedConflictWarning, setDismissedConflictWarning ] =
useState( false );
const [ pricesEnteredWithTaxSetting, setMainVal ] = useState(
document.forms.mainform.elements.woocommerce_prices_include_tax
.value === 'yes'
? 'incl'
: 'excl'
);
const [ displayPricesInShopWithTaxSetting, setDisplayShop ] = useState(
/** We're using jQuery in this file because the select boxes are implemented using select2 and can only be interacted with using jQuery */
window.jQuery( '#woocommerce_tax_display_shop' ).val()
);
const [ displayPricesInCartWithTaxSetting, setDisplayCart ] = useState(
/** We're using jQuery in this file because the select boxes are implemented using select2 and can only be interacted with using jQuery */
window.jQuery( '#woocommerce_tax_display_cart' ).val()
);
const { createNotice } = useDispatch( noticesStore );
const handleApplyRecommendedSettings = () => {
/** We're using jQuery in this file because the select boxes are implemented using select2 and can only be interacted with using jQuery */
// eslint-disable-next-line no-undef
window
.jQuery( '#woocommerce_tax_display_shop' )
.val( pricesEnteredWithTaxSetting )
.trigger( 'change' );
window
.jQuery( '#woocommerce_tax_display_cart' )
.val( pricesEnteredWithTaxSetting )
.trigger( 'change' );
createNotice(
'success',
__( 'Recommended settings applied.', 'woocommerce' )
);
};
const ApplyRecommendedSettingsButton = () => (
<Button variant="primary" onClick={ handleApplyRecommendedSettings }>
{ __( 'Use recommended settings', 'woocommerce' ) }
</Button>
);
useEffect( () => {
document
.querySelectorAll( "input[name='woocommerce_prices_include_tax']" )
.forEach( ( input ) => {
input.addEventListener( 'change', () =>
setMainVal(
document.forms.mainform.elements
.woocommerce_prices_include_tax.value === 'yes'
? 'incl'
: 'excl'
)
);
} );
}, [] );
useEffect( () => {
window
.jQuery( '#woocommerce_tax_display_shop' )
.on( 'click change', () =>
setDisplayShop(
document.getElementById( 'woocommerce_tax_display_shop' )
.value
)
);
}, [] );
useEffect( () => {
window
.jQuery( '#woocommerce_tax_display_cart' )
.on( 'click change', () =>
setDisplayCart(
document.getElementById( 'woocommerce_tax_display_cart' )
.value
)
);
}, [] );
const [ isConflict, setIsConflict ] = useState( false );
useEffect( () => {
if (
displayPricesInShopWithTaxSetting === pricesEnteredWithTaxSetting &&
displayPricesInCartWithTaxSetting === pricesEnteredWithTaxSetting
) {
setIsConflict( false );
} else {
setIsConflict( true );
}
}, [
displayPricesInCartWithTaxSetting,
displayPricesInShopWithTaxSetting,
pricesEnteredWithTaxSetting,
] );
if ( ! isConflict || dismissedConflictWarning ) {
return <Fill></Fill>;
}
return (
<Fill>
<div className="woocommerce_tax_settings_conflict_error">
<Card>
<CardBody className="woocommerce_tax_settings_conflict_error_card_body">
<div>
<img
className="woocommerce_tax_settings_conflict_error_card_body__warning_icon"
src={ warningIcon }
alt="Warning Icon"
/>
</div>
<div>
<div className="woocommerce_tax_settings_conflict_error_card_body__body_text">
<p style={ { fontSize: 13 } }>
{ interpolateComponents( {
mixedString: __(
'{{b}}Inconsistent tax settings:{{/b}} To avoid possible rounding errors, prices should be entered and displayed consistently in all locations either including, or excluding taxes.',
'woocommerce'
),
components: {
b: <b />,
},
} ) }
</p>
</div>
<div className="woocommerce_tax_settings_conflict_error_card_body__buttons">
<ApplyRecommendedSettingsButton /> <LearnMore />
</div>
</div>
<div>
<Button
className="woocommerce_tax_settings_conflict_error_card_body__close_icon"
onClick={ () =>
setDismissedConflictWarning( true )
}
>
<Icon icon={ closeSmall } />
</Button>
</div>
</CardBody>
</Card>
</div>
</Fill>
);
};
export const WcAdminConflictErrorSlot = () => {
return (
<>
<SlotFillProvider>
<Slot />
<PluginArea scope="woocommerce-settings" />
</SlotFillProvider>
</>
);
};
registerPlugin( 'woocommerce-admin-tax-settings-conflict-warning', {
scope: 'woocommerce-settings',
render: SettingsErrorFill,
} );

View File

@ -0,0 +1,39 @@
.woocommerce_tax_settings_conflict_error {
padding: 15px 10px;
min-width: 400px;
width: 400px;
}
.woocommerce_tax_settings_conflict_error_card_body {
display: flex;
flex-direction: row;
padding-right: 16px !important; // override core styles
}
.woocommerce_tax_settings_conflict_error_card_body__warning_icon {
width: 20px;
height: 20px;
margin-block: 10px;
margin-right: 16px;
}
.woocommerce_tax_settings_conflict_error_card_body__body_text {
margin-bottom: 16px;
}
.woocommerce_tax_settings_conflict_error_card_body__close_icon {
padding: 0;
height: 24px;
color: #808080;
}
.woocommerce_admin_tax_settings_slotfill_th,
.woocommerce_admin_tax_settings_slotfill_td {
padding: 0 !important;
}
[aria-labelledby='select2-woocommerce_tax_display_shop-container'].is-conflict {
border: #f00 !important;
border-style: solid !important;
border-width: 1px !important;
}

View File

@ -0,0 +1,4 @@
Significance: minor
Type: enhancement
Add warning banner that informs the user if they have conflicting tax display settings

View File

@ -27,7 +27,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
$this->label = __( 'Tax', 'woocommerce' ); $this->label = __( 'Tax', 'woocommerce' );
add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 ); add_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
add_action( 'woocommerce_admin_field_conflict_error', array( $this, 'conflict_error' ) );
if ( wc_tax_enabled() ) { if ( wc_tax_enabled() ) {
add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) ); add_action( 'woocommerce_sections_' . $this->id, array( $this, 'output_sections' ) );
add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) ); add_action( 'woocommerce_settings_' . $this->id, array( $this, 'output' ) );
@ -35,6 +35,21 @@ class WC_Settings_Tax extends WC_Settings_Page {
} }
} }
/**
* Creates the React mount point for the embedded banner.
*/
public function conflict_error() {
?>
<tr valign="top">
<th scope="row" class="titledesc woocommerce_admin_tax_settings_slotfill_th">
</th>
<td class="forminp forminp-text woocommerce_admin_tax_settings_slotfill_td">
<div id="wc_conflict_error_slotfill"> </div>
</td>
</tr>
<?php
}
/** /**
* Add this page to settings. * Add this page to settings.
* *

View File

@ -97,6 +97,8 @@ $settings = array(
), ),
), ),
array( 'type' => 'conflict_error' ), // React mount point for embedded banner slotfill.
array( array(
'title' => __( 'Price display suffix', 'woocommerce' ), 'title' => __( 'Price display suffix', 'woocommerce' ),
'id' => 'woocommerce_price_display_suffix', 'id' => 'woocommerce_price_display_suffix',

View File

@ -64,6 +64,7 @@ class WC_Settings_Tax_Test extends WC_Settings_Unit_Test_Case {
'woocommerce_tax_display_cart' => 'select', 'woocommerce_tax_display_cart' => 'select',
'woocommerce_price_display_suffix' => 'text', 'woocommerce_price_display_suffix' => 'text',
'woocommerce_tax_total_display' => 'select', 'woocommerce_tax_total_display' => 'select',
'' => 'conflict_error',
); );
$this->assertEquals( $expected, $settings_ids_and_types ); $this->assertEquals( $expected, $settings_ids_and_types );