-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Update Address search - show all inputs #6569
Conversation
onChange callback gets called with an object, and only once when a result is selected
Had to add again `skippedFirstOnChangeText` state to work around the library's feature that is clearing the input at the beginning.
Update IndentityForm input names, now it reads and writes using the same input names for address related fields.
src/libs/ValidationUtils.js
Outdated
@@ -206,7 +206,7 @@ function isValidURL(url) { | |||
* @returns {Object} | |||
*/ | |||
function validateIdentity(identity) { | |||
const requiredFields = ['firstName', 'lastName', 'street', 'city', 'zipCode', 'state', 'ssnLast4', 'dob']; | |||
const requiredFields = ['firstName', 'lastName', 'addressStreet', 'addressCity', 'addressZipCode', 'addressState', 'ssnLast4', 'dob']; |
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.
This change is because I changed these names in the IdentityForm.js for consistency, see lines:
App/src/pages/ReimbursementAccount/IdentityForm.js
Lines 155 to 156 in 970c2d5
value={props.values.state} | |
onChange={value => props.onFieldChange('addressState', value)} |
IdentityForm is reading values using one key (i.e. state
) and then calling onChangeText
with a different key (i.e. addressState
). This just makes it harder to follow and update the logic where we already have some key renaming (i.e. requestor step, beneficial owners).
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.
This just makes it harder to follow and update the logic where we already have some key renaming (i.e. requestor step, beneficial owners).
Unsure what is hard to follow or update, but won't we have to "rename" again if something else decides to use this method which is meant to be generic? Could be better to just leave it generic and then pass in fields we want to validate regardless of what they are named.
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.
Well, looking at it again, this function could really stay as it is. The thing that was confusing is the IdentityForm reading an input with one key and then calling "onFieldChange" with another key.
Two more things:
|
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.
While testing, I've seen the following behavior:
- Selecting an address from the dropdown does not clear City and Zip Code errors (see video below).
- Just leaving this one here for the record, as we might want to fix it in a different PR. When the dropdown is open, tabbing out will cycle through all dropdown options (without highlighting them) until we finally land on the City field after 6 tabs. The only way to close the dropdown then is to focus on it again and click away or selecting one of the values. Maybe we should close the dropdown on tab out?
error.mov
Thanks 😬 , will have a look at it!
There is this separate GH issue for that: #6235 |
Fixed! |
I'm going on vacations for two weeks starting on Monday, so I won't be able to continue and finish this during that time. If anyone wants to use this PR and finish it, feel free to do it. Also feel free to pick the original issue and do a new PR if this doesn't seem to be the right path :) If the issue is still there when I come back, I can start working on it. I don't think it is of high priority because the user currently can enter any address manually. |
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.
Left a few more comments and ideas for how to improve a few things.
I think it would be good to maybe try and just wrap this one up and really try to do the bare minimum simplest things we can to make this work - then revisit things like perfecting errors and validation once the forms doc refactors start.
src/libs/ValidationUtils.js
Outdated
@@ -206,7 +206,7 @@ function isValidURL(url) { | |||
* @returns {Object} | |||
*/ | |||
function validateIdentity(identity) { | |||
const requiredFields = ['firstName', 'lastName', 'street', 'city', 'zipCode', 'state', 'ssnLast4', 'dob']; | |||
const requiredFields = ['firstName', 'lastName', 'addressStreet', 'addressCity', 'addressZipCode', 'addressState', 'ssnLast4', 'dob']; |
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.
This just makes it harder to follow and update the logic where we already have some key renaming (i.e. requestor step, beneficial owners).
Unsure what is hard to follow or update, but won't we have to "rename" again if something else decides to use this method which is meant to be generic? Could be better to just leave it generic and then pass in fields we want to validate regardless of what they are named.
src/components/AddressSearch.js
Outdated
// We use `skippedFirstOnChangeText` to work around a feature of the library: | ||
// The library is calling onChangeText with '' at the start and we don't need this | ||
// https://github.com/FaridSafi/react-native-google-places-autocomplete/blob/47d7223dd48f85da97e80a0729a985bbbcee353f/GooglePlacesAutocomplete.js#L148 | ||
const [skippedFirstOnChangeText, setSkippedFirstOnChangeText] = useState(false); |
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.
Not saying this is likely but example of how this could go wrong in the future...
- Someone updates this library and the bug gets fixed
- They are now stuck having to figure out why the first
onChangeText
never fires - Maybe they find this code eventually 😂
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 an issue here FaridSafi/react-native-google-places-autocomplete#786
@@ -233,14 +252,20 @@ class ACHContractStep extends React.Component { | |||
</ExpensifyText> | |||
<IdentityForm | |||
style={[styles.mb2]} | |||
onFieldChange={(inputKey, value) => this.clearErrorAndSetBeneficialOwnerValue(index, inputKey, value)} | |||
onFieldChange={(inputKeyOrValues, value) => { |
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.
Having onFieldChange
return one of two possible things is a bit strange.
What if we just return an object and have the validation and error clearing handled all at the same time for the various fields?
Sorry to sound like a broken record, but the way we validate these will be changing soon in pretty drastic ways so maybe we should not spend much time perfecting this.
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.
Having onFieldChange return one of two possible things is a bit strange.
Agreed, didn't liked it, but it was what I came up with to solve the clearing multiple errors in a batch.
What if we just return an object and have the validation and error clearing handled all at the same time for the various fields?
the idea is to ALWAYS pass an object to the onFieldChange
callback, even if we are just changing a single field like firstName
, right?
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.
Update: onFieldChange
gets called with an object every time.
/> | ||
<ExpensifyText style={[styles.mutedTextLabel, styles.mt1]}>{props.translate('common.noPO')}</ExpensifyText> | ||
<View style={[styles.flexRow, styles.mt4]}> | ||
<View style={[styles.flex2, styles.mr2]}> |
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.
opportunity to DRY up the code perhaps?
Looks very similar to the lines here:
App/src/pages/ReimbursementAccount/CompanyStep.js
Lines 223 to 227 in b0fe744
<ExpensifyText style={[styles.mutedTextLabel, styles.mt1]}>{this.props.translate('common.noPO')}</ExpensifyText> | |
<View style={[styles.flexRow, styles.mt4]}> | |
<View style={[styles.flex2, styles.mr2]}> | |
<ExpensiTextInput | |
label={this.props.translate('common.city')} |
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.
Did some refactoring for this. Added AddressForm
component, the name was based on the name for IndentityForm
, which is also a "partial" form.
@aldo-expensify just checking on this one. Are you actively working on it and if not should we close it? |
I'll try to revive this PR and address your comments, there are a bunch of conflicts now :P |
Update:
|
Update: |
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.
Tests well, nice job and thanks for sticking with it!
|
||
const values = {}; | ||
if (street && street.length > props.value.length) { | ||
// Don't replace if the user has typed something longer. I.e. maybe the user entered the Apt # |
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.
Thanks for the change!
NAB, this comment does help explain why we might not want to use the street number and street name provided by Google Places. I do have a lingering concern that we are not quite painting the exact picture of why we need this logic. Here's a suggestion that is more verbose, but explains the situation in greater detail
We are only passing the street number and name if the combined length is longer than the value that was initially passed to the autocomplete component. Google Places can truncate details like Apt # and this is the best way we have to tell that the new value it's giving us is less specific than the one the user entered manually.
const state = GooglePlacesUtils.getAddressComponent(addressComponents, 'administrative_area_level_1', 'short_name'); | ||
|
||
const values = {}; | ||
if (street && street.length > props.value.length) { |
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.
NAB, should we trim props.value.length
in case there is whitespace that makes it longer but less accurate?
Also, I wonder if we need to worry about a case like this where someone enters some very long address into the street input. I feel like in trying to optimize for the Apartment # case we are maybe introducing the possibility of bad street info instead of trusting the correctness of what Google Places returns.
Here's an example of what I mean:
I think we can maybe wait and see if this is a problem...
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.
NAB, should we trim
props.value.length
in case there is whitespace that makes it longer but less accurate?Also, I wonder if we need to worry about a case like this where someone enters some very long address into the street input. I feel like in trying to optimize for the Apartment # case we are maybe introducing the possibility of bad street info instead of trusting the correctness of what Google Places returns.
I think we can maybe wait and see if this is a problem...
I honestly don't like it either, I think the ideal case is to have the Apt number in a separate field. As @luacmartins , this also causes the autocomplete to start showing results while the user may be only trying to input the apt number and it looks weird :P. Having said that, if we wanted to do a separate input for it, I think that should be done in a separate pr/issue.
errors: {}, | ||
}; | ||
|
||
const AddressForm = props => ( |
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.
Thanks for refactoring this!
this.setValue(renamedValues); | ||
this.clearErrors(_.keys(renamedValues)); | ||
}} | ||
/> |
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.
So much cleaner than what we had before 👏
onFieldChange={(values) => { | ||
const renamedValues = {}; | ||
_.each(values, (value, inputKey) => { | ||
const renamedInputKey = lodashGet(this.renamedFields, inputKey, inputKey); |
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.
NAB, if we are not using this.renamedFields
again we could just declare them in this method. Only thought to mention it because we are doing that in the RequestorStep
and could keep it consistent.
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
🚀 Deployed to staging by @marcaaron in version: 1.1.23-2 🚀
|
_.each(values, (value, inputKey) => { | ||
if (inputKey === 'city') { | ||
return; | ||
} |
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.
Just noticed this, but don't we want to update the city
field?
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 saw that the city
field wasn't properly added in any of these places in the component: state, requiredFields and errorTranslationKeys.
And, most importantly, we didn't really use it when sending the information to the backend here:
App/src/libs/actions/PaymentMethods.js
Lines 43 to 71 in 87f41cc
function addBillingCard(params) { | |
const cardMonth = CardUtils.getMonthFromExpirationDateString(params.expirationDate); | |
const cardYear = CardUtils.getYearFromExpirationDateString(params.expirationDate); | |
Onyx.merge(ONYXKEYS.ADD_DEBIT_CARD_FORM, {submitting: true}); | |
API.AddBillingCard({ | |
cardNumber: params.cardNumber, | |
cardYear, | |
cardMonth, | |
cardCVV: params.securityCode, | |
addressName: params.nameOnCard, | |
addressZip: params.addressZipCode, | |
currency: CONST.CURRENCY.USD, | |
}).then(((response) => { | |
let errorMessage = ''; | |
if (response.jsonCode === 200) { | |
const cardObject = { | |
additionalData: { | |
isBillingCard: false, | |
isP2PDebitCard: true, | |
}, | |
addressName: params.nameOnCard, | |
addressState: params.addressState, | |
addressStreet: params.addressStreet, | |
addressZip: params.addressZipCode, | |
cardMonth, | |
cardNumber: CardUtils.maskCardNumber(params.cardNumber), | |
cardYear, | |
currency: 'USD', |
I thought that it was added by mistake when the "manual" switch was added. It made sense to me that it wasn't being sent to the back because I have seen many card forms that don't have a "City" field.
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.
ah, not used by the backend hmm OK then thanks!
🚀 Deployed to production by @francoisl in version: 1.1.24-8 🚀
|
Added reviewers from previous PR because you already have some context and saw most of these changes :)
Similar to the previous PR, but without moving the error handling to the form's state. I did a work around to batch clearing errors and kept the errors in Onyx.
Details
Allow the user to manually complete address information and all fields are visible:
state
,city
andzip code
fields back in the following steps: "Company Step", "Personal information (requestor)" and "Additional information (beneficial owners)"state
andzip code
to AddDebitCardPageFixed Issues
$ #6309
Tests / QA steps
Company address
,City
,Zip Code
, andState
:Company address
and select a result. The other address fields (City
,Zip Code
, andState
) should get updated with the information from the selected result.Company address
field and type#12334
at the end (adding the apt number).settings/payments/add-debit-card
and do the same steps (sub-steps in step (3)). The only difference in this view is that theCity
field is not available.Tested On
Screenshots
Web
Mobile Web
Desktop
iOS
Android