Skip to content
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

[N6.1] Use Google AddressSearch component in VBA Company Step #5643

Merged
merged 30 commits into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0a67fed
Add google search lib to code
tgolen Sep 30, 2021
11bfd92
Add skeleton component for address search
tgolen Sep 30, 2021
57b0d97
Add test component to the page
tgolen Sep 30, 2021
aff65c0
Remove API key from config
tgolen Sep 30, 2021
8cf96d7
Make proxy request for search
tgolen Sep 30, 2021
f19193d
Collect all place information when selecting an option
tgolen Sep 30, 2021
fcbe85b
Use a custom input and style it
tgolen Oct 1, 2021
9b572ea
Get the custom component working
tgolen Oct 1, 2021
be6326d
Set the initial value of the address
tgolen Oct 1, 2021
7fa7ab7
Remove unused form fields
tgolen Oct 4, 2021
7603874
Style the results container
tgolen Oct 4, 2021
3bfd396
Merge branch 'main' of github.com:Expensify/App into tgolen-google-ad…
Oct 4, 2021
e851c65
added phone number validation
Oct 4, 2021
89fadbb
flex direction none not allowed
Oct 4, 2021
230f50d
swapped functional component to class
Oct 5, 2021
efea621
js style
Oct 5, 2021
7a09620
ignore virtualized list error
Oct 5, 2021
de295e8
refactor address info to method
Oct 5, 2021
23bada3
slight name change
Oct 5, 2021
d4aff2d
style
Oct 5, 2021
ef4d2db
thread details into the helper method
Oct 5, 2021
3d7f624
facepalm
Oct 5, 2021
b7116c0
used built in style setter
Oct 6, 2021
6b243d5
added a few custom styles
Oct 6, 2021
b5d9c2c
remove unneeded imports
Oct 6, 2021
ebe8522
js style
Oct 6, 2021
65ce8ac
text style
Oct 6, 2021
911979c
style
Oct 6, 2021
b8f1e05
style adjustment
Oct 6, 2021
dd9f01f
decrease top margin on dropdown
Oct 7, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const includeModules = [
'react-native-onyx',
'react-native-gesture-handler',
'react-native-flipper',
'react-native-google-places-autocomplete',
].join('|');

