Merge Shipping Settings Legacy Refresh (#40983)

This commit is contained in:
Paul Sealock 2023-11-22 12:20:34 +13:00 committed by GitHub
commit e701512f80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1465 additions and 508 deletions

View File

@ -122,12 +122,12 @@ const spotlitElementsSelectors: Array< NonEmptySelectorArray > = [
// top left = table header cell for sort handles
'th.wc-shipping-zone-sort',
// bottom right = worldwide region cell
'tr.wc-shipping-zone-worldwide > td.wc-shipping-zone-region',
'tfoot.wc-shipping-zone-worldwide tr > td.wc-shipping-zone-region',
],
[
// selectors for rightmost column (shipping methods)
'th.wc-shipping-zone-methods',
'tr.wc-shipping-zone-worldwide > td.wc-shipping-zone-methods',
'tfoot.wc-shipping-zone-worldwide tr > td.wc-shipping-zone-methods',
],
];
@ -251,14 +251,14 @@ export const ShippingTour: React.FC< {
<>
<span>
{ __(
'We added a few shipping zones for you based on your location, but you can manage them at any time.',
"Specify the areas you'd like to ship to! Give each zone a name, then list the regions you'd like to include. Your regions can be as specific as a zip code or as broad as a country. Shoppers will only see the methods available in their region.",
'woocommerce'
) }
</span>
<br />
<span>
{ __(
'A shipping zone is a geographic area where a certain set of shipping methods are offered.',
"We've added some shipping zones to get you started — you can manage them by selecting Edit or Delete.",
'woocommerce'
) }
</span>
@ -275,9 +275,22 @@ export const ShippingTour: React.FC< {
name: 'shipping-methods',
heading: __( 'Shipping methods', 'woocommerce' ),
descriptions: {
desktop: __(
'We defaulted to some recommended shipping methods based on your store location, but you can manage them at any time within each shipping zone settings.',
'woocommerce'
desktop: (
<>
<span>
{ __(
"Add one or more shipping methods you'd like to offer to shoppers in your zones.",
'woocommerce'
) }
</span>
<br />
<span>
{ __(
"For example, we've added the “Free shipping” method for shoppers in your country. You can edit, add to, or remove shipping methods by selecting Edit or Delete.",
'woocommerce'
) }
</span>
</>
),
},
},
@ -323,7 +336,7 @@ export const ShippingTour: React.FC< {
heading: __( 'WooCommerce Shipping', 'woocommerce' ),
descriptions: {
desktop: __(
'Print USPS and DHL labels straight from your WooCommerce dashboard and save on shipping thanks to discounted rates. You can manage WooCommerce Shipping in this section.',
'Print USPS and DHL labels straight from your Woo dashboard and save on shipping thanks to discounted rates. You can manage WooCommerce Shipping in this section.',
'woocommerce'
),
},
@ -341,7 +354,7 @@ export const ShippingTour: React.FC< {
heading: __( 'WooCommerce Shipping', 'woocommerce' ),
descriptions: {
desktop: __(
'If youd like to speed up your process and print your shipping label straight from your WooCommerce dashboard, WooCommerce Shipping may be for you! ',
'If youd like to speed up your process and print your shipping label straight from your Woo dashboard, WooCommerce Shipping may be for you! ',
'woocommerce'
),
},

View File

@ -0,0 +1,19 @@
/**
* External dependencies
*/
import { useContext, useEffect } from '@wordpress/element';
import { CurrencyContext } from '@woocommerce/currency';
import { numberFormat } from '@woocommerce/number';
export const ShippingCurrencyContext = () => {
const context = useContext( CurrencyContext );
useEffect( () => {
window.wc.ShippingCurrencyContext =
window.wc.ShippingCurrencyContext || context;
window.wc.ShippingCurrencyNumberFormat =
window.wc.ShippingCurrencyNumberFormat || numberFormat;
}, [ context ] );
return null;
};

View File

@ -0,0 +1,32 @@
/**
* External dependencies
*/
import { render, createRoot } from '@wordpress/element';
/**
* Internal dependencies
*/
import { RegionPicker } from './region-picker';
import { ShippingCurrencyContext } from './currency-context';
const shippingZoneRegionPickerRoot = document.getElementById(
'wc-shipping-zone-region-picker-root'
);
const options = window.shippingZoneMethodsLocalizeScript?.region_options ?? [];
const initialValues = window.shippingZoneMethodsLocalizeScript?.locations ?? [];
const ShippingApp = () => (
<div>
<ShippingCurrencyContext />
<RegionPicker options={ options } initialValues={ initialValues } />
</div>
);
if ( shippingZoneRegionPickerRoot ) {
if ( createRoot ) {
createRoot( shippingZoneRegionPickerRoot ).render( <ShippingApp /> );
} else {
render( <ShippingApp />, shippingZoneRegionPickerRoot );
}
}

View File

@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { useState } from '@wordpress/element';
import { TreeSelectControl } from '@woocommerce/components';
export const RegionPicker = ( { options, initialValues } ) => {
const [ selected, setSelected ] = useState( initialValues );
const onChange = ( value ) => {
document.body.dispatchEvent(
/* global CustomEvent */
new CustomEvent( 'wc_region_picker_update', { detail: value } )
);
setSelected( value );
};
return (
<TreeSelectControl
value={ selected }
onChange={ onChange }
options={ options }
placeholder="Start typing to filter zones"
selectAllLabel="Select all countries"
individuallySelectParent
clearOnSelect={ false }
maxVisibleTags={ 5 }
/>
);
};

View File

@ -70,6 +70,7 @@ const wpAdminScripts = [
'product-import-tracking',
'variable-product-tour',
'product-category-metabox',
'shipping-settings-region-picker',
];
const getEntryPoints = () => {
const entryPoints = {

View File

@ -0,0 +1 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M8 7h2V5H8v2zm0 6h2v-2H8v2zm0 6h2v-2H8v2zm6-14v2h2V5h-2zm0 8h2v-2h-2v2zm0 6h2v-2h-2v2z"></path></svg>

After

Width:  |  Height:  |  Size: 230 B

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Update Shipping Settings to new experience.

View File

@ -18,6 +18,9 @@
@include loader();
}
$font-sf-pro-text: helveticaneue-light, "Helvetica Neue Light",
"Helvetica Neue", sans-serif;
.wc-addons-wrap {
.marketplace-header {
background-image: url(../images/marketplace-header-bg@2x.png);
@ -214,9 +217,6 @@
/**
* Marketplace related variables
*/
$font-sf-pro-text: helveticaneue-light, "Helvetica Neue Light",
"Helvetica Neue", sans-serif;
$font-sf-pro-display: sans-serif;
h1.search-form-title {
@ -3940,6 +3940,10 @@ table.wc_shipping {
}
.wc-shipping-zone-settings {
#zone_name {
width: 600px;
}
th {
padding: 24px 24px 24px 0;
}
@ -3974,9 +3978,12 @@ table.wc_shipping {
.wc-shipping-zone-postcodes-toggle {
margin: 0.5em 0 0;
font-size: 0.9em;
text-decoration: underline;
display: block;
font-family: $font-sf-pro-text;
font-size: 13px;
font-weight: 400;
line-height: 16px;
}
.wc-shipping-zone-postcodes-toggle + .wc-shipping-zone-postcodes {
@ -3984,15 +3991,28 @@ table.wc_shipping {
}
.wc-shipping-zone-postcodes {
textarea {
width: 100%;
max-width: 600px;
#zone_postcodes {
margin: 10px 0;
padding: 8px;
width: 100%;
}
.description {
font-size: 0.9em;
color: #999;
font-family: $font-sf-pro-text;
font-size: 13px;
font-weight: 400;
line-height: 18px;
color: #3C434A;
}
}
#wc-shipping-zone-region-picker-root {
width: 100%;
max-width: 600px;
}
}
.wc-shipping-zone-settings + p.submit {
@ -4003,6 +4023,262 @@ 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;
&.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-method-add-class-costs {
margin-top: -24px;
text-decoration: none;
color: #3858E9;
font-size: 12px;
line-height: 16px;
}
.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 var(--wp-admin-theme-color);
.dashicons-yes {
display: block;
}
}
}
label {
display: block;
width: 100%;
padding: 20px 16px;
outline: 1px solid #DDDDDD;
margin: 12px 0;
border-radius: 4px;
}
.wc-shipping-zone-method-input-help-text {
position: absolute;
bottom: -30px;
height: 30px;
display: none;
}
input:checked ~ .wc-shipping-zone-method-input-help-text {
display: block;
}
}
.wc-shipping-zone-method-selector {
position: relative;
}
.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 {
font-size: 12px;
font-weight: 500;
line-height: 16px;
& > .woocommerce-help-tip {
display: none;
}
& > .woocommerce-help-tip.wc-shipping-visible-help-text {
display: inline-block;
}
}
fieldset {
margin-bottom: 24px;
position: relative;
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-shipping-modal-price {
color: #757575;
&.wc-shipping-currency-position-left,
&.wc-shipping-currency-position-left_space {
&.wc-shipping-currency-size-1 {
padding-left: 28px;
}
&.wc-shipping-currency-size-2 {
padding-left: 33px;
}
&.wc-shipping-currency-size-3 {
padding-left: 38px;
}
&.wc-shipping-currency-size-4 {
padding-left: 43px;
}
&.wc-shipping-currency-size-5 {
padding-left: 48px;
}
}
&.wc-shipping-currency-position-right,
&.wc-shipping-currency-position-right_space {
padding-left: 12px;
}
}
}
}
.wc-shipping-zone-method-currency {
position: absolute;
top: 19px;
color: #757575;
&.wc-shipping-currency-position-left,
&.wc-shipping-currency-position-left_space {
left: 12px;
}
&.wc-shipping-currency-position-right,
&.wc-shipping-currency-position-right_space {
right: 12px;
}
}
}
.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;
}
}
.wc-backbone-modal-add-shipping-method .wc-backbone-modal-main article {
padding: 0 32px 50px 32px;
}
table {
tr,
tr:hover {
@ -4018,21 +4294,123 @@ table {
}
}
.wc-shipping-zones-heading .page-title-action {
display: inline-block;
.wc-shipping-class-modal {
.edit {
margin: 5px 0;
}
.edit > input,
.edit > select {
width: 100%;
max-width: 100%;
padding: 12px;
}
.view {
font-size: 12px;
font-weight: 500;
line-height: 16px;
}
.wc-shipping-class-modal-input {
padding: 0 0 24px 0;
input {
font-size: 13px;
line-height: 16px;
}
}
.wc-shipping-class-count {
display: none;
}
}
.wc-shipping-class-hide-sibling-view + .view {
display: none;
}
.wc-shipping-zones-heading {
display: flex;
align-items: center;
.button-primary {
display: inline-block;
margin: 0 12px;
}
}
.wc-shipping-zone-heading-help-text {
max-width: 800px;
}
.wc-shipping-zone-heading-help-text {
font-family: $font-sf-pro-text;
color: #3C434A;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 19px;
}
.wc-shipping-zone-help-text {
font-family: $font-sf-pro-text;
color: #3C434A;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 17px;
}
table.wc-shipping-zones,
table.wc-shipping-zone-methods,
table.wc-shipping-classes {
font-family: $font-sf-pro-text;
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: #000000;
&.widefat p {
color: #000000;
}
thead tr th {
color: #646970;
vertical-align: middle;
padding: 12px;
}
tbody tr td {
padding: 24px 12px;
&:last-child {
padding: 24px 32px 24px 12px;
}
}
tfoot tr td {
padding: 24px 12px;
background-color: #F6F7F7;
color: #000000;
vertical-align: top;
&:last-child {
padding: 24px 32px 24px 12px;
}
}
td,
th {
vertical-align: top;
line-height: 24px;
padding: 1em !important;
font-size: 14px;
background: #fff;
display: table-cell !important;
font-family: $font-sf-pro-text;
font-size: 14px;
font-weight: 400;
line-height: 20px;
color: #000000;
vertical-align: middle;
padding: 24px 12px;
li {
line-height: 24px;
@ -4045,10 +4423,6 @@ table.wc-shipping-classes {
}
thead {
th {
vertical-align: middle;
}
.wc-shipping-zone-sort {
text-align: center;
}
@ -4113,22 +4487,9 @@ table.wc-shipping-classes {
}
}
.wc-shipping-zone-method-rows {
tr:nth-child(even) td {
background: #f9f9f9;
}
}
tr.odd,
.wc-shipping-class-rows tr:nth-child(odd) {
tbody.wc-shipping-tables-tbody {
td {
background: #f9f9f9;
}
}
tbody.wc-shipping-zone-rows {
td {
border-top: 2px solid #f9f9f9;
border-top: 1px solid #C3C4C7;
}
tr:first-child {
@ -4136,6 +4497,13 @@ table.wc-shipping-classes {
border-top: 0;
}
}
.wc-shipping-zone-name,
.wc-shipping-zone-method-title,
.wc-shipping-class-name {
font-weight: 500;
}
}
tr.wc-shipping-zone-worldwide {
@ -4157,20 +4525,17 @@ table.wc-shipping-classes {
text-align: center;
&::before {
content: "\f333";
font-family: "Dashicons";
text-align: center;
line-height: 1;
color: #999;
content: "";
mask: url(../images/icons/move-icon.svg);
background-color: #1E1E1E;
display: block;
width: 17px;
float: left;
height: 100%;
line-height: 24px;
height: 24px;
}
&:hover::before {
color: #333;
background-color: #999;
}
}
@ -4182,7 +4547,7 @@ table.wc-shipping-classes {
font-family: "dashicons";
text-align: center;
line-height: 1;
color: #999;
color: #000;
display: block;
width: 17px;
float: left;
@ -4196,26 +4561,32 @@ table.wc-shipping-classes {
width: 25%;
}
.wc-shipping-zone-actions {
width: 13%;
text-align: right;
a.wc-shipping-zone-actions,
a.wc-shipping-zone-actions:hover {
color: #D63638;
}
a.wc-shipping-zone-action-edit,
a.wc-shipping-zone-action-edit:hover {
color: #0675C4;
}
}
.wc-shipping-class-description,
.wc-shipping-class-name,
.wc-shipping-class-slug,
.wc-shipping-zone-name,
.wc-shipping-class-actions,
.wc-shipping-zone-region {
input,
select,
textarea {
width: 100%;
}
a.wc-shipping-zone-delete,
a.wc-shipping-class-delete {
color: #a00;
}
a.wc-shipping-zone-delete:hover,
a.wc-shipping-class-delete:hover {
color: red;
}
}
.wc-shipping-class-count {
@ -4232,20 +4603,21 @@ table.wc-shipping-classes {
ul {
position: relative;
padding-right: 32px;
display: flex;
flex-wrap: wrap;
li {
color: #555;
color: #000;
display: inline;
margin: 0;
}
}
li::before {
content: ", ";
}
li:first-child::before {
content: "";
}
.wc-shipping-zone-method {
background-color: #F0F0F0;
margin: 0 3px 3px 0;
padding: 5px 9px;
border-radius: 5px;
}
.add_shipping_method {
@ -4279,17 +4651,13 @@ table.wc-shipping-classes {
.wc-shipping-zone-method-title {
width: 25%;
.wc-shipping-zone-method-delete {
color: red;
}
}
.wc-shipping-zone-method-enabled {
text-align: center;
a {
display: inline-block;
float: left;
}
.woocommerce-input-toggle {
@ -4360,8 +4728,6 @@ table.wc-shipping-classes {
}
.wc-modal-shipping-method-settings {
background: #f8f8f8;
padding: 1em !important;
form .form-table {
width: 100%;
@ -4370,7 +4736,7 @@ table.wc-shipping-classes {
tr {
th {
width: 30%;
width: 40%;
position: relative;
.woocommerce-help-tip {
@ -4395,6 +4761,7 @@ table.wc-shipping-classes {
width: auto;
min-width: 16px;
}
width: 40%;
}
td,
@ -4413,24 +4780,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;
@ -4637,10 +4986,6 @@ img.help_tip {
margin-top: 8px;
}
table.widefat th {
padding-right: inherit;
}
.wp-list-table .woocommerce-help-tip {
float: none;
}
@ -5674,7 +6019,7 @@ img.help_tip {
:not(#variable_product_options_inner),
&#product_attributes {
.toolbar:not(.expand-close-hidden) {
border-bottom: 1px solid #eee;
border-bottom: 1px solid #eee;
}
}
.toolbar {
@ -7112,6 +7457,7 @@ table.bar_chart {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 50%;
max-width: 100%;
min-width: 500px;
@ -7121,10 +7467,13 @@ table.bar_chart {
}
&.wc-backbone-modal-shipping-method-settings .wc-backbone-modal-content {
width: 75%;
min-width: 500px;
}
&.wc-backbone-modal-add-shipping-method .wc-backbone-modal-content article {
min-height: 180px
}
.select2-container {
width: 100% !important;
}
@ -7544,8 +7893,8 @@ table.bar_chart {
color: var(--wp-admin-theme-color, #007cba);
}
.select2-container.select2-container--focus
.select2-selection--single,
.select2-container.select2-container--focus .select2-selection--single,
.select2-container.select2-container--open .select2-selection--single,
.select2-container.select2-container--open
.select2-selection--single,
.select2-container.select2-container--open
@ -7555,7 +7904,7 @@ table.bar_chart {
}
.select2-container--default
.select2-results__option--highlighted[aria-selected],
.select2-results__option--highlighted[aria-selected],
.select2-container--default
.select2-results__option--highlighted[data-selected] {
background-color: var(--wp-admin-theme-color, #007cba);

View File

@ -54,8 +54,10 @@
events: {
'click .modal-close': 'closeButton',
'click #btn-ok' : 'addButton',
'click #btn-next' : 'nextButton',
'touchstart #btn-ok': 'addButton',
'keydown' : 'keyboardActions'
'keydown' : 'keyboardActions',
'input' : 'handleInputValidation'
},
resizeContent: function() {
var $content = $( '.wc-backbone-modal-content' ).find( 'article' );
@ -94,9 +96,9 @@
$( document.body ).trigger( 'wc_backbone_modal_loaded', this._target );
},
closeButton: function( e ) {
closeButton: function( e, addButtonCalled ) {
e.preventDefault();
$( document.body ).trigger( 'wc_backbone_modal_before_remove', this._target );
$( document.body ).trigger( 'wc_backbone_modal_before_remove', [ this._target, this.getFormData(), !!addButtonCalled ] );
this.undelegateEvents();
$( document ).off( 'focusin' );
$( document.body ).css({
@ -107,12 +109,21 @@
},
addButton: function( e ) {
$( document.body ).trigger( 'wc_backbone_modal_response', [ this._target, this.getFormData() ] );
this.closeButton( e );
this.closeButton( e, true );
},
getFormData: function() {
nextButton: function( e ) {
var context = this;
function closeModal() {
context.closeButton( e );
}
$( document.body ).trigger( 'wc_backbone_modal_next_response', [ this._target, this.getFormData(), closeModal ] );
},
getFormData: function( updating = true ) {
var data = {};
$( document.body ).trigger( 'wc_backbone_modal_before_update', this._target );
if ( updating ) {
$( document.body ).trigger( 'wc_backbone_modal_before_update', this._target );
}
$.each( $( 'form', this.$el ).serializeArray(), function( index, item ) {
if ( item.name.indexOf( '[]' ) !== -1 ) {
@ -126,6 +137,9 @@
return data;
},
handleInputValidation: function() {
$( document.body ).trigger( 'wc_backbone_modal_validation', [ this._target, this.getFormData( false ) ] );
},
keyboardActions: function( e ) {
var button = e.keyCode || e.which;
@ -134,7 +148,11 @@
13 === button &&
! ( e.target.tagName && ( e.target.tagName.toLowerCase() === 'input' || e.target.tagName.toLowerCase() === 'textarea' ) )
) {
this.addButton( e );
if ( $( '#btn-ok' ).length ) {
this.addButton( e );
} else if ( $( '#btn-next' ).length ) {
this.nextButton( e );
}
}
// ESC key

View File

@ -8,44 +8,16 @@
// Backbone model
ShippingClass = Backbone.Model.extend({
changes: {},
logChanges: function( changedRows ) {
var changes = this.changes || {};
_.each( changedRows, function( row, id ) {
changes[ id ] = _.extend( changes[ id ] || { term_id : id }, row );
} );
this.changes = changes;
this.trigger( 'change:classes' );
},
save: function() {
if ( _.size( this.changes ) ) {
$.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_classes_save_changes', {
wc_shipping_classes_nonce : data.wc_shipping_classes_nonce,
changes : this.changes
}, this.onSaveResponse, 'json' );
} else {
shippingClass.trigger( 'saved:classes' );
}
},
discardChanges: function( id ) {
var changes = this.changes || {};
// Delete all changes
delete changes[ id ];
// No changes? Disable save button.
if ( 0 === _.size( this.changes ) ) {
shippingClassView.clearUnloadConfirmation();
}
save: function( changes ) {
$.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_classes_save_changes', {
wc_shipping_classes_nonce : data.wc_shipping_classes_nonce,
changes,
}, this.onSaveResponse, 'json' );
},
onSaveResponse: function( response, textStatus ) {
if ( 'success' === textStatus ) {
if ( response.success ) {
shippingClass.set( 'classes', response.data.shipping_classes );
shippingClass.trigger( 'change:classes' );
shippingClass.changes = {};
shippingClass.trigger( 'saved:classes' );
} else if ( response.data ) {
window.alert( response.data );
@ -61,14 +33,11 @@
ShippingClassView = Backbone.View.extend({
rowTemplate: $row_template,
initialize: function() {
this.listenTo( this.model, 'change:classes', this.setUnloadConfirmation );
this.listenTo( this.model, 'saved:classes', this.clearUnloadConfirmation );
this.listenTo( this.model, 'saved:classes', this.render );
$tbody.on( 'change', { view: this }, this.updateModelOnChange );
$( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
$save_button.on( 'click', { view: this }, this.onSubmit );
$( document.body ).on( 'click', '.wc-shipping-class-add', { view: this }, this.onAddNewRow );
$( document.body ).on( 'click', '.wc-shipping-class-save-changes', { view: this }, this.onSubmit );
$( document.body ).on( 'click', '.wc-shipping-class-add-new', { view: this }, this.configureNewShippingClass );
$( document.body ).on( 'wc_backbone_modal_response', { view: this }, this.onConfigureShippingClassSubmitted );
$( document.body ).on( 'wc_backbone_modal_loaded', { view: this }, this.onLoadBackboneModal );
$( document.body ).on( 'wc_backbone_modal_validation', this.validateFormArguments );
},
block: function() {
$( this.el ).block({
@ -83,8 +52,8 @@
$( this.el ).unblock();
},
render: function() {
var classes = _.indexBy( this.model.get( 'classes' ), 'term_id' ),
view = this;
var classes = _.indexBy( this.model.get( 'classes' ), 'term_id' ),
view = this;
this.$el.empty();
this.unblock();
@ -123,114 +92,111 @@
$tr.find( '.edit' ).hide();
$tr.find( '.wc-shipping-class-edit' ).on( 'click', { view: this }, this.onEditRow );
$tr.find( '.wc-shipping-class-delete' ).on( 'click', { view: this }, this.onDeleteRow );
$tr.find( '.editing .wc-shipping-class-edit' ).trigger('click');
$tr.find( '.wc-shipping-class-cancel-edit' ).on( 'click', { view: this }, this.onCancelEditRow );
},
configureNewShippingClass: function( event ) {
event.preventDefault();
const term_id = 'new-1-' + Date.now();
// Editing?
if ( true === rowData.editing ) {
$tr.addClass( 'editing' );
$tr.find( '.wc-shipping-class-edit' ).trigger( 'click' );
$( this ).WCBackboneModal({
template : 'wc-shipping-class-configure',
variable : {
term_id,
action: 'create',
},
data : {
term_id,
action: 'create',
}
});
},
onConfigureShippingClassSubmitted: function( event, target, posted_data ) {
if ( target === 'wc-shipping-class-configure' ) {
const view = event.data.view;
const model = view.model;
const isNewRow = posted_data.term_id.includes( 'new-1-' );
const rowData = {
...posted_data,
};
if ( isNewRow ) {
rowData.newRow = true;
}
view.block();
model.save( {
[ posted_data.term_id ]: rowData
} );
}
},
onSubmit: function( event ) {
event.data.view.block();
event.data.view.model.save();
event.preventDefault();
},
onAddNewRow: function( event ) {
event.preventDefault();
var view = event.data.view,
model = view.model,
classes = _.indexBy( model.get( 'classes' ), 'term_id' ),
changes = {},
size = _.size( classes ),
newRow = _.extend( {}, data.default_shipping_class, {
term_id: 'new-' + size + '-' + Date.now(),
editing: true,
newRow : true
} );
changes[ newRow.term_id ] = newRow;
model.logChanges( changes );
view.renderRow( newRow );
$( '.wc-shipping-classes-blank-state' ).remove();
validateFormArguments: function( element, target, data ) {
const requiredFields = [ 'name', 'description' ];
const formIsComplete = Object.keys( data ).every( key => {
if ( ! requiredFields.includes( key ) ) {
return true;
}
if ( Array.isArray( data[ key ] ) ) {
return data[ key ].length && !!data[ key ][ 0 ];
}
return !!data[ key ];
} );
const createButton = document.getElementById( 'btn-ok' );
createButton.disabled = ! formIsComplete;
createButton.classList.toggle( 'disabled', ! formIsComplete );
},
onEditRow: function( event ) {
const term_id = $( this ).closest('tr').data('id');
const model = event.data.view.model;
const classes = _.indexBy( model.get( 'classes' ), 'term_id' );
const rowData = classes[ term_id ];
event.preventDefault();
$( this ).closest('tr').addClass('editing');
$( this ).closest('tr').find('.view').hide();
$( this ).closest('tr').find('.edit').show();
event.data.view.model.trigger( 'change:classes' );
$( this ).WCBackboneModal({
template : 'wc-shipping-class-configure',
variable : {
action: 'edit',
...rowData
},
data : {
action: 'edit',
...rowData
}
});
},
onLoadBackboneModal: function( event, target ) {
if ( 'wc-shipping-class-configure' === target ) {
const modalContent = $('.wc-backbone-modal-content');
const term_id = modalContent.data('id');
const model = event.data.view.model;
const classes = _.indexBy( model.get( 'classes' ), 'term_id' );
const rowData = classes[ term_id ];
if ( rowData ) {
// Support select boxes
$('.wc-backbone-modal-content').find( 'select' ).each( function() {
var attribute = $( this ).data( 'attribute' );
$( this ).find( 'option[value="' + rowData[ attribute ] + '"]' ).prop( 'selected', true );
} );
}
}
},
onDeleteRow: function( event ) {
var view = event.data.view,
model = view.model,
classes = _.indexBy( model.get( 'classes' ), 'term_id' ),
changes = {},
term_id = $( this ).closest('tr').data('id');
event.preventDefault();
if ( classes[ term_id ] ) {
delete classes[ term_id ];
changes[ term_id ] = _.extend( changes[ term_id ] || {}, { deleted : 'deleted' } );
model.set( 'classes', classes );
model.logChanges( changes );
}
view.block();
view.render();
model.save( {
[ term_id ]: {
term_id,
deleted: 'deleted',
}
} );
},
onCancelEditRow: function( event ) {
var view = event.data.view,
model = view.model,
row = $( this ).closest('tr'),
term_id = $( this ).closest('tr').data('id'),
classes = _.indexBy( model.get( 'classes' ), 'term_id' );
event.preventDefault();
model.discardChanges( term_id );
if ( classes[ term_id ] ) {
classes[ term_id ].editing = false;
row.after( view.rowTemplate( classes[ term_id ] ) );
view.initRow( classes[ term_id ] );
}
row.remove();
},
setUnloadConfirmation: function() {
this.needsUnloadConfirm = true;
$save_button.prop( 'disabled', false );
},
clearUnloadConfirmation: function() {
this.needsUnloadConfirm = false;
$save_button.attr( 'disabled', 'disabled' );
},
unloadConfirmation: function( event ) {
if ( event.data.view.needsUnloadConfirm ) {
event.returnValue = data.strings.unload_confirmation_msg;
window.event.returnValue = data.strings.unload_confirmation_msg;
return data.strings.unload_confirmation_msg;
}
},
updateModelOnChange: function( event ) {
var model = event.data.view.model,
$target = $( event.target ),
term_id = $target.closest( 'tr' ).data( 'id' ),
attribute = $target.data( 'attribute' ),
value = $target.val(),
classes = _.indexBy( model.get( 'classes' ), 'term_id' ),
changes = {};
if ( ! classes[ term_id ] || classes[ term_id ][ attribute ] !== value ) {
changes[ term_id ] = {};
changes[ term_id ][ attribute ] = value;
}
model.logChanges( changes );
}
} ),
shippingClass = new ShippingClass({
classes: data.classes

View File

@ -100,9 +100,22 @@
$( document.body ).on( 'click', '.wc-shipping-zone-method-settings', { view: this }, this.onConfigureShippingMethod );
$( document.body ).on( 'click', '.wc-shipping-zone-add-method', { view: this }, this.onAddShippingMethod );
$( document.body ).on( 'wc_backbone_modal_response', this.onConfigureShippingMethodSubmitted );
$( document.body ).on( 'wc_backbone_modal_response', this.onAddShippingMethodSubmitted );
$( document.body ).on( 'wc_region_picker_update', this.onUpdateZoneRegionPicker );
$( document.body ).on( 'wc_backbone_modal_next_response', this.onAddShippingMethodSubmitted );
$( 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,
attribute = 'zone_locations',
changes = {};
changes[ attribute ] = value;
shippingMethodView.model.set( attribute, value );
shippingMethodView.model.logChanges( changes );
},
onUpdateZone: function( event ) {
var view = event.data.view,
@ -308,22 +321,26 @@
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
method = methods[ instance_id ];
// Only load modal if supported
// Only load modal if supported.
if ( ! method.settings_html ) {
return true;
}
event.preventDefault();
method.settings_html = shippingMethodView.reformatSettingsHTML( method.settings_html );
$( this ).WCBackboneModal({
template : 'wc-modal-shipping-method-settings',
variable : {
instance_id : instance_id,
method : method
method : method,
status : 'existing'
},
data : {
instance_id : instance_id,
method : method
method : method,
status : 'existing'
}
});
@ -387,10 +404,124 @@
$( '.wc-shipping-zone-method-selector select' ).trigger( 'change' );
},
onAddShippingMethodSubmitted: function( event, target, posted_data ) {
/**
* 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 formattingFunctions = [
this.replaceHTMLTables,
this.moveAdvancedCostsHelpTip,
this.moveHTMLHelpTips,
this.addCurrencySymbol
];
return formattingFunctions.reduce( ( formattedHTML, fn ) => {
return fn( formattedHTML );
}, html );
},
moveAdvancedCostsHelpTip: function( html ) {
const htmlContent = $( html );
const advancedCostsHelpTip = htmlContent.find( '#wc-shipping-advanced-costs-help-text' );
advancedCostsHelpTip.addClass( 'wc-shipping-zone-method-fields-help-text' );
const input = htmlContent.find( '#woocommerce_flat_rate_cost' );
const fieldset = input.closest( 'fieldset' );
advancedCostsHelpTip.appendTo( fieldset );
return htmlContent.prop( 'outerHTML' );
},
addCurrencySymbol: function( html ) {
if ( ! window.wc.ShippingCurrencyContext || ! window.wc.ShippingCurrencyNumberFormat ) {
return html;
}
const htmlContent = $( html );
const priceInputs = htmlContent.find( '.wc-shipping-modal-price' );
const config = window.wc.ShippingCurrencyContext.getCurrencyConfig();
const { symbol, symbolPosition } = config;
priceInputs.addClass( `wc-shipping-currency-size-${ symbol.length }` );
priceInputs.addClass( `wc-shipping-currency-position-${ symbolPosition }` );
priceInputs.before( `<div class="wc-shipping-zone-method-currency wc-shipping-currency-position-${ symbolPosition }">${ symbol }</div>` );
priceInputs.each( ( i ) => {
const priceInput = $( priceInputs[ i ] );
const value = priceInput.attr( 'value' );
const formattedValue = window.wc.ShippingCurrencyNumberFormat( config, value );
priceInput.attr( 'value', formattedValue );
} );
return htmlContent.prop( 'outerHTML' );
},
moveHTMLHelpTips: function( html ) {
// These help tips aren't moved.
const helpTipsToRetain = [ 'woocommerce_flat_rate_cost', 'woocommerce_flat_rate_no_class_cost', 'woocommerce_flat_rate_class_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 ( helpTipsToRetain.some( ( tip ) => id.includes( tip ) ) ) {
const helpTip = htmlContent.find( `label[for=${ id }] span.woocommerce-help-tip` );
helpTip.addClass( 'wc-shipping-visible-help-text' );
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();
$('#btn-next').html('<img alt="processing" src="images/wpspin_light.gif" class="waiting" />');
// Add method to zone via ajax call
$.post( ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?' ) + 'action=woocommerce_shipping_zone_add_method', {
wc_shipping_zones_nonce : data.wc_shipping_zones_nonce,
@ -408,26 +539,152 @@
);
}
}
// Trigger save if there are changes, or just re-render
if ( _.size( shippingMethodView.model.changes ) ) {
shippingMethodView.model.set( 'methods', response.data.methods );
shippingMethodView.model.trigger( 'change:methods' );
shippingMethodView.model.trigger( 'rerender' );
} else {
shippingMethodView.model.set( 'methods', response.data.methods );
shippingMethodView.model.trigger( 'change:methods' );
shippingMethodView.model.trigger( 'saved:methods' );
}
// Avoid triggering a rerender here because we don't want to show the method in the table in case merchant doesn't finish flow.
shippingMethodView.model.set( 'methods', response.data.methods );
// Close original modal
closeModal();
}
var instance_id = response.data.instance_id,
method = response.data.methods[ instance_id ];
shippingMethodView.unblock();
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' );
}, '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() } );
}
event.data.view.possiblyAddShippingClassLink( event );
}
},
possiblyAddShippingClassLink: function( event ) {
const article = $( 'article.wc-modal-shipping-method-settings' );
const shippingClassesCount = article.data( 'shipping-classes-count' );
const status = article.data( 'status' );
const instance_id = article.data( 'id' );
const model = event.data.view.model;
const methods = _.indexBy( model.get( 'methods' ), 'instance_id' );
const method = methods[ instance_id ];
if ( method.id === 'flat_rate' && shippingClassesCount === 0 ) {
const link = article.find( '.wc-shipping-method-add-class-costs' );
link.css( 'display', 'block' );
}
},
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' ) {
shippingMethodView.block();
var view = shippingMethodView,
model = view.model,
methods = _.indexBy( model.get( 'methods' ), 'instance_id' ),
changes = {},
instance_id = post_data.instance_id;
// Remove method to zone via ajax call
$.post( {
url: ajaxurl + ( ajaxurl.indexOf( '?' ) > 0 ? '&' : '?') + 'action=woocommerce_shipping_zone_remove_method',
data: {
wc_shipping_zones_nonce: data.wc_shipping_zones_nonce,
instance_id: instance_id,
zone_id: data.zone_id,
},
success: function( { data } ) {
delete methods[instance_id];
changes.methods = changes.methods || data.methods;
model.set('methods', methods);
model.logChanges( changes );
view.clearUnloadConfirmation();
view.render();
shippingMethodView.unblock();
},
error: function( jqXHR, textStatus, errorThrown ) {
window.alert( data.strings.remove_method_failed );
shippingMethodView.unblock();
},
dataType: 'json'
});
}
}
},
onChangeShippingMethodSelector: function() {
var description = $( this ).find( 'option:selected' ).data( 'description' );
$( this ).parent().find( '.wc-shipping-zone-method-description' ).remove();
$( this ).after( '<div class="wc-shipping-zone-method-description">' + description + '</div>' );
$( this ).closest( 'article' ).height( $( this ).parent().height() );
},
onTogglePostcodes: function( event ) {
event.preventDefault();

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

@ -145,7 +145,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
wp_register_script( 'wc-backbone-modal', WC()->plugin_url() . '/assets/js/admin/backbone-modal' . $suffix . '.js', array( 'underscore', 'backbone', 'wp-util' ), $version );
wp_register_script( 'wc-shipping-zones', WC()->plugin_url() . '/assets/js/admin/wc-shipping-zones' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone', 'jquery-ui-sortable', 'wc-enhanced-select', 'wc-backbone-modal' ), $version );
wp_register_script( 'wc-shipping-zone-methods', WC()->plugin_url() . '/assets/js/admin/wc-shipping-zone-methods' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone', 'jquery-ui-sortable', 'wc-backbone-modal' ), $version );
wp_register_script( 'wc-shipping-classes', WC()->plugin_url() . '/assets/js/admin/wc-shipping-classes' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone' ), $version );
wp_register_script( 'wc-shipping-classes', WC()->plugin_url() . '/assets/js/admin/wc-shipping-classes' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone', 'wc-backbone-modal' ), $version, array( 'in_footer' => false ) );
wp_register_script( 'wc-clipboard', WC()->plugin_url() . '/assets/js/admin/wc-clipboard' . $suffix . '.js', array( 'jquery' ), $version );
wp_register_script( 'select2', WC()->plugin_url() . '/assets/js/select2/select2.full' . $suffix . '.js', array( 'jquery' ), '4.0.3' );
wp_register_script( 'selectWoo', WC()->plugin_url() . '/assets/js/selectWoo/selectWoo.full' . $suffix . '.js', array( 'jquery' ), '1.0.6' );

View File

@ -45,7 +45,7 @@ class WC_Meta_Box_Product_Categories {
* @param number $threshold The default threshold.
* @returns number The threshold that will be used.
*/
if ( $categories_count <= apply_filters( 'woocommerce_product_category_metabox_search_threshold', 100 ) && function_exists( 'post_categories_meta_box' ) ) {
if ( $categories_count <= apply_filters( 'woocommerce_product_category_metabox_search_threshold', 5 ) && function_exists( 'post_categories_meta_box' ) ) {
return post_categories_meta_box( $post, $box );
}

View File

@ -7,6 +7,7 @@
*/
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
defined( 'ABSPATH' ) || exit;
@ -47,8 +48,8 @@ class WC_Settings_Shipping extends WC_Settings_Page {
protected function get_own_sections() {
$sections = array(
'' => __( 'Shipping zones', 'woocommerce' ),
'options' => __( 'Shipping options', 'woocommerce' ),
'classes' => __( 'Shipping classes', 'woocommerce' ),
'options' => __( 'Shipping settings', 'woocommerce' ),
'classes' => __( 'Classes', 'woocommerce' ),
);
if ( ! $this->wc_is_installing() ) {
@ -108,7 +109,7 @@ class WC_Settings_Shipping extends WC_Settings_Page {
$settings =
array(
array(
'title' => __( 'Shipping options', 'woocommerce' ),
'title' => __( 'Shipping settings', 'woocommerce' ),
'type' => 'title',
'id' => 'shipping_options',
),
@ -247,6 +248,48 @@ class WC_Settings_Shipping extends WC_Settings_Page {
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Get all available regions.
*
* @param int $allowed_countries Zone ID.
* @param int $shipping_continents Zone ID.
*/
protected function get_region_options( $allowed_countries, $shipping_continents ) {
$options = array();
foreach ( $shipping_continents as $continent_code => $continent ) {
$continent_data = array(
'value' => 'continent:' . esc_attr( $continent_code ),
'label' => esc_html( $continent['name'] ),
'children' => array(),
);
$countries = array_intersect( array_keys( $allowed_countries ), $continent['countries'] );
foreach ( $countries as $country_code ) {
$country_data = array(
'value' => 'country:' . esc_attr( $country_code ),
'label' => esc_html( $allowed_countries[ $country_code ] ),
'children' => array(),
);
$states = WC()->countries->get_states( $country_code );
if ( $states ) {
foreach ( $states as $state_code => $state_name ) {
$country_data['children'][] = array(
'value' => 'state:' . esc_attr( $country_code . ':' . $state_code ),
'label' => esc_html( $state_name . ', ' . $allowed_countries[ $country_code ] ),
);
}
}
$continent_data['children'][] = $country_data;
}
$options[] = $continent_data;
}
return $options;
}
/**
* Show method for a zone
*
@ -278,26 +321,34 @@ class WC_Settings_Shipping extends WC_Settings_Page {
}
}
$localized_object = array(
'methods' => $zone->get_shipping_methods( false, 'json' ),
'zone_name' => $zone->get_zone_name(),
'zone_id' => $zone->get_id(),
'locations' => $locations,
'wc_shipping_zones_nonce' => wp_create_nonce( 'wc_shipping_zones_nonce' ),
'strings' => array(
'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
'save_changes_prompt' => __( 'Do you wish to save your changes first? Your changed data will be discarded if you choose to cancel.', 'woocommerce' ),
'save_failed' => __( 'Your changes were not saved. Please retry.', 'woocommerce' ),
'add_method_failed' => __( 'Shipping method could not be added. Please retry.', 'woocommerce' ),
'remove_method_failed' => __( 'Shipping method could not be removed. Please retry.', 'woocommerce' ),
'yes' => __( 'Yes', 'woocommerce' ),
'no' => __( 'No', 'woocommerce' ),
'default_zone_name' => __( 'Zone', 'woocommerce' ),
'delete_shipping_method_confirmation' => __( 'Are you sure you want to delete this shipping method?', 'woocommerce' ),
),
);
if ( 0 !== $zone->get_id() ) {
WCAdminAssets::register_script( 'wp-admin-scripts', 'shipping-settings-region-picker', true, array( 'wc-shipping-zone-methods' ) );
$localized_object['region_options'] = $this->get_region_options( $allowed_countries, $shipping_continents );
}
wp_localize_script(
'wc-shipping-zone-methods',
'shippingZoneMethodsLocalizeScript',
array(
'methods' => $zone->get_shipping_methods( false, 'json' ),
'zone_name' => $zone->get_zone_name(),
'zone_id' => $zone->get_id(),
'wc_shipping_zones_nonce' => wp_create_nonce( 'wc_shipping_zones_nonce' ),
'strings' => array(
'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
'save_changes_prompt' => __( 'Do you wish to save your changes first? Your changed data will be discarded if you choose to cancel.', 'woocommerce' ),
'save_failed' => __( 'Your changes were not saved. Please retry.', 'woocommerce' ),
'add_method_failed' => __( 'Shipping method could not be added. Please retry.', 'woocommerce' ),
'remove_method_failed' => __( 'Shipping method could not be removed. Please retry.', 'woocommerce' ),
'yes' => __( 'Yes', 'woocommerce' ),
'no' => __( 'No', 'woocommerce' ),
'default_zone_name' => __( 'Zone', 'woocommerce' ),
'delete_shipping_method_confirmation' => __( 'Are you sure you want to delete this shipping method?', 'woocommerce' ),
),
)
$localized_object,
);
wp_enqueue_script( 'wc-shipping-zone-methods' );

View File

@ -8,38 +8,119 @@
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<h2>
<?php esc_html_e( 'Shipping classes', 'woocommerce' ); ?>
<?php echo wc_help_tip( __( 'Shipping classes can be used to group products of similar type and can be used by some Shipping Methods (such as "Flat rate shipping") to provide different rates to different classes of product.', 'woocommerce' ) ); // @codingStandardsIgnoreLine ?>
<h2 class="wc-shipping-zones-heading">
<span><?php esc_html_e( 'Shipping classes', 'woocommerce' ); ?></span>
<a class="button button-primary wc-shipping-class-add-new" href="#"><?php esc_html_e( 'Add shipping class', 'woocommerce' ); ?></a>
</h2>
<p class="wc-shipping-zone-help-text">
<?php esc_html_e( 'Use shipping classes to customize the shipping rates for different groups of products, such as heavy items that require higher postage fees.', 'woocommerce' ); ?> <a target="_blank" href="https://woocommerce.com/document/product-shipping-classes/"><?php esc_html_e( 'Learn more', 'woocommerce' ); ?></a>
</p>
<table class="wc-shipping-classes widefat">
<thead>
<tr>
<?php foreach ( $shipping_class_columns as $class => $heading ) : ?>
<th class="<?php echo esc_attr( $class ); ?>"><?php echo esc_html( $heading ); ?></th>
<?php endforeach; ?>
<th />
</tr>
</thead>
<tfoot>
<tr>
<td colspan="<?php echo absint( count( $shipping_class_columns ) ); ?>">
<button type="submit" name="save" class="button button-primary wc-shipping-class-save" value="<?php esc_attr_e( 'Save shipping classes', 'woocommerce' ); ?>" disabled><?php esc_html_e( 'Save shipping classes', 'woocommerce' ); ?></button>
<a class="button button-secondary wc-shipping-class-add" href="#"><?php esc_html_e( 'Add shipping class', 'woocommerce' ); ?></a>
</td>
</tr>
</tfoot>
<tbody class="wc-shipping-class-rows"></tbody>
<tbody class="wc-shipping-class-rows wc-shipping-tables-tbody"></tbody>
</table>
<script type="text/html" id="tmpl-wc-shipping-class-row-blank">
<tr>
<td class="wc-shipping-classes-blank-state" colspan="<?php echo absint( count( $shipping_class_columns ) ); ?>"><p><?php esc_html_e( 'No shipping classes have been created.', 'woocommerce' ); ?></p></td>
<td class="wc-shipping-classes-blank-state" colspan="<?php echo absint( count( $shipping_class_columns ) + 1 ); ?>"><p><?php esc_html_e( 'No shipping classes have been created.', 'woocommerce' ); ?></p></td>
</tr>
</script>
<!-- 1. Placeholder becomes the "label" in view class div -->
<!-- 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 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>
<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">
<input type="hidden" name="term_id" value="{{{ data.term_id }}}" />
<?php
foreach ( $shipping_class_columns as $class => $heading ) {
echo '<div class="wc-shipping-class-modal-input ' . esc_attr( $class ) . '">';
switch ( $class ) {
case 'wc-shipping-class-name':
?>
<div class="view">
<?php echo esc_html( $heading ); ?> *
</div>
<div class="edit">
<input type="text" name="name" data-attribute="name" value="{{ data.name }}" placeholder="<?php esc_attr_e( 'e.g. Heavy', 'woocommerce' ); ?>" />
</div>
<div class="wc-shipping-class-modal-help-text"><?php esc_html_e( 'Give your shipping class a name for easy identification', 'woocommerce' ); ?></div>
<?php
break;
case 'wc-shipping-class-slug':
?>
<div class="view">
<?php echo esc_html( $heading ); ?>
</div>
<div class="edit">
<input type="text" name="slug" data-attribute="slug" value="{{ data.slug }}" placeholder="<?php esc_attr_e( 'e.g. heavy-packages', 'woocommerce' ); ?>" />
</div>
<div class="wc-shipping-class-modal-help-text"><?php esc_html_e( 'Slug (unique identifier) can be left blank and auto-generated, or you can enter one', 'woocommerce' ); ?></div>
<?php
break;
case 'wc-shipping-class-description':
?>
<div class="view">
<?php echo esc_html( $heading ); ?> *
</div>
<div class="edit">
<input type="text" name="description" data-attribute="description" value="{{ data.description }}" placeholder="<?php esc_attr_e( 'e.g. For heavy items requiring higher postage', 'woocommerce' ); ?>" />
</div>
<?php
break;
case 'wc-shipping-class-count':
break;
default:
?>
<div class="view wc-shipping-class-hide-sibling-view">
<?php echo esc_html( $heading ); ?>
</div>
<?php
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
do_action( 'woocommerce_shipping_classes_column_' . $class );
break;
}
echo '</div>';
}
?>
</form>
</article>
<footer>
<div class="inner">
<button id="btn-ok" disabled class="button button-primary button-large disabled">
<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>
</section>
</div>
</div>
<div class="wc-backbone-modal-backdrop modal-close"></div>
</script>
<script type="text/html" id="tmpl-wc-shipping-class-row">
<tr data-id="{{ data.term_id }}">
<?php
@ -50,28 +131,17 @@ if ( ! defined( 'ABSPATH' ) ) {
?>
<div class="view">
{{ data.name }}
<div class="row-actions">
<a class="wc-shipping-class-edit" href="#"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a> | <a href="#" class="wc-shipping-class-delete"><?php esc_html_e( 'Remove', 'woocommerce' ); ?></a>
</div>
</div>
<div class="edit">
<input type="text" name="name[{{ data.term_id }}]" data-attribute="name" value="{{ data.name }}" placeholder="<?php esc_attr_e( 'Shipping class name', 'woocommerce' ); ?>" />
<div class="row-actions">
<a class="wc-shipping-class-cancel-edit" href="#"><?php esc_html_e( 'Cancel changes', 'woocommerce' ); ?></a>
</div>
</div>
<?php
break;
case 'wc-shipping-class-slug':
?>
<div class="view">{{ data.slug }}</div>
<div class="edit"><input type="text" name="slug[{{ data.term_id }}]" data-attribute="slug" value="{{ data.slug }}" placeholder="<?php esc_attr_e( 'Slug', 'woocommerce' ); ?>" /></div>
<?php
break;
case 'wc-shipping-class-description':
?>
<div class="view">{{ data.description }}</div>
<div class="edit"><input type="text" name="description[{{ data.term_id }}]" data-attribute="description" value="{{ data.description }}" placeholder="<?php esc_attr_e( 'Description for your reference', 'woocommerce' ); ?>" /></div>
<?php
break;
case 'wc-shipping-class-count':
@ -80,11 +150,17 @@ if ( ! defined( 'ABSPATH' ) ) {
<?php
break;
default:
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
do_action( 'woocommerce_shipping_classes_column_' . $class );
break;
}
echo '</td>';
}
?>
<td class="wc-shipping-zone-actions">
<div>
<a class="wc-shipping-class-edit wc-shipping-zone-action-edit" href="#"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a> | <a href="#" class="wc-shipping-class-delete wc-shipping-zone-actions"><?php esc_html_e( 'Delete', 'woocommerce' ); ?></a>
</div>
</td>
</tr>
</script>

View File

@ -24,8 +24,10 @@ if ( ! defined( 'ABSPATH' ) ) {
<th scope="row" class="titledesc">
<label for="zone_name">
<?php esc_html_e( 'Zone name', 'woocommerce' ); ?>
<?php echo wc_help_tip( __( 'This is the name of the zone for your reference.', 'woocommerce' ) ); // @codingStandardsIgnoreLine ?>
</label>
<p class="wc-shipping-zone-help-text">
<?php esc_html_e( 'Give your zone a name! E.g. Local, or Worldwide.', 'woocommerce' ); ?>
</p>
</th>
<td class="forminp">
<input type="text" data-attribute="zone_name" name="zone_name" id="zone_name" value="<?php echo esc_attr( $zone->get_zone_name( 'edit' ) ); ?>" placeholder="<?php esc_attr_e( 'Zone name', 'woocommerce' ); ?>">
@ -35,31 +37,15 @@ if ( ! defined( 'ABSPATH' ) ) {
<th scope="row" class="titledesc">
<label for="zone_locations">
<?php esc_html_e( 'Zone regions', 'woocommerce' ); ?>
<?php echo wc_help_tip( __( 'These are regions inside this zone. Customers will be matched against these regions.', 'woocommerce' ) ); // @codingStandardsIgnoreLine ?>
</label>
<p class="wc-shipping-zone-help-text">
<?php esc_html_e( 'List the regions you\'d like to include in your shipping zone. Customers will be matched against these regions.', 'woocommerce' ); ?>
</p>
</th>
<td class="forminp">
<select multiple="multiple" data-attribute="zone_locations" id="zone_locations" name="zone_locations" data-placeholder="<?php esc_attr_e( 'Select regions within this zone', 'woocommerce' ); ?>" class="wc-shipping-zone-region-select chosen_select">
<?php
foreach ( $shipping_continents as $continent_code => $continent ) {
echo '<option value="continent:' . esc_attr( $continent_code ) . '"' . wc_selected( "continent:$continent_code", $locations ) . '>' . esc_html( $continent['name'] ) . '</option>';
$countries = array_intersect( array_keys( $allowed_countries ), $continent['countries'] );
foreach ( $countries as $country_code ) {
echo '<option value="country:' . esc_attr( $country_code ) . '"' . wc_selected( "country:$country_code", $locations ) . '>' . esc_html( '&nbsp;&nbsp; ' . $allowed_countries[ $country_code ] ) . '</option>';
$states = WC()->countries->get_states( $country_code );
if ( $states ) {
foreach ( $states as $state_code => $state_name ) {
echo '<option value="state:' . esc_attr( $country_code . ':' . $state_code ) . '"' . wc_selected( "state:$country_code:$state_code", $locations ) . '>' . esc_html( '&nbsp;&nbsp;&nbsp;&nbsp; ' . $state_name . ', ' . $allowed_countries[ $country_code ] ) . '</option>';
}
}
}
}
?>
</select>
<td>
<div>
<div id="wc-shipping-zone-region-picker-root"/>
</div>
<?php if ( empty( $postcodes ) ) : ?>
<a class="wc-shipping-zone-postcodes-toggle" href="#"><?php esc_html_e( 'Limit to specific ZIP/postcodes', 'woocommerce' ); ?></a>
<?php endif; ?>
@ -69,14 +55,16 @@ if ( ! defined( 'ABSPATH' ) ) {
<span class="description"><?php printf( __( 'Postcodes containing wildcards (e.g. CB23*) or fully numeric ranges (e.g. <code>90210...99000</code>) are also supported. Please see the shipping zones <a href="%s" target="_blank">documentation</a> for more information.', 'woocommerce' ), 'https://woo.com/document/setting-up-shipping-zones/#section-3' ); ?></span><?php // @codingStandardsIgnoreLine. ?>
</div>
</td>
<?php endif; ?>
</tr>
</tr>
<?php endif; ?>
<tr valign="top" class="">
<th scope="row" class="titledesc">
<label>
<?php esc_html_e( 'Shipping methods', 'woocommerce' ); ?>
<?php echo wc_help_tip( __( 'The following shipping methods apply to customers with shipping addresses within this zone.', 'woocommerce' ) ); // @codingStandardsIgnoreLine ?>
</label>
<p class="wc-shipping-zone-help-text">
<?php esc_html_e( 'Add the shipping methods you\'d like to make available to customers in this zone.', 'woocommerce' ); ?>
</p>
</th>
<td class="">
<table class="wc-shipping-zone-methods widefat">
@ -86,19 +74,19 @@ if ( ! defined( 'ABSPATH' ) ) {
<th class="wc-shipping-zone-method-title"><?php esc_html_e( 'Title', 'woocommerce' ); ?></th>
<th class="wc-shipping-zone-method-enabled"><?php esc_html_e( 'Enabled', 'woocommerce' ); ?></th>
<th class="wc-shipping-zone-method-description"><?php esc_html_e( 'Description', 'woocommerce' ); ?></th>
<th></th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="4">
<button type="submit" class="button wc-shipping-zone-add-method" value="<?php esc_attr_e( 'Add shipping method', 'woocommerce' ); ?>"><?php esc_html_e( 'Add shipping method', 'woocommerce' ); ?></button>
</td>
</tr>
</tfoot>
<tbody class="wc-shipping-zone-method-rows"></tbody>
<tbody class="wc-shipping-zone-method-rows wc-shipping-tables-tbody"></tbody>
</table>
</td>
</tr>
<tr>
<th scope="row"></th>
<td>
<button type="submit" class="button button-primary wc-shipping-zone-add-method" value="<?php esc_attr_e( 'Add shipping method', 'woocommerce' ); ?>"><?php esc_html_e( 'Add shipping method', 'woocommerce' ); ?></button>
</td>
</tr>
</tbody>
</table>
@ -120,16 +108,17 @@ if ( ! defined( 'ABSPATH' ) ) {
<tr data-id="{{ data.instance_id }}" data-enabled="{{ data.enabled }}">
<td width="1%" class="wc-shipping-zone-method-sort"></td>
<td class="wc-shipping-zone-method-title">
<a class="wc-shipping-zone-method-settings" href="admin.php?page=wc-settings&amp;tab=shipping&amp;instance_id={{ data.instance_id }}">{{{ data.title }}}</a>
<div class="row-actions">
<a class="wc-shipping-zone-method-settings" href="admin.php?page=wc-settings&amp;tab=shipping&amp;instance_id={{ data.instance_id }}"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a> | <a href="#" class="wc-shipping-zone-method-delete"><?php esc_html_e( 'Delete', 'woocommerce' ); ?></a>
</div>
{{{ data.title }}}
</td>
<td width="1%" class="wc-shipping-zone-method-enabled"><a href="#">{{{ data.enabled_icon }}}</a></td>
<td class="wc-shipping-zone-method-description">
<strong class="wc-shipping-zone-method-type">{{ data.method_title }}</strong>
{{{ data.method_description }}}
</td>
<td class="wc-shipping-zone-actions">
<div>
<a class="wc-shipping-zone-method-settings wc-shipping-zone-action-edit" href="admin.php?page=wc-settings&amp;tab=shipping&amp;instance_id={{ data.instance_id }}"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a> | <a href="#" class="wc-shipping-zone-method-delete wc-shipping-zone-actions"><?php esc_html_e( 'Delete', 'woocommerce' ); ?></a>
</div>
</td>
</tr>
</script>
@ -142,8 +131,8 @@ if ( ! defined( 'ABSPATH' ) ) {
<?php
printf(
/* translators: %s: shipping method title */
esc_html__( '%s Settings', 'woocommerce' ),
'{{{ data.method.method_title }}}'
esc_html__( 'Set up %s', 'woocommerce' ),
'{{{ data.method.method_title.toLowerCase() }}}'
);
?>
</h1>
@ -151,15 +140,20 @@ if ( ! defined( 'ABSPATH' ) ) {
<span class="screen-reader-text"><?php esc_html_e( 'Close modal panel', 'woocommerce' ); ?></span>
</button>
</header>
<article class="wc-modal-shipping-method-settings">
<article class="wc-modal-shipping-method-settings" data-id="{{{ data.instance_id }}}" data-status="{{{ data.status }}}" data-shipping-classes-count="<?php echo count( WC()->shipping()->get_shipping_classes() ); ?>">
<form action="" method="post">
{{{ data.method.settings_html }}}
<input type="hidden" name="instance_id" value="{{{ data.instance_id }}}" />
</form>
<a class="wc-shipping-method-add-class-costs" style="display:none;" target="_blank" href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=classes' ) ); ?>"><?php esc_html_e( 'Add shipping class costs', 'woocommerce' ); ?></a>
</article>
<footer>
<div class="inner">
<button id="btn-ok" class="button button-primary button-large"><?php esc_html_e( 'Save changes', 'woocommerce' ); ?></button>
<button id="btn-ok" data-status='{{ data.status }}' disabled='{{ data.status === "new" ? "disabled" : "" }}' class="button button-primary button-large {{ data.status === 'new' ? 'disabled' : '' }}">
<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>
@ -169,36 +163,52 @@ if ( ! defined( 'ABSPATH' ) ) {
</script>
<script type="text/template" id="tmpl-wc-modal-add-shipping-method">
<div class="wc-backbone-modal">
<div class="wc-backbone-modal wc-backbone-modal-add-shipping-method">
<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( $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() ) . '<span class="dashicons dashicons-yes"></span></label><div class="wc-shipping-zone-method-input-help-text"><span>' . esc_html( $description ) . '</span></div></div>';
}
?>
</fieldset>
</form>
</article>
<footer>
<div class="inner">
<button id="btn-ok" class="button button-primary button-large"><?php esc_html_e( 'Add 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

@ -5,10 +5,10 @@ if ( ! defined( 'ABSPATH' ) ) {
?>
<h2 class="wc-shipping-zones-heading">
<?php _e( 'Shipping zones', 'woocommerce' ); ?>
<a href="<?php echo admin_url( 'admin.php?page=wc-settings&tab=shipping&zone_id=new' ); ?>" class="page-title-action"><?php esc_html_e( 'Add shipping zone', 'woocommerce' ); ?></a>
<span><?php esc_html_e( 'Shipping zones', 'woocommerce' ); ?></span>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&zone_id=new' ) ); ?>" class="button-primary"><?php esc_html_e( 'Add zone', 'woocommerce' ); ?></a>
</h2>
<p><?php echo __( 'A shipping zone is a geographic region where a certain set of shipping methods are offered.', 'woocommerce' ) . ' ' . __( 'WooCommerce will match a customer to a single zone using their shipping address and present the shipping methods within that zone to them.', 'woocommerce' ); ?></p>
<p class="wc-shipping-zone-heading-help-text"><?php echo esc_html_e( 'A shipping zone consists of the region(s) you\'d like to ship to and the shipping method(s) offered. A shopper can only be matched to one zone, and we\'ll use their shipping address to show them the methods available in their area.', 'woocommerce' ); ?></p>
<table class="wc-shipping-zones widefat">
<thead>
<tr>
@ -16,39 +16,40 @@ if ( ! defined( 'ABSPATH' ) ) {
<th class="wc-shipping-zone-name"><?php esc_html_e( 'Zone name', 'woocommerce' ); ?></th>
<th class="wc-shipping-zone-region"><?php esc_html_e( 'Region(s)', 'woocommerce' ); ?></th>
<th class="wc-shipping-zone-methods"><?php esc_html_e( 'Shipping method(s)', 'woocommerce' ); ?></th>
<th></th>
</tr>
</thead>
<tbody class="wc-shipping-zone-rows"></tbody>
<tbody>
<tr data-id="0" class="wc-shipping-zone-worldwide">
<td width="1%" class="wc-shipping-zone-worldwide"></td>
<td class="wc-shipping-zone-name">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&zone_id=0' ) ); ?>"><?php esc_html_e( 'Locations not covered by your other zones', 'woocommerce' ); ?></a>
<div class="row-actions">
<a href="admin.php?page=wc-settings&amp;tab=shipping&amp;zone_id=0"><?php _e( 'Manage shipping methods', 'woocommerce' ); ?></a>
</div>
</td>
<td class="wc-shipping-zone-region"><?php _e( 'This zone is <b>optionally</b> used for regions that are not included in any other shipping zone.', 'woocommerce' ); ?></td>
<td class="wc-shipping-zone-methods">
<ul>
<?php
$worldwide = new WC_Shipping_Zone( 0 );
$methods = $worldwide->get_shipping_methods();
uasort( $methods, 'wc_shipping_zone_method_order_uasort_comparison' );
<tbody class="wc-shipping-zone-rows wc-shipping-tables-tbody"></tbody>
if ( ! empty( $methods ) ) {
foreach ( $methods as $method ) {
$class_name = 'yes' === $method->enabled ? 'method_enabled' : 'method_disabled';
echo '<li class="wc-shipping-zone-method ' . esc_attr( $class_name ) . '">' . esc_html( $method->get_title() ) . '</li>';
}
} else {
echo '<li class="wc-shipping-zone-method">' . __( 'No shipping methods offered to this zone.', 'woocommerce' ) . '</li>';
<tfoot data-id="0" class="wc-shipping-zone-worldwide">
<td width="1%" class="wc-shipping-zone-worldwide"></td>
<td class="wc-shipping-zone-name">
<?php esc_html_e( 'Rest of the world', 'woocommerce' ); ?>
</td>
<td class="wc-shipping-zone-region"><?php esc_html_e( 'An optional zone you can use to set the shipping method(s) available to any regions that have not been listed above.', 'woocommerce' ); ?></td>
<td class="wc-shipping-zone-methods">
<ul>
<?php
$worldwide = new WC_Shipping_Zone( 0 );
$methods = $worldwide->get_shipping_methods();
uasort( $methods, 'wc_shipping_zone_method_order_uasort_comparison' );
if ( ! empty( $methods ) ) {
foreach ( $methods as $method ) {
$class_name = 'yes' === $method->enabled ? 'method_enabled' : 'method_disabled';
echo '<li class="wc-shipping-zone-method ' . esc_attr( $class_name ) . '">' . esc_html( $method->get_title() ) . '</li>';
}
?>
</ul>
</td>
</tr>
</tbody>
} else {
echo '<li>' . esc_html_e( 'No shipping methods offered to this zone.', 'woocommerce' ) . '</li>';
}
?>
</ul>
</td>
<td class="wc-shipping-zone-actions">
<a class="wc-shipping-zone-action-edit" href="admin.php?page=wc-settings&amp;tab=shipping&amp;zone_id=0"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a>
</td>
</tfoot>
</table>
<script type="text/html" id="tmpl-wc-shipping-zone-row-blank">
@ -73,10 +74,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<tr data-id="{{ data.zone_id }}">
<td width="1%" class="wc-shipping-zone-sort"></td>
<td class="wc-shipping-zone-name">
<a href="admin.php?page=wc-settings&amp;tab=shipping&amp;zone_id={{ data.zone_id }}">{{ data.zone_name }}</a>
<div class="row-actions">
<a href="admin.php?page=wc-settings&amp;tab=shipping&amp;zone_id={{ data.zone_id }}"><?php _e( 'Edit', 'woocommerce' ); ?></a> | <a href="#" class="wc-shipping-zone-delete"><?php _e( 'Delete', 'woocommerce' ); ?></a>
</div>
{{ data.zone_name }}
</td>
<td class="wc-shipping-zone-region">
{{ data.formatted_zone_location }}
@ -84,6 +82,11 @@ if ( ! defined( 'ABSPATH' ) ) {
<td class="wc-shipping-zone-methods">
<div><ul></ul></div>
</td>
<td class="wc-shipping-zone-actions">
<div>
<a class="wc-shipping-zone-action-edit" href="admin.php?page=wc-settings&amp;tab=shipping&amp;zone_id={{ data.zone_id }}"><?php esc_html_e( 'Edit', 'woocommerce' ); ?></a> | <a href="#" class="wc-shipping-zone-delete wc-shipping-zone-actions"><?php esc_html_e( 'Delete', 'woocommerce' ); ?></a>
</div>
</td>
</tr>
</script>

View File

@ -281,7 +281,7 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
public function sanitize_cost( $value ) {
$value = is_null( $value ) ? '' : $value;
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ) ), '', $value );
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $value );
// Thrown an error on the front end if the evaluate_cost will fail.
$dummy_cost = $this->evaluate_cost(
$value,

View File

@ -8,13 +8,15 @@
defined( 'ABSPATH' ) || exit;
$cost_desc = __( 'Enter a cost (excl. tax) or sum, e.g. <code>10.00 * [qty]</code>.', 'woocommerce' ) . '<br/><br/>' . __( 'Use <code>[qty]</code> for the number of items, <br/><code>[cost]</code> for the total cost of items, and <code>[fee percent="10" min_fee="20" max_fee=""]</code> for percentage based fees.', 'woocommerce' );
$cost_link = sprintf( '<span id="wc-shipping-advanced-costs-help-text">%s <a target="_blank" href="https://woo.com/document/flat-rate-shipping/#advanced-costs">%s</a>.</span>', __( 'Charge a flat rate per item, or enter a cost formula to charge a percentage based cost or a minimum fee. Learn more about', 'woocommerce' ), __( 'advanced costs', 'woocommerce' ) );
$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' ),
'description' => __( 'Your customers will see the name of this shipping method during checkout.', 'woocommerce' ),
'default' => '',
'placeholder' => __( 'e.g. Standard national', 'woocommerce' ),
'desc_tip' => true,
),
'tax_status' => array(
@ -30,6 +32,7 @@ $settings = array(
'cost' => array(
'title' => __( 'Cost', 'woocommerce' ),
'type' => 'text',
'class' => 'wc-shipping-modal-price',
'placeholder' => '',
'description' => $cost_desc,
'default' => '0',
@ -45,8 +48,8 @@ if ( ! empty( $shipping_classes ) ) {
'title' => __( 'Shipping class costs', 'woocommerce' ),
'type' => 'title',
'default' => '',
/* translators: %s: URL for link. */
'description' => sprintf( __( 'These costs can optionally be added based on the <a href="%s">product shipping class</a>.', 'woocommerce' ), admin_url( 'admin.php?page=wc-settings&tab=shipping&section=classes' ) ),
/* translators: %s: URL for link */
'description' => sprintf( __( 'These costs can optionally be added based on the <a target="_blank" href="%s">product shipping class</a>. Learn more about <a target="_blank" href="https://woo.com/document/flat-rate-shipping/#shipping-classes">setting shipping class costs</a>.', 'woocommerce' ), admin_url( 'admin.php?page=wc-settings&tab=shipping&section=classes' ) ),
);
foreach ( $shipping_classes as $shipping_class ) {
if ( ! isset( $shipping_class->term_id ) ) {
@ -56,6 +59,7 @@ if ( ! empty( $shipping_classes ) ) {
/* translators: %s: shipping class name */
'title' => sprintf( __( '"%s" shipping class cost', 'woocommerce' ), esc_html( $shipping_class->name ) ),
'type' => 'text',
'class' => 'wc-shipping-modal-price',
'placeholder' => __( 'N/A', 'woocommerce' ),
'description' => $cost_desc,
'default' => $this->get_option( 'class_cost_' . $shipping_class->slug ), // Before 2.5.0, we used slug here which caused issues with long setting names.
@ -67,6 +71,7 @@ if ( ! empty( $shipping_classes ) ) {
$settings['no_class_cost'] = array(
'title' => __( 'No shipping class cost', 'woocommerce' ),
'type' => 'text',
'class' => 'wc-shipping-modal-price',
'placeholder' => __( 'N/A', 'woocommerce' ),
'description' => $cost_desc,
'default' => '',
@ -75,14 +80,15 @@ if ( ! empty( $shipping_classes ) ) {
);
$settings['type'] = array(
'title' => __( 'Calculation type', 'woocommerce' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'default' => 'class',
'options' => array(
'title' => __( 'Calculation type', 'woocommerce' ),
'type' => 'select',
'class' => 'wc-enhanced-select',
'default' => 'class',
'options' => array(
'class' => __( 'Per class: Charge shipping for each shipping class individually', 'woocommerce' ),
'order' => __( 'Per order: Charge shipping for the most expensive shipping class', 'woocommerce' ),
),
'description' => $cost_link,
);
}

View File

@ -83,38 +83,61 @@ class WC_Shipping_Free_Shipping extends WC_Shipping_Method {
add_action( 'admin_footer', array( 'WC_Shipping_Free_Shipping', 'enqueue_admin_js' ), 10 ); // Priority needs to be higher than wc_print_js (25).
}
/**
* Sanitize the cost field.
*
* @since 8.3.0
* @param string $value Unsanitized value.
* @throws Exception Last error triggered.
* @return string
*/
public function sanitize_cost( $value ) {
$value = is_null( $value ) ? '' : $value;
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $value );
if ( ! is_numeric( $value ) ) {
throw new Exception( __( 'Please enter a valid number', 'woocommerce' ) );
}
return $value;
}
/**
* Init form fields.
*/
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,
'description' => __( 'Your customers will see the name of this shipping method during checkout.', 'woocommerce' ),
'default' => '',
'placeholder' => __( 'e.g. Free shipping', 'woocommerce' ),
'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' ),
'default' => '0',
'desc_tip' => true,
'title' => __( 'Minimum order amount', 'woocommerce' ),
'type' => 'text',
'class' => 'wc-shipping-modal-price',
'placeholder' => wc_format_localized_price( 0 ),
'description' => __( 'Customers will need to spend this amount to get free shipping.', 'woocommerce' ),
'default' => '0',
'desc_tip' => true,
'sanitize_callback' => array( $this, 'sanitize_cost' ),
),
'ignore_discounts' => array(
'title' => __( 'Coupons discounts', 'woocommerce' ),

View File

@ -79,16 +79,37 @@ class WC_Shipping_Local_Pickup extends WC_Shipping_Method {
);
}
/**
* Sanitize the cost field.
*
* @since 8.3.0
* @param string $value Unsanitized value.
* @throws Exception Last error triggered.
* @return string
*/
public function sanitize_cost( $value ) {
$value = is_null( $value ) ? '' : $value;
$value = wp_kses_post( trim( wp_unslash( $value ) ) );
$value = str_replace( array( get_woocommerce_currency_symbol(), html_entity_decode( get_woocommerce_currency_symbol() ), wc_get_price_thousand_separator() ), '', $value );
if ( $value && ! is_numeric( $value ) ) {
throw new Exception( __( 'Please enter a valid number', 'woocommerce' ) );
}
return $value;
}
/**
* Init form fields.
*/
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' ),
'description' => __( 'Your customers will see the name of this shipping method during checkout.', 'woocommerce' ),
'default' => '',
'placeholder' => __( 'e.g. Local pickup', 'woocommerce' ),
'desc_tip' => true,
),
'tax_status' => array(
@ -102,12 +123,14 @@ class WC_Shipping_Local_Pickup extends WC_Shipping_Method {
),
),
'cost' => array(
'title' => __( 'Cost', 'woocommerce' ),
'type' => 'text',
'placeholder' => '0',
'description' => __( 'Optional cost for local pickup.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
'title' => __( 'Cost', 'woocommerce' ),
'type' => 'text',
'class' => 'wc-shipping-modal-price',
'placeholder' => wc_format_localized_price( 0 ),
'description' => __( 'Optional cost for local pickup.', 'woocommerce' ),
'default' => '',
'desc_tip' => true,
'sanitize_callback' => array( $this, 'sanitize_cost' ),
),
);
}

View File

@ -133,7 +133,19 @@ class Homescreen {
$zone = new \WC_Shipping_Zone();
$zone->set_zone_name( $country_name );
$zone->add_location( $country_code, 'country' );
$zone->add_shipping_method( 'free_shipping' );
// Method creation has no default title, use the REST API to add a title.
$instance_id = $zone->add_shipping_method( 'free_shipping' );
$request = new \WP_REST_Request( 'POST', '/wc/v2/shipping/zones/' . $zone->get_id() . '/methods/' . $instance_id );
$request->set_body_params(
array(
'settings' => array(
'title' => 'Free shipping',
),
)
);
rest_do_request( $request );
update_option( 'woocommerce_admin_created_default_shipping_zones', 'yes' );
Shipping::delete_zone_count_transient();
}

View File

@ -442,15 +442,16 @@ class WCAdminAssets {
* @param string $script_path_name The script path name.
* @param string $script_name Filename of the script to load.
* @param bool $need_translation Whether the script need translations.
* @param array $dependencies Array of any extra dependencies. Note wc-admin and any application JS dependencies are automatically added by Dependency Extraction Webpack Plugin. Use this parameter to designate any extra dependencies.
*/
public static function register_script( $script_path_name, $script_name, $need_translation = false ) {
public static function register_script( $script_path_name, $script_name, $need_translation = false, $dependencies = array() ) {
$script_assets_filename = self::get_script_asset_filename( $script_path_name, $script_name );
$script_assets = require WC_ADMIN_ABSPATH . WC_ADMIN_DIST_JS_FOLDER . $script_path_name . '/' . $script_assets_filename;
wp_enqueue_script(
'wc-admin-' . $script_name,
self::get_url( $script_path_name . '/' . $script_name, 'js' ),
array_merge( array( WC_ADMIN_APP ), $script_assets ['dependencies'] ),
array_merge( array( WC_ADMIN_APP ), $script_assets ['dependencies'], $dependencies ),
self::get_file_version( 'js' ),
true
);

View File

@ -12,10 +12,6 @@ test.describe( 'Merchant can add shipping classes', () => {
await page
.locator( '.wc-shipping-class-delete >> nth=0' )
.dispatchEvent( 'click' );
await page
.locator( '.wc-shipping-class-delete >> nth=0' )
.dispatchEvent( 'click' );
await page.locator( 'text=Save shipping classes' ).click();
} );
test( 'can add shipping classes', async ( { page } ) => {
@ -31,24 +27,27 @@ test.describe( 'Merchant can add shipping classes', () => {
const shippingClassNoSlug = {
name: 'Poster Pack',
slug: '',
description: '',
description: 'Posters, stickers, and other flat items.',
};
const shippingClasses = [ shippingClassSlug, shippingClassNoSlug ];
// Add shipping classes
for ( const { name, slug, description } of shippingClasses ) {
await page.locator( 'text=Add shipping class' ).click();
await page.getByRole('link', { name: 'Add shipping class' }).click();
await page
.locator( '.editing:last-child [data-attribute="name"]' )
.getByPlaceholder( 'e.g. Heavy', { exact: true } )
.fill( name );
await page
.locator( '.editing:last-child [data-attribute="slug"]' )
.getByPlaceholder( 'e.g. heavy-packages', { exact: true } )
.fill( slug );
await page
.locator( '.editing:last-child [data-attribute="description"]' )
.getByPlaceholder( 'e.g. For heavy items requiring higher postage', { exact: true } )
.fill( description );
await page.getByRole( 'button', { name: 'Create' } ).click();
await page.waitForLoadState( 'networkidle' );
}
await page.locator( 'text=Save shipping classes' ).click();
// Set the expected auto-generated slug
shippingClassNoSlug.slug = 'poster-pack';
@ -56,15 +55,12 @@ test.describe( 'Merchant can add shipping classes', () => {
// Verify that the specified shipping classes were saved
for ( const { name, slug, description } of shippingClasses ) {
await expect(
page.locator( `text=${ name } Edit | Remove` )
page.getByText( name, { exact: true } )
).toBeVisible();
await expect( page.locator( `text=${ slug }` ) ).toBeVisible();
// account for blank description
if ( description !== '' ) {
await expect(
page.locator( `text=${ description }` )
).toBeVisible();
}
await expect(
page.getByText( description, { exact: true } )
).toBeVisible();
}
} );
} );

View File

@ -67,32 +67,37 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
.getByPlaceholder( 'Zone name' )
.fill( shippingZoneNameLocalPickup );
await page.getByPlaceholder( 'Select regions within this zone' ).click();
await page
.getByPlaceholder( 'Select regions within this zone' )
.type( 'British Columbia, Canada' );
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
input.click();
input.type( 'British Columbia, Canada' );
await page
.getByText( 'British Columbia, Canada' ).last()
.click();
// Close dropdown
await page.keyboard.press('Escape');
await page.getByRole( 'link', { name: 'Limit to specific ZIP/postcodes' } ).click();
await page.getByPlaceholder( 'List 1 postcode per line' ).fill( maynePostal );
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
await page
.getByRole( 'combobox' )
.selectOption( { label: 'Local pickup' } );
await page.getByRole('button', { name: 'Add 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
.getByPlaceholder( 'e.g. Local pickup' )
.fill( 'Local pickup' );
await page.locator( '#btn-ok' ).click();
await page.waitForLoadState( 'networkidle' );
await expect(
page
.locator( '.wc-shipping-zone-method-title' )
.filter( { hasText: 'Local pickup' } )
).toBeVisible();
await page.getByRole( 'button', { name: 'Save changes'} ).click();
await page.goto(
'wp-admin/admin.php?page=wc-settings&tab=shipping'
);
@ -127,29 +132,35 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
);
await page.getByPlaceholder( 'Zone name' ).fill( shippingZoneNameFreeShip );
await page.getByPlaceholder( 'Select regions within this zone' ).click();
await page
.getByPlaceholder( 'Select regions within this zone' )
.type( 'British Columbia, Canada' );
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
input.click();
input.type( 'British Columbia, Canada' );
await page
.getByText( 'British Columbia, Canada' ).last()
.click();
// Close dropdown
await page.keyboard.press('Escape');
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
await page
.getByRole( 'combobox' )
.selectOption( { label: 'Free shipping' } );
await page.getByRole('button', { name: 'Add 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
.getByPlaceholder( 'e.g. Free shipping' )
.fill( 'Free shipping' );
await page.locator( '#btn-ok' ).click();
await page.waitForLoadState( 'networkidle' );
await expect(
page
.locator( '.wc-shipping-zone-method-title' )
.filter( { hasText: 'Free shipping' } )
).toBeVisible();
await page.getByRole( 'button', { name: 'Save changes'} ).click();
await page.goto(
'wp-admin/admin.php?page=wc-settings&tab=shipping'
);
@ -181,28 +192,37 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
);
await page.getByPlaceholder( 'Zone name' ).fill( shippingZoneNameFlatRate );
await page.getByPlaceholder( 'Select regions within this zone' ).click();
await page.getByPlaceholder( 'Select regions within this zone' ).type( 'Canada' );
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
input.click();
input.type( 'Canada' );
await page
.getByText('Canada').last()
.click();
// Close dropdown
await page.keyboard.press('Escape');
await page.getByRole( 'button', { name: 'Add shipping method' } ).click();
await page.getByText( 'Flat rate', { exact: true } ).click();
await page.getByRole('button', { name: 'Continue' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page
.getByRole( 'combobox' )
.selectOption( { label: 'Flat rate' } );
await page.getByRole('button', { name: 'Add shipping method' } ).last().click();
.getByPlaceholder( 'e.g. Standard national' )
.fill( 'Flat rate' );
await page.locator( '#btn-ok' ).click();
await page.waitForLoadState( 'networkidle' );
await expect(
page
.locator( '.wc-shipping-zone-method-title' )
.filter( { hasText: 'Flat rate' } )
).toBeVisible();
await page.getByRole( 'link', { name: 'Flat rate' } ).click();
await page.locator( 'td:has-text("Flat rate") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit' ).click();
await page.getByLabel( 'Cost', { exact: true } ).fill( '10' );
await page.getByRole( 'button', { name: 'Save changes' } ).last().click();
await page.getByRole( 'button', { name: 'Save' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page.goto(
@ -237,15 +257,16 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
);
await page.locator( '#zone_name' ).fill( shippingZoneNameUSRegion );
await page.locator( '.select2-search__field' ).click();
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
input.click();
input.type( 'United States' );
await page
.locator( '.select2-search__field' )
.type( 'United States' );
await page
.locator(
'.select2-results__option.select2-results__option--highlighted'
)
.getByText( 'United States' ).last()
.click();
// Close dropdown
await page.keyboard.press('Escape');
await page.locator( '#submit' ).click();
await page.waitForFunction( () => {
@ -265,10 +286,10 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
//delete created shipping zone region after confirmation it exists
await page.goto( 'wp-admin/admin.php?page=wc-settings&tab=shipping' );
await page.locator( 'a:has-text("USA Zone") >> nth=0' ).click();
await page.locator( 'td:has-text("USA Zone") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit' ).click();
//delete
await page.locator( 'text=×' ).click();
await page.getByRole( 'button', { name: 'Remove' } ).click();
//save changes
await page.locator( '#submit' ).click();
await page.waitForFunction( () => {
@ -298,33 +319,41 @@ test.describe( 'WooCommerce Shipping Settings - Add new shipping zone', () => {
);
await page.locator( '#zone_name' ).fill( shippingZoneNameFlatRate );
await page.locator( '.select2-search__field' ).click();
await page.locator( '.select2-search__field' ).type( 'Canada' );
const input = await page.getByPlaceholder( 'Start typing to filter zones' );
input.click();
input.type( 'Canada' );
await page
.locator(
'.select2-results__option.select2-results__option--highlighted'
)
.getByText( 'Canada' ).last()
.click();
// Close dropdown
await page.keyboard.press('Escape');
await page.locator( 'text=Add shipping method' ).click();
await page.getByText( 'Flat rate', { exact: true } ).click();
await page.getByRole('button', { name: 'Continue' } ).last().click();
await page.waitForLoadState( 'networkidle' );
await page
.locator( 'select[name=add_method_id]' )
.selectOption( 'flat_rate' );
.getByPlaceholder( 'e.g. Standard national' )
.fill( 'Flat rate' );
await page.locator( '#btn-ok' ).click();
await page.waitForLoadState( 'networkidle' );
await expect(
page
.locator( '.wc-shipping-zone-method-title' )
.filter( { hasText: 'Flat rate' } )
).toBeVisible();
await page.locator( 'a.wc-shipping-zone-method-settings' ).click();
await page.locator( 'td:has-text("Flat rate") ~ td.wc-shipping-zone-actions a.wc-shipping-zone-action-edit' ).click();
await page.locator( '#woocommerce_flat_rate_cost' ).fill( '10' );
await page.locator( '#btn-ok' ).click();
await page.waitForLoadState( 'networkidle' );
await page.locator( '.wc-shipping-zone-method-settings' ).hover();
await page.locator( 'text=Delete' ).waitFor();
page.on( 'dialog', ( dialog ) => dialog.accept() );
@ -406,13 +435,20 @@ test.describe( 'Verifies shipping options from customer perspective', () => {
method_id: 'flat_rate',
settings: {
cost: '10.00',
title: 'Flat rate',
},
} );
await api.post( `shipping/zones/${ shippingFreeId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
}
} );
await api.post( `shipping/zones/${ shippingLocalId }/methods`, {
method_id: 'local_pickup',
settings: {
title: 'Local pickup',
}
} );
} );

View File

@ -86,15 +86,22 @@ test.describe( 'Cart Block Calculate Shipping', () => {
// set shipping zone methods
await api.post( `shipping/zones/${ shippingZoneNLId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
}
} );
await api.post( `shipping/zones/${ shippingZonePTId }/methods`, {
method_id: 'flat_rate',
settings: {
cost: '5.00',
title: 'Flat rate',
},
} );
await api.post( `shipping/zones/${ shippingZonePTId }/methods`, {
method_id: 'local_pickup',
settings: {
title: 'Local pickup',
}
} );
// confirm that we allow shipping to any country

View File

@ -679,6 +679,9 @@ test.describe( 'Shipping Cart Block Tax', () => {
await api
.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'flat_rate',
settings: {
title: 'Flat rate',
},
} )
.then( ( response ) => {
shippingMethodId = response.data.id;

View File

@ -78,15 +78,22 @@ test.describe( 'Cart Calculate Shipping', () => {
// set shipping zone methods
await api.post( `shipping/zones/${ shippingZoneDEId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
},
} );
await api.post( `shipping/zones/${ shippingZoneFRId }/methods`, {
method_id: 'flat_rate',
settings: {
title: 'Flat rate',
cost: '5.00',
},
} );
await api.post( `shipping/zones/${ shippingZoneFRId }/methods`, {
method_id: 'local_pickup',
settings: {
title: 'Local pickup',
},
} );
// confirm that we allow shipping to any country
await api.put( 'settings/general/woocommerce_allowed_countries', {

View File

@ -66,6 +66,9 @@ test.describe( 'Shopper Checkout Create Account', () => {
] );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
}
} );
await api.put( 'payment_gateways/cod', {
enabled: true,

View File

@ -62,6 +62,9 @@ test.describe( 'Shopper Checkout Login Account', () => {
] );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
}
} );
// create customer and save its id
await api

View File

@ -67,6 +67,9 @@ test.describe( 'Checkout page', () => {
] );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
}
} );
} );

View File

@ -612,6 +612,9 @@ test.describe.serial( 'Shipping Tax', () => {
} );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'flat_rate',
settings: {
title: 'Flat rate',
},
} )
.then( ( response ) => {
shippingMethodId = response.data.id;

View File

@ -680,7 +680,7 @@ class WC_Tests_API_Shipping_Zones_V2 extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$this->assertArrayHasKey( 'title', $data['settings'] );
$this->assertEquals( 'Flat rate', $data['settings']['title']['value'] );
$this->assertEquals( '', $data['settings']['title']['value'] );
$this->assertArrayHasKey( 'tax_status', $data['settings'] );
$this->assertEquals( 'taxable', $data['settings']['tax_status']['value'] );
$this->assertArrayHasKey( 'cost', $data['settings'] );
@ -691,7 +691,8 @@ class WC_Tests_API_Shipping_Zones_V2 extends WC_REST_Unit_Test_Case {
$request->set_body_params(
array(
'settings' => array(
'cost' => 5,
'cost' => 5,
'title' => 'Flat rate',
),
)
);

View File

@ -703,7 +703,7 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$this->assertArrayHasKey( 'title', $data['settings'] );
$this->assertEquals( 'Flat rate', $data['settings']['title']['value'] );
$this->assertEquals( '', $data['settings']['title']['value'] );
$this->assertArrayHasKey( 'tax_status', $data['settings'] );
$this->assertEquals( 'taxable', $data['settings']['tax_status']['value'] );
$this->assertArrayHasKey( 'cost', $data['settings'] );
@ -714,7 +714,8 @@ class WC_Tests_API_Shipping_Zones extends WC_REST_Unit_Test_Case {
$request->set_body_params(
array(
'settings' => array(
'cost' => 5,
'cost' => 5,
'title' => 'Flat rate',
),
)
);

View File

@ -73,8 +73,8 @@ class WC_Settings_Shipping_Test extends WC_Settings_Unit_Test_Case {
$expected = array(
'' => 'Shipping zones',
'options' => 'Shipping options',
'classes' => 'Shipping classes',
'options' => 'Shipping settings',
'classes' => 'Classes',
'method_1_id' => 'Method_1_id',
'method_2_id' => 'method_2_title',
);