Local Pickup: merge country and state into same field in location modal (https://github.com/woocommerce/woocommerce-blocks/pull/8408)

* Get the default store state

We are merging the Country & State fields. So, we should get both
default values

* Merge the Country & State fields

We merged both fields in the Pickup Location form modal

We can see a similar example in `WooCommerce -> Settings -> General`

* Clean up the code

* Show the state text field above the country select

* Create some util functions

* Refactor the Form component

* Display a user friendly country and state in admin

* Fix country defaulting to "Afghan" bug
This commit is contained in:
Saad Tarhi 2023-03-14 14:49:31 +01:00 committed by GitHub
parent a2945306ac
commit abe3488861
6 changed files with 139 additions and 90 deletions

View File

@ -3,13 +3,12 @@
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { SelectControl, TextControl } from '@wordpress/components'; import { SelectControl, TextControl } from '@wordpress/components';
import { getSetting } from '@woocommerce/settings';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import type { PickupLocation } from '../types'; import type { PickupLocation } from '../types';
import StateControl from './state-control'; import { countryStateOptions, states } from '../utils';
const Form = ( { const Form = ( {
formRef, formRef,
@ -20,11 +19,7 @@ const Form = ( {
values: PickupLocation; values: PickupLocation;
setValues: React.Dispatch< React.SetStateAction< PickupLocation > >; setValues: React.Dispatch< React.SetStateAction< PickupLocation > >;
} ) => { } ) => {
const countries = getSetting< Record< string, string > >( 'countries', [] ); const { country: selectedCountry, state: selectedState } = values.address;
const states = getSetting< Record< string, Record< string, string > > >(
'countryStates',
[]
);
const setLocationField = const setLocationField =
( field: keyof PickupLocation ) => ( newValue: string | boolean ) => { ( field: keyof PickupLocation ) => ( newValue: string | boolean ) => {
setValues( ( prevValue: PickupLocation ) => ( { setValues( ( prevValue: PickupLocation ) => ( {
@ -45,6 +40,10 @@ const Form = ( {
} ) ); } ) );
}; };
const countryHasStates =
states[ selectedCountry ] &&
Object.keys( states[ selectedCountry ] ).length > 0;
return ( return (
<form ref={ formRef }> <form ref={ formRef }>
<TextControl <TextControl
@ -97,42 +96,77 @@ const Form = ( {
onChange={ setLocationAddressField( 'postcode' ) } onChange={ setLocationAddressField( 'postcode' ) }
autoComplete="off" autoComplete="off"
/> />
<StateControl { ! countryHasStates && (
label={ __( 'State', 'woo-gutenberg-products-block' ) } <TextControl
name={ 'location_state' } placeholder={ __(
hideLabelFromVision={ true } 'State',
placeholder={ __( 'State', 'woo-gutenberg-products-block' ) } 'woo-gutenberg-products-block'
value={ values.address.state } ) }
onChange={ setLocationAddressField( 'state' ) } value={ selectedState }
autoComplete="off" onChange={ setLocationAddressField( 'state' ) }
states={ states } />
currentCountry={ values.address.country } ) }
/>
<SelectControl <SelectControl
label={ __( 'Country', 'woo-gutenberg-products-block' ) } name="location_country_state"
name={ 'location_country' } label={ __(
'Country / State',
'woo-gutenberg-products-block'
) }
hideLabelFromVision={ true } hideLabelFromVision={ true }
placeholder={ __( 'Country', 'woo-gutenberg-products-block' ) } placeholder={ __(
value={ values.address.country } 'Country / State',
'woo-gutenberg-products-block'
) }
value={ ( () => {
if ( ! selectedState && countryHasStates ) {
return `${ selectedCountry }:${
Object.keys( states[ selectedCountry ] )[ 0 ]
}`;
}
return `${ selectedCountry }${
selectedState &&
states[ selectedCountry ]?.[ selectedState ]
? ':' + selectedState
: ''
}`;
} )() }
onChange={ ( val: string ) => { onChange={ ( val: string ) => {
setLocationAddressField( 'state' )( '' ); const [ country, state = '' ] = val.split( ':' );
setLocationAddressField( 'country' )( val ); setLocationAddressField( 'country' )( country );
setLocationAddressField( 'state' )( state );
} } } }
autoComplete="off" >
options={ [ { countryStateOptions.options.map( ( option ) => {
{ if ( option.label ) {
value: '', return (
disabled: true, <optgroup
label: __( 'Country', 'woo-gutenberg-products-block' ), key={ option.label }
}, label={ option.label }
...Object.entries( countries ).map( >
( [ code, country ] ) => ( { { option.options.map( ( subOption ) => (
value: code, <option
label: country, key={ subOption.value }
} ) value={ subOption.value }
), >
] } { subOption.label }
/> </option>
) ) }
</optgroup>
);
}
return (
<option
key={ option.options[ 0 ].value }
value={ option.options[ 0 ].value }
>
{ option.options[ 0 ].label }
</option>
);
} ) }
</SelectControl>
<TextControl <TextControl
label={ __( 'Pickup details', 'woo-gutenberg-products-block' ) } label={ __( 'Pickup details', 'woo-gutenberg-products-block' ) }
name={ 'pickup_details' } name={ 'pickup_details' }

View File

@ -1,45 +0,0 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { SelectControl, TextControl } from '@wordpress/components';
const StateControl = ( {
states,
currentCountry,
...props
}: {
states: Record< string, Record< string, string > >;
currentCountry: string;
} ): JSX.Element | null => {
const filteredStates = states[ currentCountry ] || [];
if ( filteredStates.length === 0 ) {
return (
<TextControl
{ ...props }
disabled={ ! currentCountry || props.disabled }
/>
);
}
return (
<SelectControl
{ ...props }
options={ [
{
value: '',
disabled: true,
label: __( 'State', 'woo-gutenberg-products-block' ),
},
...Object.entries( filteredStates ).map(
( [ code, state ] ) => ( {
value: code,
label: state,
} )
),
] }
/>
);
};
export default StateControl;

View File

@ -4,7 +4,7 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element'; import { useState } from '@wordpress/element';
import type { UniqueIdentifier } from '@dnd-kit/core'; import type { UniqueIdentifier } from '@dnd-kit/core';
import { isObject, isBoolean } from '@woocommerce/types'; import { isBoolean } from '@woocommerce/types';
import { ToggleControl, Button, ExternalLink } from '@wordpress/components'; import { ToggleControl, Button, ExternalLink } from '@wordpress/components';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
@ -19,6 +19,7 @@ import {
import EditLocation from './edit-location'; import EditLocation from './edit-location';
import type { SortablePickupLocation } from './types'; import type { SortablePickupLocation } from './types';
import { useSettingsContext } from './settings-context'; import { useSettingsContext } from './settings-context';
import { getUserFriendlyAddress } from './utils';
const LocationSettingsDescription = () => ( const LocationSettingsDescription = () => (
<> <>
@ -62,10 +63,7 @@ const LocationSettings = () => {
<> <>
{ row.name } { row.name }
<StyledAddress> <StyledAddress>
{ isObject( row.address ) && { getUserFriendlyAddress( row.address ) }
Object.values( row.address )
.filter( ( value ) => value !== '' )
.join( ', ' ) }
</StyledAddress> </StyledAddress>
</> </>
), ),
@ -137,7 +135,7 @@ const LocationSettings = () => {
address: { address: {
address_1: '', address_1: '',
city: '', city: '',
state: '', state: readOnlySettings.storeState,
postcode: '', postcode: '',
country: readOnlySettings.storeCountry, country: readOnlySettings.storeCountry,
}, },

View File

@ -32,6 +32,7 @@ export type ShippingMethodSettings = {
export type ReadOnlySettings = { export type ReadOnlySettings = {
storeCountry: string; storeCountry: string;
storeState: string;
hasLegacyPickup: boolean; hasLegacyPickup: boolean;
}; };

View File

@ -3,6 +3,8 @@
*/ */
import { cleanForSlug } from '@wordpress/url'; import { cleanForSlug } from '@wordpress/url';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { isObject } from '@woocommerce/types';
import { getSetting } from '@woocommerce/settings';
/** /**
* Internal dependencies * Internal dependencies
*/ */
@ -33,6 +35,7 @@ export const defaultSettings = {
export const defaultReadyOnlySettings = { export const defaultReadyOnlySettings = {
hasLegacyPickup: false, hasLegacyPickup: false,
storeCountry: '', storeCountry: '',
storeState: '',
}; };
declare global { declare global {
const hydratedScreenSettings: { const hydratedScreenSettings: {
@ -65,3 +68,60 @@ export const getInitialPickupLocations = (): SortablePickupLocation[] =>
export const readOnlySettings = export const readOnlySettings =
hydratedScreenSettings.readonlySettings || defaultReadyOnlySettings; hydratedScreenSettings.readonlySettings || defaultReadyOnlySettings;
export const countries = getSetting< Record< string, string > >(
'countries',
[]
);
export const states = getSetting< Record< string, Record< string, string > > >(
'countryStates',
[]
);
export const getUserFriendlyAddress = ( address: unknown ) => {
const updatedAddress = isObject( address ) && {
...address,
country:
typeof address.country === 'string' && countries[ address.country ],
state:
typeof address.country === 'string' &&
typeof address.state === 'string' &&
states[ address.country ]?.[ address.state ]
? states[ address.country ][ address.state ]
: address.state,
};
return Object.values( updatedAddress )
.filter( ( value ) => value !== '' )
.join( ', ' );
};
// Outputs the list of countries and states in a single dropdown select.
const countryStateDropdownOptions = () => {
const countryStateOptions = Object.keys( countries ).map( ( country ) => {
const countryStates = states[ country ] || {};
if ( Object.keys( countryStates ).length === 0 ) {
return {
options: [
{
value: country,
label: countries[ country ],
},
],
};
}
const stateOptions = Object.keys( countryStates ).map( ( state ) => ( {
value: `${ country }:${ state }`,
label: `${ countries[ country ] }${ countryStates[ state ] }`,
} ) );
return {
label: countries[ country ],
options: [ ...stateOptions ],
};
} );
return {
options: countryStateOptions,
};
};
export const countryStateOptions = countryStateDropdownOptions();

View File

@ -302,6 +302,7 @@ class ShippingController {
'readonlySettings' => array( 'readonlySettings' => array(
'hasLegacyPickup' => $has_legacy_pickup, 'hasLegacyPickup' => $has_legacy_pickup,
'storeCountry' => WC()->countries->get_base_country(), 'storeCountry' => WC()->countries->get_base_country(),
'storeState' => WC()->countries->get_base_state(),
), ),
); );