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:
parent
a2945306ac
commit
abe3488861
|
@ -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' }
|
||||||
|
|
|
@ -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;
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -32,6 +32,7 @@ export type ShippingMethodSettings = {
|
||||||
|
|
||||||
export type ReadOnlySettings = {
|
export type ReadOnlySettings = {
|
||||||
storeCountry: string;
|
storeCountry: string;
|
||||||
|
storeState: string;
|
||||||
hasLegacyPickup: boolean;
|
hasLegacyPickup: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue