Skip to content

Commit

Permalink
Merge pull request Expensify#7807 from LucioChavezFuentes/7535_refact…
Browse files Browse the repository at this point in the history
…or-picker-compatible-form

7535 refactor picker compatible form
  • Loading branch information
stitesExpensify authored Apr 7, 2022
2 parents 3203b95 + bcc4c66 commit afd2201
Show file tree
Hide file tree
Showing 18 changed files with 259 additions and 45 deletions.
1 change: 1 addition & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
addons: [
'@storybook/addon-essentials',
'@storybook/addon-a11y',
'@storybook/addon-react-native-web',
],
staticDirs: [
'./public',
Expand Down
12 changes: 9 additions & 3 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"@react-native-firebase/crashlytics": "^12.3.0",
"@react-native-firebase/perf": "^12.3.0",
"@react-native-masked-view/masked-view": "^0.2.4",
"@react-native-picker/picker": "^1.9.11",
"@react-native-picker/picker": "^2.3.1",
"@react-navigation/compat": "^5.3.20",
"@react-navigation/drawer": "6.1.8",
"@react-navigation/native": "6.0.8",
Expand Down Expand Up @@ -126,6 +126,7 @@
"@react-native-community/eslint-config": "^2.0.0",
"@storybook/addon-a11y": "^6.4.12",
"@storybook/addon-essentials": "^6.4.12",
"@storybook/addon-react-native-web": "0.0.18",
"@storybook/addons": "^6.4.12",
"@storybook/react": "^6.4.12",
"@storybook/theming": "^6.4.12",
Expand Down
2 changes: 1 addition & 1 deletion src/components/AddPlaidBankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class AddPlaidBankAccount extends React.Component {
<View style={[styles.mb5]}>
<Picker
label={this.props.translate('addPersonalBankAccountPage.chooseAccountLabel')}
onChange={(index) => {
onInputChange={(index) => {
this.setState({selectedIndex: Number(index)});
this.clearError('selectedBank');
}}
Expand Down
2 changes: 1 addition & 1 deletion src/components/LocalePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const LocalePicker = (props) => {
return (
<Picker
label={props.size === 'normal' ? props.translate('preferencesPage.language') : null}
onChange={(locale) => {
onInputChange={(locale) => {
if (locale === props.preferredLocale) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Picker/BasePicker/basePickerPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as Expensicons from '../../Icon/Expensicons';

const propTypes = {
/** A callback method that is called when the value changes and it received the selected value as an argument */
onChange: PropTypes.func.isRequired,
onInputChange: PropTypes.func.isRequired,

/** Whether or not to show the disabled styles */
disabled: PropTypes.bool,
Expand Down
61 changes: 41 additions & 20 deletions src/components/Picker/BasePicker/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,53 @@
import React from 'react';
import RNPickerSelect from 'react-native-picker-select';
import _ from 'underscore';

import styles from '../../../styles/styles';
import * as basePickerPropTypes from './basePickerPropTypes';
import basePickerStyles from './basePickerStyles';

const BasePicker = props => (
<RNPickerSelect
onValueChange={props.onChange}
items={props.items}
style={props.size === 'normal' ? basePickerStyles(props.disabled, props.hasError, props.focused) : styles.pickerSmall}
useNativeAndroidPickerStyle={false}
placeholder={props.placeholder}
value={props.value}
Icon={() => props.icon(props.size)}
disabled={props.disabled}
fixAndroidTouchableBug
onOpen={props.onOpen}
onClose={props.onClose}
pickerProps={{
onFocus: props.onOpen,
onBlur: props.onClose,
}}
/>
);
class BasePicker extends React.Component {
constructor(props) {
super(props);

this.state = {
selectedValue: this.props.value || this.props.defaultValue,
};

this.updateSelectedValueAndExecuteOnChange = this.updateSelectedValueAndExecuteOnChange.bind(this);
}

updateSelectedValueAndExecuteOnChange(value) {
this.props.onInputChange(value);
this.setState({selectedValue: value});
}

render() {
const hasError = !_.isEmpty(this.props.errorText);
return (
<RNPickerSelect
onValueChange={this.updateSelectedValueAndExecuteOnChange}
items={this.props.items}
style={this.props.size === 'normal' ? basePickerStyles(this.props.disabled, hasError, this.props.focused) : styles.pickerSmall}
useNativeAndroidPickerStyle={false}
placeholder={this.props.placeholder}
value={this.state.selectedValue}
Icon={() => this.props.icon(this.props.size)}
disabled={this.props.disabled}
fixAndroidTouchableBug
onOpen={this.props.onOpen}
onClose={this.props.onClose}
pickerProps={{
onFocus: this.props.onOpen,
onBlur: this.props.onBlur,
ref: this.props.innerRef,
}}
/>
);
}
}

BasePicker.propTypes = basePickerPropTypes.propTypes;
BasePicker.defaultProps = basePickerPropTypes.defaultProps;
BasePicker.displayName = 'BasePicker';

export default BasePicker;
33 changes: 27 additions & 6 deletions src/components/Picker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import BasePicker from './BasePicker';
import Text from '../Text';
import styles from '../../styles/styles';
import InlineErrorText from '../InlineErrorText';
import * as FormUtils from '../../libs/FormUtils';

const propTypes = {
/** Picker label */
Expand All @@ -14,22 +15,39 @@ const propTypes = {
/** Should the picker appear disabled? */
isDisabled: PropTypes.bool,

/** Should the input be styled for errors */
hasError: PropTypes.bool,
/** Input value */
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

/** Error text to display */
errorText: PropTypes.string,

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

/** Indicates that the input is being used with the Form component */
isFormInput: PropTypes.bool,

/**
* The ID used to uniquely identify the input
*
* @param {Object} props - props passed to the input
* @returns {Object} - returns an Error object if isFormInput is supplied but inputID is falsey or not a string
*/
inputID: props => FormUtils.validateInputIDProps(props),

/** Saves a draft of the input value when used in a form */
shouldSaveDraft: PropTypes.bool,
};

const defaultProps = {
label: '',
isDisabled: false,
hasError: false,
errorText: '',
containerStyles: [],
isFormInput: false,
inputID: undefined,
shouldSaveDraft: false,
value: undefined,
};

class Picker extends PureComponent {
Expand All @@ -48,6 +66,7 @@ class Picker extends PureComponent {
style={[
styles.pickerContainer,
this.props.isDisabled && styles.inputDisabled,
...this.props.containerStyles,
]}
>
{this.props.label && (
Expand All @@ -58,12 +77,13 @@ class Picker extends PureComponent {
onClose={() => this.setState({isOpen: false})}
disabled={this.props.isDisabled}
focused={this.state.isOpen}
hasError={this.props.hasError}
errorText={this.props.errorText}
value={this.props.value}
// eslint-disable-next-line react/jsx-props-no-spreading
{...pickerProps}
/>
</View>
<InlineErrorText>
<InlineErrorText styles={[styles.mh3]}>
{this.props.errorText}
</InlineErrorText>
</>
Expand All @@ -74,4 +94,5 @@ class Picker extends PureComponent {
Picker.propTypes = propTypes;
Picker.defaultProps = defaultProps;

export default Picker;
// eslint-disable-next-line react/jsx-props-no-spreading
export default React.forwardRef((props, ref) => <Picker {...props} innerRef={ref} key={props.inputID} />);
2 changes: 1 addition & 1 deletion src/components/StatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const StatePicker = props => (
<Picker
placeholder={{value: '', label: '-'}}
items={STATES}
onChange={props.onChange}
onInputChange={props.onChange}
value={props.value}
label={props.label || props.translate('common.state')}
hasError={props.hasError}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ReimbursementAccount/CompanyStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class CompanyStep extends React.Component {
<Picker
label={this.props.translate('companyStep.companyType')}
items={_.map(this.props.translate('companyStep.incorporationTypes'), (label, value) => ({value, label}))}
onChange={value => this.clearErrorAndSetValue('incorporationType', value)}
onInputChange={value => this.clearErrorAndSetValue('incorporationType', value)}
value={this.state.incorporationType}
placeholder={{value: '', label: '-'}}
hasError={this.getErrors().incorporationType}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ReportSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class ReportSettingsPage extends Component {
<View style={[styles.mt2]}>
<Picker
label={this.props.translate('notificationPreferences.label')}
onChange={(notificationPreference) => {
onInputChange={(notificationPreference) => {
Report.updateNotificationPreference(
this.props.report.reportID,
notificationPreference,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/settings/PreferencesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const PreferencesPage = (props) => {
<View style={[styles.mb2, styles.w100]}>
<Picker
label={props.translate('preferencesPage.priorityMode')}
onChange={
onInputChange={
mode => NameValuePair.set(CONST.NVP.PRIORITY_MODE, mode, ONYXKEYS.NVP_PRIORITY_MODE)
}
items={_.values(priorityModes)}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/settings/Profile/ProfilePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ class ProfilePage extends Component {
<View style={styles.mb6}>
<Picker
label={this.props.translate('profilePage.preferredPronouns')}
onChange={(pronouns) => {
onInputChange={(pronouns) => {
const hasSelfSelectedPronouns = pronouns === CONST.PRONOUNS.SELF_SELECT;
this.setState({
pronouns: hasSelfSelectedPronouns ? '' : pronouns,
Expand Down Expand Up @@ -288,7 +288,7 @@ class ProfilePage extends Component {
<View style={styles.mb3}>
<Picker
label={this.props.translate('profilePage.timezone')}
onChange={selectedTimezone => this.setState({selectedTimezone})}
onInputChange={selectedTimezone => this.setState({selectedTimezone})}
items={timezones}
isDisabled={this.state.isAutomaticTimezone}
value={this.state.selectedTimezone}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/workspace/WorkspaceNewRoomPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,15 @@ class WorkspaceNewRoomPage extends React.Component {
items={this.state.workspaceOptions}
errorText={this.state.errors.policyID}
hasError={Boolean(this.state.errors.policyID)}
onChange={policyID => this.clearErrorAndSetValue('policyID', policyID)}
onInputChange={policyID => this.clearErrorAndSetValue('policyID', policyID)}
/>
</View>
<View style={styles.mb2}>
<Picker
value={this.state.visibility}
label={this.props.translate('newRoomPage.visibility')}
items={visibilityOptions}
onChange={visibility => this.setState({visibility})}
onInputChange={visibility => this.setState({visibility})}
/>
</View>
<Text style={[styles.textLabel, styles.colorMuted]}>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/WorkspaceSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class WorkspaceSettingsPage extends React.Component {
<View style={[styles.mt4]}>
<Picker
label={this.props.translate('workspace.editor.currencyInputLabel')}
onChange={currency => this.setState({currency})}
onInputChange={currency => this.setState({currency})}
items={this.getCurrencyItems()}
value={this.state.currency}
isDisabled={hasVBA}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class WorkspaceReimburseNoVBAView extends React.Component {
label={this.props.translate('workspace.reimburse.trackDistanceUnit')}
items={this.unitItems}
value={this.state.unitValue}
onChange={value => this.setUnit(value)}
onInputChange={value => this.setUnit(value)}
/>
</View>
</View>
Expand Down
Loading

0 comments on commit afd2201

Please sign in to comment.