const webpackConfig = {
Expand Down
20 changes: 18 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"react-native-config": "^1.4.0",
"react-native-document-picker": "^5.1.0",
"react-native-gesture-handler": "1.9.0",
"react-native-google-places-autocomplete": "^2.4.1",
"react-native-image-pan-zoom": "^2.1.12",
"react-native-image-picker": "^4.0.3",
"react-native-keyboard-spacer": "^0.4.1",
Expand Down
124 changes: 124 additions & 0 deletions src/components/AddressSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import _ from 'underscore';
import React, {useRef, useEffect} from 'react';
import PropTypes from 'prop-types';
import {View, Text} from 'react-native';
import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete';
import CONFIG from '../CONFIG';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import styles from '../styles/styles';
import ExpensiTextInput from './ExpensiTextInput';

const propTypes = {
/** The label to display for the field */
label: PropTypes.string.isRequired,

/** The value to set the field to initially */
value: PropTypes.string,

/** A callback function when the value of this field has changed */
onChangeText: PropTypes.func.isRequired,

/** Customize the ExpensiTextInput container */
containerStyles: PropTypes.arrayOf(PropTypes.object),

...withLocalizePropTypes,
};
const defaultProps = {
value: '',
containerStyles: null,
};

const AddressSearch = (props) => {
// Using a hook is the only way to see the value of this component
const ref = useRef();
useEffect(() => {
ref?.current?.setAddressText(props.value);
}, []);

/**
* @param {Object} details See https://developers.google.com/maps/documentation/places/web-service/details#PlaceDetailsResponses
*/
const saveLocationDetails = (details) => {
if (details.address_components) {
// Gather the values from the Google details
const streetNumber = _.chain(details.address_components)
.find(component => _.contains(component.types, 'street_number'))
.get('long_name')
.value();
const streetName = _.chain(details.address_components)
.find(component => _.contains(component.types, 'route'))
.get('long_name')
.value();
const city = _.chain(details.address_components)
.find(component => _.contains(component.types, 'locality'))
.get('long_name')
.value();
const state = _.chain(details.address_components)
.find(component => _.contains(component.types, 'administrative_area_level_1'))
.get('short_name')
.value();
const zipCode = _.chain(details.address_components)
.find(component => _.contains(component.types, 'postal_code'))
.get('long_name')
.value();

// Trigger text change events for each of the individual fields being saved on the server
props.onChangeText('addressStreet', `${streetNumber} ${streetName}`);
props.onChangeText('addressCity', city);
props.onChangeText('addressState', state);
props.onChangeText('addressZipCode', zipCode);
}
};

return (
<GooglePlacesAutocomplete
ref={ref}
fetchDetails
keepResultsAfterBlur
suppressDefaultStyles
enablePoweredByContainer={false}
onPress={(data, details) => saveLocationDetails(details)}
query={{
key: 'AIzaSyC4axhhXtpiS-WozJEsmlL3Kg3kXucbZus',
language: props.preferredLocale,
}}
requestUrl={{
useOnPlatform: 'web',
url: `${CONFIG.EXPENSIFY.URL_EXPENSIFY_COM}api?command=Proxy_GooglePlaces&proxyUrl=`,
}}
textInputProps={{
InputComp: ExpensiTextInput,
label: props.label,
containerStyles: props.containerStyles,
}}
styles={{
textInputContainer: [styles.googleSearchTextInputContainer],
}}
renderRow={(data, index) => {
// This is using a custom render component in order for the styles to be properly applied to the top row.
// The styles for the top and bottom row could have instead by passed to `styles.listView` for
// <GooglePlacesAutocomplete>, however the list is always visible, even when there are no results.
// Because of this, if the padding and borders are applied to the list, even when it's empty, it takes
// up space in the UI and looks like a horizontal line with padding around it.
// Using this custom render, the rounded borders and padding can be applied to the first row
// so that they are only visible when there are results.
const rowStyles = [styles.pv4, styles.ph3, styles.borderLeft, styles.borderRight, styles.borderBottom];

if (index === 0) {
rowStyles.push(styles.borderTop);
rowStyles.push(styles.mt2);
}
return (
<View style={rowStyles}>
<Text>{data.description}</Text>
</View>
);
}}
/>
);
};

AddressSearch.propTypes = propTypes;
AddressSearch.defaultProps = defaultProps;

export default withLocalize(AddressSearch);
52 changes: 5 additions & 47 deletions src/pages/ReimbursementAccount/CompanyStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import TextLink from '../../components/TextLink';
import StatePicker from '../../components/StatePicker';
import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize';
import {
isValidAddress, isValidDate, isValidZipCode, isRequiredFulfilled, isValidPhoneWithSpecialChars, isValidURL,
isValidDate, isRequiredFulfilled, isValidURL, isValidPhoneWithSpecialChars,
} from '../../libs/ValidationUtils';
import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import ExpensiPicker from '../../components/ExpensiPicker';
import * as ReimbursementAccountUtils from '../../libs/ReimbursementAccountUtils';
import reimbursementAccountPropTypes from './reimbursementAccountPropTypes';
import ReimbursementAccountForm from './ReimbursementAccountForm';
import AddressSearch from '../../components/AddressSearch';

const propTypes = {
/** Bank account currently in setup */
Expand Down Expand Up @@ -69,10 +70,6 @@ class CompanyStep extends React.Component {
// These fields need to be filled out in order to submit the form
this.requiredFields = [
'companyName',
'addressStreet',
'addressCity',
'addressState',
'addressZipCode',
'website',
'companyTaxID',
'incorporationDate',
Expand All @@ -84,9 +81,6 @@ class CompanyStep extends React.Component {

// Map a field to the key of the error's translation
this.errorTranslationKeys = {
addressStreet: 'bankAccount.error.addressStreet',
addressCity: 'bankAccount.error.addressCity',
addressZipCode: 'bankAccount.error.zipCode',
companyName: 'bankAccount.error.companyName',
companyPhone: 'bankAccount.error.phoneNumber',
website: 'bankAccount.error.website',
Expand Down Expand Up @@ -125,13 +119,6 @@ class CompanyStep extends React.Component {
*/
validate() {
const errors = {};
if (!isValidAddress(this.state.addressStreet)) {
errors.addressStreet = true;
}

if (!isValidZipCode(this.state.addressZipCode)) {
errors.addressZipCode = true;
}

if (!isValidURL(this.state.website)) {
errors.website = true;
Expand Down Expand Up @@ -193,40 +180,11 @@ class CompanyStep extends React.Component {
disabled={shouldDisableCompanyName}
errorText={this.getErrorText('companyName')}
/>
<ExpensiTextInput
<AddressSearch
label={this.props.translate('common.companyAddress')}
containerStyles={[styles.mt4]}
onChangeText={value => this.clearErrorAndSetValue('addressStreet', value)}
value={this.state.addressStreet}
errorText={this.getErrorText('addressStreet')}
/>
<Text style={[styles.mutedTextLabel, styles.mt1]}>{this.props.translate('common.noPO')}</Text>
<View style={[styles.flexRow, styles.mt4]}>
<View style={[styles.flex2, styles.mr2]}>
<ExpensiTextInput
label={this.props.translate('common.city')}
onChangeText={value => this.clearErrorAndSetValue('addressCity', value)}
value={this.state.addressCity}
errorText={this.getErrorText('addressCity')}
translateX={-14}
/>
</View>
<View style={[styles.flex1]}>
<StatePicker
onChange={value => this.clearErrorAndSetValue('addressState', value)}
value={this.state.addressState}
hasError={this.getErrors().addressState}
/>
</View>
</View>
<ExpensiTextInput
label={this.props.translate('common.zip')}
containerStyles={[styles.mt4]}
keyboardType={CONST.KEYBOARD_TYPE.NUMERIC}
onChangeText={value => this.clearErrorAndSetValue('addressZipCode', value)}
value={this.state.addressZipCode}
errorText={this.getErrorText('addressZipCode')}
maxLength={CONST.BANK_ACCOUNT.MAX_LENGTH.ZIP_CODE}
value={`${this.state.addressStreet} ${this.state.addressCity} ${this.state.addressState} ${this.state.addressZipCode}`}
onChangeText={(fieldName, value) => this.clearErrorAndSetValue(fieldName, value)}
/>
<ExpensiTextInput
label={this.props.translate('common.phoneNumber')}
Expand Down
28 changes: 28 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,25 @@ const styles = {
flex: 1,
},

borderTop: {
borderTopWidth: 1,
borderColor: themeColors.border,
},

borderTopRounded: {
borderTopWidth: 1,
borderColor: themeColors.border,
borderTopLeftRadius: variables.componentBorderRadiusNormal,
borderTopRightRadius: variables.componentBorderRadiusNormal,
},

borderBottomRounded: {
borderBottomWidth: 1,
borderColor: themeColors.border,
borderBottomLeftRadius: variables.componentBorderRadiusNormal,
borderBottomRightRadius: variables.componentBorderRadiusNormal,
},

borderBottom: {
borderBottomWidth: 1,
borderColor: themeColors.border,
Expand All @@ -1390,6 +1409,11 @@ const styles = {
borderColor: themeColors.border,
},

borderLeft: {
borderLeftWidth: 1,
borderColor: themeColors.border,
},

headerBar: {
overflow: 'hidden',
justifyContent: 'center',
Expand Down Expand Up @@ -2030,6 +2054,10 @@ const styles = {
{translateY},
],
}),

googleSearchTextInputContainer: {
flexDirection: 'column',
},
};

const baseCodeTagStyles = {
Expand Down