Skip to content

Commit

Permalink
Merge pull request #2594 from woocommerce/add/min-max-time-inputs
Browse files Browse the repository at this point in the history
Update UI to support minimum and maximum shipping times.
  • Loading branch information
jorgemd24 authored Sep 16, 2024
2 parents 8ffaa67 + 6a31818 commit cd67ca0
Show file tree
Hide file tree
Showing 19 changed files with 516 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ exports[`checkErrors Shipping rates When the type of shipping rate is an invalid

exports[`checkErrors Shipping rates When the type of shipping rate is an invalid value or missing, should not pass 3`] = `"Please select a shipping rate option."`;

exports[`checkErrors Shipping times For flat type When minimum max_time is < 0, should not pass 1`] = `"The minimum shipping time must not be more than the maximum shipping time."`;

exports[`checkErrors Shipping times For flat type When minimum times is < 0, should not pass 1`] = `"Please specify estimated shipping times for all the countries, and the time cannot be less than 0."`;

exports[`checkErrors Shipping times For flat type When there are any selected countries' shipping times is not set, should not pass 1`] = `"Please specify estimated shipping times for all the countries, and the time cannot be less than 0."`;

exports[`checkErrors Shipping times For flat type When there are any shipping times is < 0, should not pass 1`] = `"Please specify estimated shipping times for all the countries, and the time cannot be less than 0."`;
exports[`checkErrors Shipping times For flat type When there are any shipping times are < 0, should not pass 1`] = `"Please specify estimated shipping times for all the countries, and the time cannot be less than 0."`;

exports[`checkErrors Shipping times For flat type shouldnt pass if min time is bigger than max time 1`] = `"The minimum shipping time must not be more than the maximum shipping time."`;

exports[`checkErrors Shipping times When the type of shipping time is an invalid value or missing, should not pass 1`] = `"Please select a shipping time option."`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,24 @@ const checkErrors = (
if (
values.shipping_time === 'flat' &&
( shippingTimes.length < finalCountryCodes.length ||
shippingTimes.some( ( el ) => el.time < 0 ) )
shippingTimes.some( ( el ) => el.time < 0 || el.maxTime < 0 ) )
) {
errors.shipping_country_times = __(
'Please specify estimated shipping times for all the countries, and the time cannot be less than 0.',
'google-listings-and-ads'
);
}

if (
values.shipping_time === 'flat' &&
shippingTimes.some( ( el ) => el.time > el.maxTime )
) {
errors.shipping_country_times = __(
'The minimum shipping time must not be more than the maximum shipping time.',
'google-listings-and-ads'
);
}

/**
* Check tax rate (required for U.S. only).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ function toRates( ...tuples ) {
}

function toTimes( ...tuples ) {
return tuples.map( ( [ countryCode, time ] ) => ( {
return tuples.map( ( [ countryCode, time, maxTime ] ) => ( {
countryCode,
time,
maxTime,
} ) );
}

Expand All @@ -44,7 +45,7 @@ describe( 'checkErrors', () => {
shipping_country_rates: toRates( [ 'US', 10 ], [ 'JP', 30, 88 ] ),
offer_free_shipping: true,
};
const times = toTimes( [ 'US', 3 ], [ 'JP', 10 ] );
const times = toTimes( [ 'US', 3, 3 ], [ 'JP', 10, 10 ] );
const codes = [ 'US', 'JP' ];

const errors = checkErrors( values, times, codes );
Expand Down Expand Up @@ -381,7 +382,7 @@ describe( 'checkErrors', () => {

describe( 'For flat type', () => {
it( `When there are any selected countries' shipping times is not set, should not pass`, () => {
const times = toTimes( [ 'US', 7 ], [ 'FR', 16 ] );
const times = toTimes( [ 'US', 7, 7 ], [ 'FR', 16, 16 ] );
const codes = [ 'US', 'JP', 'FR' ];

const errors = checkErrors( flatShipping, times, codes );
Expand All @@ -391,16 +392,36 @@ describe( 'checkErrors', () => {
} );

it( `When all selected countries' shipping times are set, should pass`, () => {
const times = toTimes( [ 'US', 7 ], [ 'FR', 16 ] );
const times = toTimes( [ 'US', 7, 7 ], [ 'FR', 16, 16 ] );
const codes = [ 'US', 'FR' ];

const errors = checkErrors( flatShipping, times, codes );

expect( errors ).not.toHaveProperty( 'shipping_time' );
} );

it( `When there are any shipping times is < 0, should not pass`, () => {
const times = toTimes( [ 'US', 10 ], [ 'JP', -1 ] );
it( `When there are any shipping times are < 0, should not pass`, () => {
const times = toTimes( [ 'US', 10, 10 ], [ 'JP', -1, -1 ] );
const codes = [ 'US', 'JP' ];

const errors = checkErrors( flatShipping, times, codes );

expect( errors ).toHaveProperty( 'shipping_country_times' );
expect( errors.shipping_country_times ).toMatchSnapshot();
} );

it( `When minimum times is < 0, should not pass`, () => {
const times = toTimes( [ 'US', 10, 10 ], [ 'JP', -1, 10 ] );
const codes = [ 'US', 'JP' ];

const errors = checkErrors( flatShipping, times, codes );

expect( errors ).toHaveProperty( 'shipping_country_times' );
expect( errors.shipping_country_times ).toMatchSnapshot();
} );

it( `When minimum max_time is < 0, should not pass`, () => {
const times = toTimes( [ 'US', 10, 10 ], [ 'JP', 1, -10 ] );
const codes = [ 'US', 'JP' ];

const errors = checkErrors( flatShipping, times, codes );
Expand All @@ -410,13 +431,23 @@ describe( 'checkErrors', () => {
} );

it( `When all shipping times are ≥ 0, should pass`, () => {
const times = toTimes( [ 'US', 1 ], [ 'JP', 0 ] );
const times = toTimes( [ 'US', 1, 1 ], [ 'JP', 0, 0 ] );
const codes = [ 'US', 'JP' ];

const errors = checkErrors( flatShipping, times, codes );

expect( errors ).not.toHaveProperty( 'shipping_time' );
} );

it( `shouldnt pass if min time is bigger than max time`, () => {
const times = toTimes( [ 'US', 1, 0 ], [ 'JP', 1, 1 ] );
const codes = [ 'US', 'JP' ];

const errors = checkErrors( flatShipping, times, codes );

expect( errors ).toHaveProperty( 'shipping_country_times' );
expect( errors.shipping_country_times ).toMatchSnapshot();
} );
} );
} );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const AddTimeModal = ( { countries, onRequestClose, onSubmit } ) => {
initialValues={ {
countries,
time: 0,
maxTime: 0,
} }
validate={ validateShippingTimeGroup }
onSubmit={ handleSubmitCallback }
Expand Down Expand Up @@ -81,7 +82,7 @@ const AddTimeModal = ( { countries, onRequestClose, onSubmit } ) => {
/>
<AppInputNumberControl
label={ __(
'Then the estimated shipping time displayed in the product listing is',
'Then the minimum estimated shipping time displayed in the product listing is',
'google-listings-and-ads'
) }
suffix={ __(
Expand All @@ -90,6 +91,17 @@ const AddTimeModal = ( { countries, onRequestClose, onSubmit } ) => {
) }
{ ...getInputProps( 'time' ) }
/>
<AppInputNumberControl
label={ __(
'And the maximum time is',
'google-listings-and-ads'
) }
suffix={ __(
'days',
'google-listings-and-ads'
) }
{ ...getInputProps( 'maxTime' ) }
/>
</VerticalGapLayout>
</AppModal>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default function ShippingCountriesForm( {
countriesTimeArray.push( {
countries: audienceCountries,
time: null,
maxTime: null,
} );
}

Expand All @@ -57,16 +58,20 @@ export default function ShippingCountriesForm( {
)
);
}
function handleAdd( { countries, time } ) {
function handleAdd( { countries, time, maxTime } ) {
// Split aggregated time, to individial times per country.
const addedIndividualTimes = countries.map( ( countryCode ) => ( {
countryCode,
time,
maxTime,
} ) );

onChange( shippingTimes.concat( addedIndividualTimes ) );
}
function handleChange( { countries, time }, deletedCountries = [] ) {
function handleChange(
{ countries, time, maxTime },
deletedCountries = []
) {
deletedCountries.forEach( ( countryCode ) =>
actualCountries.delete( countryCode )
);
Expand All @@ -76,6 +81,7 @@ export default function ShippingCountriesForm( {
actualCountries.set( countryCode, {
countryCode,
time,
maxTime,
} );
} );
onChange( Array.from( actualCountries.values() ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const EditTimeModal = ( {
initialValues={ {
countries: time.countries,
time: time.time,
maxTime: time.maxTime,
} }
validate={ validateShippingTimeGroup }
onSubmit={ handleSubmitCallback }
Expand Down Expand Up @@ -108,9 +109,10 @@ const EditTimeModal = ( {
}
{ ...getInputProps( 'countries' ) }
/>

<AppInputNumberControl
label={ __(
'Then the estimated shipping time displayed in the product listing is',
'Then the minimum estimated shipping time displayed in the product listing is',
'google-listings-and-ads'
) }
suffix={ __(
Expand All @@ -119,6 +121,18 @@ const EditTimeModal = ( {
) }
{ ...getInputProps( 'time' ) }
/>

<AppInputNumberControl
label={ __(
'And the maximum time is',
'google-listings-and-ads'
) }
suffix={ __(
'days',
'google-listings-and-ads'
) }
{ ...getInputProps( 'maxTime' ) }
/>
</VerticalGapLayout>
</AppModal>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/**
* External dependencies
*/
import { Flex, FlexItem } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import AppInputNumberControl from '.~/components/app-input-number-control';
import AppSpinner from '.~/components/app-spinner';
import ShippingTimeInputControlLabelText from '.~/components/shipping-time-input-control-label-text';
import EditTimeButton from './edit-time-button';
import Stepper from '../time-stepper';
import './index.scss';

/**
Expand All @@ -29,44 +30,85 @@ const CountriesTimeInput = ( {
onChange,
onDelete,
} ) => {
const { countries, time } = value;
const { countries, time, maxTime } = value;

if ( ! audienceCountries ) {
return <AppSpinner />;
}

const handleBlur = ( e, numberValue ) => {
if ( time === numberValue ) {
/**
*
* @param {Object} e The event object
* @param {number} numberValue The string value of the input field converted to a number
* @param {string} field The field name: time or maxTime
*/
const handleBlur = ( e, numberValue, field ) => {
if ( value[ field ] === numberValue ) {
return;
}

onChange( {
countries,
time: numberValue,
...value,
[ field ]: numberValue,
} );
};

/**
*
* @param {number} numberValue The string value of the input field converted to a number
* @param {string} field The field name: time or maxTime
*/
const handleIncrement = ( numberValue, field ) => {
onChange( {
...value,
[ field ]: numberValue,
} );
};

return (
<div className="gla-countries-time-input">
<AppInputNumberControl
label={
<div className="label">
<ShippingTimeInputControlLabelText
countries={ countries }
/>
<EditTimeButton
audienceCountries={ audienceCountries }
onChange={ onChange }
onDelete={ onDelete }
time={ value }
/>
</div>
}
suffix={ __( 'days', 'google-listings-and-ads' ) }
value={ time }
onBlur={ handleBlur }
/>
</div>
<Flex direction="column" className="gla-countries-time-input-container">
<FlexItem>
<div className="label">
<ShippingTimeInputControlLabelText
countries={ countries }
/>
<EditTimeButton
audienceCountries={ audienceCountries }
onChange={ onChange }
onDelete={ onDelete }
time={ value }
/>
</div>
</FlexItem>

<FlexItem>
<Flex justify="space-between" gap="4">
<FlexItem>
<div className="gla-countries-time-input">
<Stepper
handleBlur={ handleBlur }
time={ time }
handleIncrement={ handleIncrement }
field="time"
/>
</div>
</FlexItem>
<FlexItem>
<span>{ __( 'to', 'google-listings-and-ads' ) }</span>
</FlexItem>
<FlexItem>
<div className="gla-countries-time-input">
<Stepper
handleBlur={ handleBlur }
handleIncrement={ handleIncrement }
time={ maxTime }
field="maxTime"
/>
</div>
</FlexItem>
</Flex>
</FlexItem>
</Flex>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.gla-countries-time-input {
max-width: $gla-width-medium;

.gla-countries-time-input-container {
max-width: $gla-width-medium-large;
.label {
display: flex;
justify-content: space-between;
Expand Down
Loading

0 comments on commit cd67ca0

Please sign in to comment.