Shipping Method Settings: Update modal contents (#40615)

This commit is contained in:
Paul Sealock 2023-10-17 14:52:33 +13:00 committed by paul sealock
parent 3ec81818d1
commit 1a0da8a62d
10 changed files with 413 additions and 93 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
Update Shipping Class modals to new style

View File

@ -4023,6 +4023,203 @@ table.wc_shipping {
display: table-row-group;
}
/**
* New Shipping Settings Refresh Modal Styles
**/
.wc-backbone-modal-add-shipping-method,
.wc-backbone-modal-shipping-method-settings,
.wc-shipping-class-modal {
font-family: $font-sf-pro-text;
font-size: 13px;
font-weight: 400;
line-height: 16px;
color: #1e1e1e;
.button-primary,
.button-primary:hover,
.button-primary:focus {
background-color: #3858E9;
border-radius: 2px;
color: #FFFFFF;
border-color: #3858e9
}
.button-primary:focus {
box-shadow: 0 0 0 1px #fff, 0 0 0 3px #3858E9;
}
.button-primary.disabled {
background-color: #3858E9 !important;
border-color: #3858E9 !important;
color: #8494EC !important;
}
&.wc-backbone-modal .wc-backbone-modal-content {
border-radius: 8px;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
max-width: 600px;
@media screen and (max-width: 782px) {
border-radius: none;
border-top: none;
border-bottom: none;
}
}
.wc-backbone-modal-main article {
padding: 0 32px 32px 32px;
}
.wc-backbone-modal-main header{
padding: 20px 32px;
}
.wc-backbone-modal-main footer {
padding: 20px 32px 12px 32px;
}
.wc-backbone-modal-main .wc-backbone-modal-header h1 {
font-weight: 400;
}
.wc-backbone-modal-main .wc-backbone-modal-header {
background-color: #fff;
border-bottom: none;
font-size: 20px;
line-height: 28px;
.modal-close-link {
border-left: none;
&:hover {
background-color: #fff;
}
}
}
.wc-backbone-modal-main footer {
box-shadow: none;
border-top: 1px solid #E0E0E0;
background-color: #fff;
.inner {
display: flex;
justify-content: space-between;
flex-direction: row-reverse;
}
.wc-shipping-zone-method-modal-info {
display: flex;
align-items: center;
font-size: 11px;
font-weight: 500;
line-height: 16px;
color: #787C82;
&.wc-shipping-zone-method-modal-info-inactive {
display: none;
}
}
}
.wc-shipping-zone-method-input {
input {
clip: rect(0 0 0 0);
clip-path: inset(100%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
&:checked + label {
outline: 2px solid #3858E9;
.dashicons-yes {
display: block;
}
}
}
label {
display: block;
width: 100%;
padding: 20px 16px;
outline: 1px solid #DDDDDD;
margin: 12px 0;
border-radius: 4px;
}
}
.dashicons-yes {
display: none;
color: #949494;
float: right;
}
.woocommerce-help-tip {
color: #949494;
&:after {
top: -6px;
font-size: 24px;
}
}
.wc-shipping-zone-method-fields {
& > label {
text-transform: uppercase;
font-size: 11px;
font-weight: 500;
line-height: 16px;
& > .woocommerce-help-tip {
display: none;
}
}
fieldset {
margin-bottom: 24px;
input,
select {
margin: 6px 0;
padding: 12px;
font-size: 13px;
line-height: 16px;
&:not([type="checkbox"]) {
width: 100%;
max-width: 100%;
}
&[type="checkbox"] {
border-radius: 2px;
margin: 4px 8px 6px 0;
& + .woocommerce-help-tip {
margin: 6px 0 4px 8px;
}
}
}
}
}
.wc-backbone-modal-action-inactive {
display: none;
}
.wc-shipping-zone-method-fields-help-text,
.wc-shipping-class-modal-help-text {
font-size: 12px;
font-weight: 400;
line-height: 16px;
color: #757575;
}
}
table {
tr,
tr:hover {
@ -4039,11 +4236,6 @@ table {
}
.wc-shipping-class-modal {
font-family: $font-sf-pro-text;
font-size: 13px;
font-weight: 400;
line-height: 18px;
.edit {
margin: 5px 0;
}
@ -4052,28 +4244,27 @@ table {
.edit > select {
width: 100%;
max-width: 100%;
padding: 12px;
}
.view {
font-weight: 600;
}
.wc-shipping-class-modal-action-inactive {
display: none;
font-size: 11px;
font-weight: 500;
line-height: 16px;
}
.wc-shipping-class-modal-input {
padding: 10px 0;
padding: 0 0 24px 0;
input {
font-size: 13px;
line-height: 16px;
}
}
.wc-shipping-class-count {
display: none;
}
.wc-shipping-class-modal-help-text {
font-size: 12px;
line-height: 17px;
}
}
.wc-shipping-class-hide-sibling-view + .view {
@ -4477,8 +4668,6 @@ table.wc-shipping-classes {
}
.wc-modal-shipping-method-settings {
background: #f8f8f8;
padding: 1em !important;
form .form-table {
width: 100%;
@ -4531,24 +4720,6 @@ table.wc-shipping-classes {
}
}
.wc-backbone-modal .wc-shipping-zone-method-selector {
p {
margin-top: 0;
}
.wc-shipping-zone-method-description {
margin: 0.75em 1px 0;
line-height: 1.5em;
color: #999;
font-style: italic;
}
select {
width: 100%;
cursor: pointer;
}
}
img.help_tip {
margin: 0 0 0 9px;
vertical-align: middle;

View File

@ -105,6 +105,8 @@
$( document.body ).on( 'wc_backbone_modal_before_remove', this.onCloseConfigureShippingMethod );
$( document.body ).on( 'change', '.wc-shipping-zone-method-selector select', this.onChangeShippingMethodSelector );
$( document.body ).on( 'click', '.wc-shipping-zone-postcodes-toggle', this.onTogglePostcodes );
$( document.body ).on( 'wc_backbone_modal_validation', { view: this }, this.validateFormArguments );
$( document.body ).on( 'wc_backbone_modal_loaded', { view: this }, this.onModalLoaded );
},
onUpdateZoneRegionPicker: function( event ) {
var value = event.detail,
@ -326,6 +328,8 @@
event.preventDefault();
method.settings_html = shippingMethodView.reformatSettingsHTML( method.settings_html );
$( this ).WCBackboneModal({
template : 'wc-modal-shipping-method-settings',
variable : {
@ -400,6 +404,77 @@
$( '.wc-shipping-zone-method-selector select' ).trigger( 'change' );
},
/**
* The settings HTML is controlled and built by the settings api, so in order to refactor the
* markup, it needs to be manipulated here.
*/
reformatSettingsHTML: function( html ) {
const htmlWithoutTables = this.replaceHTMLTables( html );
const htmlWithMovedHelpTips = this.moveHTMLHelpTips( htmlWithoutTables );
return htmlWithMovedHelpTips;
},
moveHTMLHelpTips: function( html ) {
// These help tips aren't moved.
const helpTipsToIgnore = [ 'woocommerce_flat_rate_cost' ];
const htmlContent = $( html );
const labels = htmlContent.find( 'label' );
labels.each( ( i ) => {
const label = $( labels[ i ] );
const helpTip = label.find( '.woocommerce-help-tip' );
if ( helpTip.length === 0 ) {
return;
}
const id = label.attr( 'for' );
if ( helpTipsToIgnore.includes( id ) ) {
return;
}
// woocommerce_free_shipping_ignore_discounts gets a helpTip appended to its label. Otherwise, add the text as the last element in the fieldset.
if ( id === 'woocommerce_free_shipping_ignore_discounts' ) {
const input = htmlContent.find( `#${ id }` );
const fieldset = input.closest( 'fieldset' );
const inputLabel = fieldset.find( 'label' );
inputLabel.append( helpTip );
} else {
const text = helpTip.data( 'tip' );
const input = htmlContent.find( `#${ id }` );
const fieldset = input.closest( 'fieldset' );
if ( fieldset.length && fieldset.find( '.wc-shipping-zone-method-fields-help-text' ).length === 0 ) {
fieldset.append( `<div class="wc-shipping-zone-method-fields-help-text">${ text }</div>` );
}
}
// Coupon discounts doesn't get a title on Free Shipping.
if ( label.text().trim() === 'Coupons discounts' ) {
label.text( '' );
}
} );
return htmlContent.prop( 'outerHTML' );
},
replaceHTMLTables: function ( html ) {
// `<table class="form-table" />` elements added by the Settings API need to be removed.
// Modern browsers won't interpret other table elements like `td` not in a `table`, so
// Removing the `table` is sufficient.
const htmlContent = $( html );
const tables = htmlContent.find( 'table.form-table' );
tables.each( ( i ) => {
const table = $( tables[ i ] );
const div = $( '<div class="wc-shipping-zone-method-fields" />' );
div.html( table.html() );
table.replaceWith( div );
} );
return htmlContent.prop('outerHTML');
},
onAddShippingMethodSubmitted: function( event, target, posted_data, closeModal ) {
if ( 'wc-modal-add-shipping-method' === target ) {
shippingMethodView.block();
@ -439,20 +514,24 @@
shippingMethodView.unblock();
// Pop up next modal
$( this ).WCBackboneModal({
template : 'wc-modal-shipping-method-settings',
variable : {
instance_id : instance_id,
method : method,
status : 'new'
},
data : {
instance_id : instance_id,
method : method,
status : 'new'
}
});
if ( method.settings_html ) {
method.settings_html = shippingMethodView.reformatSettingsHTML( method.settings_html );
// Pop up next modal
$( this ).WCBackboneModal({
template : 'wc-modal-shipping-method-settings',
variable : {
instance_id : instance_id,
method : method,
status : 'new'
},
data : {
instance_id : instance_id,
method : method,
status : 'new'
}
});
}
$( document.body ).trigger( 'init_tooltips' );
@ -461,12 +540,62 @@
}, 'json' );
}
},
// Free Shipping has hidden field elements depending on data values.
possiblyHideFreeShippingRequirements: function( data ) {
if ( Object.keys( data ).includes( 'woocommerce_free_shipping_requires' ) ) {
const shouldHideRequirements = data.woocommerce_free_shipping_requires === null ||
data.woocommerce_free_shipping_requires === '' ||
data.woocommerce_free_shipping_requires === 'coupon';
const select = $( '#woocommerce_free_shipping_requires' );
const fieldset = select.closest( 'fieldset' );
const allOtherLabelElementsAfter = fieldset.nextAll( 'label' );
const allOtherFieldsetElementsAfter = fieldset.nextAll( 'fieldset' );
allOtherLabelElementsAfter.each( ( i ) => {
$( allOtherLabelElementsAfter[ i ] ).css( 'display', shouldHideRequirements ? 'none' : 'block' );
} );
allOtherFieldsetElementsAfter.each( ( i ) => {
$( allOtherFieldsetElementsAfter[ i ] ).css( 'display', shouldHideRequirements ? 'none' : 'block' );
} );
}
},
onModalLoaded: function( event, target ) {
if ( target === 'wc-modal-shipping-method-settings' ) {
const select = $( '#woocommerce_free_shipping_requires' );
if ( select.length > 0 ) {
event.data.view.possiblyHideFreeShippingRequirements( { woocommerce_free_shipping_requires: select.val() } );
}
}
},
validateFormArguments: function( event, target, data ) {
if ( target === 'wc-modal-add-shipping-method' ) {
if ( data.add_method_id ) {
const nextButton = document.getElementById( 'btn-next' );
nextButton.disabled = false;
nextButton.classList.remove( 'disabled' );
}
} else if ( target === 'wc-modal-shipping-method-settings' ) {
event.data.view.possiblyHideFreeShippingRequirements( data );
const requiredFields = [ 'woocommerce_free_shipping_title', 'woocommerce_flat_rate_title', 'woocommerce_local_pickup_title' ];
const requiredFieldPresent = requiredFields.find( ( field ) => {
return data.hasOwnProperty( field ) && field;
} );
if ( requiredFieldPresent ) {
const shouldDisable = data[ requiredFieldPresent ].length === 0;
const saveButton = document.getElementById( 'btn-ok' );
saveButton.disabled = shouldDisable;
saveButton.classList.toggle( 'disabled', shouldDisable );
}
}
},
onCloseConfigureShippingMethod: function( event, target, post_data, addButtonCalled ) {
if ( target === 'wc-modal-shipping-method-settings' ) {
var btnData = $( '#btn-ok' ).data();
if ( ! addButtonCalled && btnData && btnData.status === 'new' ) {
console.log( 'delete instance_id: ' + post_data.instance_id );
shippingMethodView.block();
var view = shippingMethodView,

View File

@ -460,7 +460,7 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
$settings_html = $this->generate_settings_html( $this->get_form_fields(), false );
}
return '<table class="form-table">' . $settings_html . '</table>';
return '<div class="wc-shipping-zone-method-fields">' . $settings_html . '</div>';
}
/**

View File

@ -42,8 +42,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<!-- 1. Add labelFor or some kind of attribute for semantic HTML-->
<script type="text/html" id="tmpl-wc-shipping-class-configure">
<div class="wc-backbone-modal">
<div class="wc-backbone-modal-content wc-shipping-class-modal" data-id="{{ data.term_id }}">
<div class="wc-backbone-modal wc-shipping-class-modal">
<div class="wc-backbone-modal-content" data-id="{{ data.term_id }}">
<section class="wc-backbone-modal-main" role="main">
<header class="wc-backbone-modal-header">
<h1><?php esc_html_e( 'Add shipping class', 'woocommerce' ); ?></h1>
@ -110,8 +110,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<footer>
<div class="inner">
<button id="btn-ok" disabled class="button button-primary button-large disabled">
<div class="wc-shipping-class-modal-action-{{ data.action === 'create' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
<div class="wc-shipping-class-modal-action-{{ data.action === 'edit' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
<div class="wc-backbone-modal-action-{{ data.action === 'create' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
<div class="wc-backbone-modal-action-{{ data.action === 'edit' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
</button>
</div>
</footer>

View File

@ -131,8 +131,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<?php
printf(
/* translators: %s: shipping method title */
esc_html__( '%s Settings', 'woocommerce' ),
'{{{ data.method.method_title }}}'
esc_html__( 'Configure %s', 'woocommerce' ),
'{{{ data.method.method_title.toLowerCase() }}}'
);
?>
</h1>
@ -148,7 +148,11 @@ if ( ! defined( 'ABSPATH' ) ) {
</article>
<footer>
<div class="inner">
<button id="btn-ok" data-status='{{ data.status }}' class="button button-primary button-large"><?php esc_html_e( 'Save', 'woocommerce' ); ?></button>
<button id="btn-ok" data-status='{{ data.status }}' class="button button-primary button-large">
<div class="wc-backbone-modal-action-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
<div class="wc-backbone-modal-action-{{ data.status === 'existing' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
</button>
<div class="wc-shipping-zone-method-modal-info wc-shipping-zone-method-modal-info-{{ data.status === 'existing' ? 'inactive' : 'active' }}"><?php esc_html_e( 'STEP 2 OF 2', 'woocommerce' ); ?></div>
</div>
</footer>
</section>
@ -162,32 +166,48 @@ if ( ! defined( 'ABSPATH' ) ) {
<div class="wc-backbone-modal-content">
<section class="wc-backbone-modal-main" role="main">
<header class="wc-backbone-modal-header">
<h1><?php esc_html_e( 'Add shipping method', 'woocommerce' ); ?></h1>
<h1><?php esc_html_e( 'Create shipping method', 'woocommerce' ); ?></h1>
<button class="modal-close modal-close-link dashicons dashicons-no-alt">
<span class="screen-reader-text"><?php esc_html_e( 'Close modal panel', 'woocommerce' ); ?></span>
</button>
</header>
<article>
<form action="" method="post">
<div class="wc-shipping-zone-method-selector">
<p><?php esc_html_e( 'Choose the shipping method you wish to add. Only shipping methods which support zones are listed.', 'woocommerce' ); ?></p>
<fieldset class="wc-shipping-zone-method-selector">
<legend class="screen-reader-text"><?php esc_html_e( 'Choose the shipping method you wish to add. Only shipping methods which support zones are listed.', 'woocommerce' ); ?></legend>
<?php
$methods = WC()->shipping()->load_shipping_methods();
<select name="add_method_id">
<?php
foreach ( WC()->shipping()->load_shipping_methods() as $method ) {
if ( ! $method->supports( 'shipping-zones' ) ) {
continue;
$methods_placed_in_order = array();
$first_methods_ids = array( 'free_shipping', 'flat_rate', 'local_pickup' );
foreach ( $first_methods_ids as $first_method_id ) {
foreach ( $methods as $key => $obj ) {
if ( $obj->id === $first_method_id ) {
$methods_placed_in_order[] = $obj;
unset( $methods[ $key ] );
break;
}
echo '<option data-description="' . esc_attr( wp_kses_post( wpautop( $method->get_method_description() ) ) ) . '" value="' . esc_attr( $method->id ) . '">' . esc_html( $method->get_method_title() ) . '</li>';
}
?>
</select>
</div>
}
$methods_placed_in_order = array_merge( $methods_placed_in_order, array_values( $methods ) );
foreach ( $methods_placed_in_order as $method ) {
if ( ! $method->supports( 'shipping-zones' ) ) {
continue;
}
$description = wp_kses_post( wpautop( $method->get_method_description() ) );
echo '<div class="wc-shipping-zone-method-input"><input type="radio" value="' . esc_attr( $method->id ) . '" id="' . esc_attr( $method->id ) . '" name="add_method_id"/><label for="' . esc_attr( $method->id ) . '">' . esc_html( $method->get_method_title() ) . ' ' . wc_help_tip( esc_html( $description ) ) . '<span class="dashicons dashicons-yes"></span></label></div>';
}
?>
</fieldset>
</form>
</article>
<footer>
<div class="inner">
<button id="btn-next" class="button button-primary button-large"><?php esc_html_e( 'Configure shipping method', 'woocommerce' ); ?></button>
<button id="btn-next" disabled class="button button-primary button-large disabled"><?php esc_html_e( 'Continue', 'woocommerce' ); ?></button>
<div class="wc-shipping-zone-method-modal-info"><?php esc_html_e( 'STEP 1 OF 2', 'woocommerce' ); ?></div>
</div>
</footer>
</section>

View File

@ -11,7 +11,7 @@ $cost_desc = __( 'Enter a cost (excl. tax) or sum, e.g. <code>10.00 * [qty]</cod
$settings = array(
'title' => array(
'title' => __( 'Method title', 'woocommerce' ),
'title' => __( 'Name', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Flat rate', 'woocommerce' ),

View File

@ -89,30 +89,30 @@ class WC_Shipping_Free_Shipping extends WC_Shipping_Method {
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Title', 'woocommerce' ),
'title' => __( 'Name', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => $this->method_title,
'desc_tip' => true,
),
'requires' => array(
'title' => __( 'Free shipping requires...', 'woocommerce' ),
'title' => __( 'Free shipping requires', 'woocommerce' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'default' => '',
'options' => array(
'' => __( 'N/A', 'woocommerce' ),
'' => __( 'No requirement', 'woocommerce' ),
'coupon' => __( 'A valid free shipping coupon', 'woocommerce' ),
'min_amount' => __( 'A minimum order amount', 'woocommerce' ),
'either' => __( 'A minimum order amount OR a coupon', 'woocommerce' ),
'both' => __( 'A minimum order amount AND a coupon', 'woocommerce' ),
'either' => __( 'A minimum order amount OR coupon', 'woocommerce' ),
'both' => __( 'A minimum order amount AND coupon', 'woocommerce' ),
),
),
'min_amount' => array(
'title' => __( 'Minimum order amount', 'woocommerce' ),
'type' => 'price',
'placeholder' => wc_format_localized_price( 0 ),
'description' => __( 'Users will need to spend this amount to get free shipping (if enabled above).', 'woocommerce' ),
'description' => __( 'Users will need to spend this amount to get free shipping.', 'woocommerce' ),
'default' => '0',
'desc_tip' => true,
),

View File

@ -85,7 +85,7 @@ class WC_Shipping_Local_Pickup extends WC_Shipping_Method {
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Title', 'woocommerce' ),
'title' => __( 'Name', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Local pickup', 'woocommerce' ),

View File

@ -82,9 +82,8 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
await page.getByPlaceholder( 'List 1 postcode per line' ).fill( maynePostal );
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
await page.locator('select[name="add_method_id"]')
.selectOption( { label: 'Local pickup' } );
await page.getByRole('button', { name: 'Configure shipping method' } ).last().click();
await page.getByText( 'Local pickup', { exact: true } ).click();
await page.getByRole('button', { name: 'Continue' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page.locator( '#btn-ok' ).click();
@ -143,9 +142,8 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
await page.locator('select[name="add_method_id"]')
.selectOption( { label: 'Free shipping' } );
await page.getByRole('button', { name: 'Configure shipping method' } ).last().click();
await page.getByText( 'Free shipping', { exact: true } ).click();
await page.getByRole('button', { name: 'Continue' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page.locator( '#btn-ok' ).click();
@ -200,9 +198,8 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
await page.keyboard.press('Escape');
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
await page.locator('select[name="add_method_id"]')
.selectOption( { label: 'Flat rate' } );
await page.getByRole('button', { name: 'Configure shipping method' } ).last().click();
await page.getByText( 'Flat rate', { exact: true } ).click();
await page.getByRole('button', { name: 'Continue' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page.locator( '#btn-ok' ).click();
@ -326,10 +323,9 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
await page.locator( 'text=Add shipping method' ).click();
await page
.locator( 'select[name=add_method_id]' )
.selectOption( 'flat_rate' );
await page.getByRole('button', { name: 'Configure shipping method' } ).last().click();
await page.getByText( 'Flat rate', { exact: true } ).click();
await page.getByRole('button', { name: 'Continue' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page.locator( '#btn-ok' ).click();