-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement PhoneNumberCard
and ContactInformation
components for contact info
#917
Changes from all commits
05ba64b
5ba451b
5de074b
18cd8fa
17d5581
71ef39f
2deed28
8f42c78
2a59a99
6e839d8
2a5ab61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber'; | ||
import Section from '.~/wcdl/section'; | ||
import VerticalGapLayout from '.~/components/vertical-gap-layout'; | ||
import AppDocumentationLink from '.~/components/app-documentation-link'; | ||
import AppSpinner from '.~/components/app-spinner'; | ||
import PhoneNumberCard from './phone-number-card'; | ||
|
||
const description = __( | ||
'Your contact information is required by Google for verification purposes. It will be shared with the Google Merchant Center and will not be displayed to customers.', | ||
'google-listings-and-ads' | ||
); | ||
|
||
const mcTitle = __( 'Enter contact information', 'google-listings-and-ads' ); | ||
const settingsTitle = __( 'Contact information', 'google-listings-and-ads' ); | ||
|
||
export default function ContactInformation( { view, onPhoneNumberChange } ) { | ||
const phone = useGoogleMCPhoneNumber(); | ||
const isSetupMC = view === 'setup-mc'; | ||
|
||
const initEditing = isSetupMC ? ! phone.data.isValid : true; | ||
const title = isSetupMC ? mcTitle : settingsTitle; | ||
const trackContext = isSetupMC | ||
? 'setup-mc-contact-information' | ||
: 'settings-contact-information'; | ||
|
||
return ( | ||
<Section | ||
title={ title } | ||
description={ | ||
<div> | ||
<p>{ description }</p> | ||
<p> | ||
<AppDocumentationLink | ||
context={ trackContext } | ||
linkId="contact-information-read-more" | ||
// TODO: [lite-contact-info] add link | ||
href="https://example.com/" | ||
> | ||
{ __( 'Learn more', 'google-listings-and-ads' ) } | ||
</AppDocumentationLink> | ||
</p> | ||
</div> | ||
} | ||
> | ||
{ phone.loaded ? ( | ||
<VerticalGapLayout size="large"> | ||
<PhoneNumberCard | ||
phoneNumber={ phone } | ||
initEditing={ initEditing } | ||
onPhoneNumberChange={ onPhoneNumberChange } | ||
/> | ||
<div>TODO: add store address card</div> | ||
</VerticalGapLayout> | ||
) : ( | ||
<AppSpinner /> | ||
) } | ||
</Section> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { getCountryCallingCode } from 'libphonenumber-js'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { useState } from '@wordpress/element'; | ||
import { Flex, FlexItem, FlexBlock, CardDivider } from '@wordpress/components'; | ||
import { Spinner } from '@woocommerce/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import useCountryCallingCodeOptions from '.~/hooks/useCountryCallingCodeOptions'; | ||
import Section from '.~/wcdl/section'; | ||
import SelectControl from '.~/wcdl/select-control'; | ||
import AppInputControl from '.~/components/app-input-control'; | ||
import AccountCard, { APPEARANCE } from '.~/components/account-card'; | ||
import AppButton from '.~/components/app-button'; | ||
import AppSpinner from '.~/components/app-spinner'; | ||
import './phone-number-card.scss'; | ||
|
||
const noop = () => {}; | ||
|
||
function PhoneNumberContent( { | ||
initCountry, | ||
initNationalNumber, | ||
onPhoneNumberChange, | ||
} ) { | ||
const countryCallingCodeOptions = useCountryCallingCodeOptions(); | ||
const [ country, setCountry ] = useState( initCountry ); | ||
const [ number, setNumber ] = useState( initNationalNumber ); | ||
|
||
const handleChange = ( nextCountry, nextNumber ) => { | ||
setCountry( nextCountry ); | ||
setNumber( nextNumber ); | ||
|
||
const countryCallingCode = nextCountry | ||
? getCountryCallingCode( nextCountry ) | ||
: ''; | ||
onPhoneNumberChange( countryCallingCode, nextNumber, nextCountry ); | ||
}; | ||
|
||
const handleCountryChange = ( nextCountry ) => | ||
handleChange( nextCountry, number ); | ||
|
||
const handleNumberChange = ( nextNumber ) => | ||
handleChange( country, nextNumber ); | ||
|
||
return ( | ||
<Section.Card.Body> | ||
<Flex gap={ 4 }> | ||
<FlexItem> | ||
<SelectControl | ||
label={ __( | ||
'Country code', | ||
'google-listings-and-ads' | ||
) } | ||
isSearchable | ||
excludeSelectedOptions={ false } | ||
options={ countryCallingCodeOptions } | ||
selected={ country } | ||
onChange={ handleCountryChange } | ||
/> | ||
</FlexItem> | ||
<FlexBlock> | ||
<AppInputControl | ||
label={ __( | ||
'Phone number', | ||
'google-listings-and-ads' | ||
) } | ||
value={ number } | ||
onChange={ handleNumberChange } | ||
/> | ||
</FlexBlock> | ||
</Flex> | ||
</Section.Card.Body> | ||
); | ||
} | ||
|
||
export default function PhoneNumberCard( { | ||
phoneNumber, | ||
isPreview, | ||
initEditing, | ||
onEditClick, | ||
onPhoneNumberChange = noop, | ||
} ) { | ||
const [ isEditing, setEditing ] = useState( initEditing ); | ||
const { loaded, data } = phoneNumber; | ||
|
||
const editButton = isEditing ? null : ( | ||
<AppButton | ||
isSecondary | ||
onClick={ () => { | ||
if ( onEditClick ) { | ||
onEditClick(); | ||
} else { | ||
setEditing( true ); | ||
} | ||
} } | ||
> | ||
{ __( 'Edit', 'google-listings-and-ads' ) } | ||
</AppButton> | ||
); | ||
|
||
let description = null; | ||
let phoneNumberContent = null; | ||
|
||
if ( isEditing ) { | ||
description = __( | ||
'Please enter a phone number to be used for verification.', | ||
'google-listings-and-ads' | ||
); | ||
|
||
if ( loaded ) { | ||
phoneNumberContent = ( | ||
<> | ||
<CardDivider /> | ||
<PhoneNumberContent | ||
initCountry={ data.country } | ||
initNationalNumber={ data.nationalNumber } | ||
onPhoneNumberChange={ onPhoneNumberChange } | ||
/> | ||
</> | ||
); | ||
} else { | ||
phoneNumberContent = <AppSpinner />; | ||
} | ||
} else { | ||
description = loaded ? data.display : <Spinner />; | ||
Comment on lines
+114
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ Having the preloading logic here and in https://github.com/woocommerce/google-listings-and-ads/pull/917/files#diff-51ff763be0b20871d193755311c5c437caafd6c4d46bb1413de599e1124a7714R53 looks like too much. Also, in one case we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I put the wrong link. Here is code sample/suggestion https://github.com/woocommerce/google-listings-and-ads/pull/917/files#r681624815 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Speaking of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💅 📜 For the block spinner There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed the loading UI in the later PR #921, and it shows a spinner with a card layout instead. The demo in another PR shows that change: Kapture.2021-08-02.at.18.37.16.mp4 |
||
} | ||
|
||
return ( | ||
<AccountCard | ||
className="gla-phone-number-card" | ||
appearance={ APPEARANCE.PHONE } | ||
description={ description } | ||
hideIcon={ isPreview } | ||
indicator={ editButton } | ||
> | ||
{ phoneNumberContent } | ||
</AccountCard> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
.gla-phone-number-card { | ||
// Country code selector | ||
.wcdl-select-control { | ||
.wcdl-select-control__input { | ||
margin-bottom: 0; | ||
} | ||
|
||
.woocommerce-select-control { | ||
.components-base-control { | ||
height: 36px; | ||
border-color: $gray-600; | ||
} | ||
|
||
.woocommerce-select-control__control-input { | ||
font-size: 13px; | ||
color: $gray-900; | ||
} | ||
|
||
.woocommerce-select-control__listbox { | ||
top: 37px; | ||
} | ||
|
||
.woocommerce-select-control__option { | ||
min-height: 36px; | ||
font-size: 13px; | ||
tomalec marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
} | ||
|
||
// Phone number input | ||
.components-input-control .components-input-control__container { | ||
.components-input-control__input { | ||
height: 36px; | ||
font-size: 13px; | ||
color: $gray-900; | ||
} | ||
|
||
.components-input-control__backdrop { | ||
border-color: $gray-600; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { useMemo } from '@wordpress/element'; | ||
import { getCountries, getCountryCallingCode } from 'libphonenumber-js'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import useCountryKeyNameMap from './useCountryKeyNameMap'; | ||
|
||
const toOption = ( country, countryCallingCode, countryName ) => ( { | ||
key: country, | ||
keywords: [ countryName, countryCallingCode, country ], | ||
label: `${ countryName } (+${ countryCallingCode })`, | ||
} ); | ||
|
||
export default function useCountryCallingCodeOptions() { | ||
const countryNameDict = useCountryKeyNameMap(); | ||
|
||
return useMemo( () => { | ||
return getCountries().reduce( ( acc, country ) => { | ||
const countryName = countryNameDict[ country ]; | ||
if ( countryName ) { | ||
const countryCallingCode = getCountryCallingCode( country ); | ||
acc.push( | ||
toOption( country, countryCallingCode, countryName ) | ||
); | ||
} | ||
return acc; | ||
}, [] ); | ||
}, [ countryNameDict ] ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Speaking of #917 (comment) I think this loading state handling is redundant, and delivers slightly worse UX.
We can remove the redundancy from
ContactInformation
and let thePhoneNumberCard
handle loading state itself.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the change suggested above, we would have to change
initEditing
handling.Personally, in
ContactInformation
I'd cater forisSetupMC
only. Then letPhoneNumberCard
reason aboutphone.data.isValid
+ some given property..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare this way to #917 (comment), it could only deliver the phone icon and "Phone number" title earlier about 0.5 ~ 1.5 seconds at the most. The rest elements are still needed to wait for data loaded. I'm not sure if that is worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say 0.5~1.5 s on a developer machine is a lot. Then imagine how long it could take on a low-end device with a slow network.
Personally, I see it the opposite, compare:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened another PR #934 to change the handling of loading states.