From 2231d55a6ddf189042c680979e4981c6c00d178b Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Thu, 4 Nov 2021 20:21:20 -0700 Subject: [PATCH 1/7] Implement react/destructuring-assignment eslint rule --- .eslintrc.js | 1 + .../BaseAnchorForCommentsOnly/index.js | 31 +++--- .../BaseAnchorForCommentsOnly/index.native.js | 25 ++--- src/components/AvatarWithImagePicker.js | 1 + src/components/BigNumberPad.js | 4 +- src/components/Button.js | 1 + src/components/Checkbox.js | 17 ++-- src/components/CheckboxWithLabel.js | 28 +++--- src/components/DatePicker/index.android.js | 29 ++---- src/components/DatePicker/index.ios.js | 28 ++---- src/components/DatePicker/index.js | 27 ++---- src/components/DisplayNames/index.native.js | 10 +- src/components/ExpensiPicker.js | 12 +-- .../ExpensiTextInput/BaseExpensiTextInput.js | 44 +++------ .../ExpensiTextInputLabel/index.js | 15 +-- .../ExpensiTextInputLabel/index.native.js | 15 +-- src/components/FAB/index.ios.js | 4 +- src/components/FormAlertWithSubmitButton.js | 41 +++----- src/components/FullNameInputRow.js | 30 +++--- src/components/FullscreenLoadingIndicator.js | 7 +- .../GrowlNotificationContainer/index.js | 8 +- .../index.native.js | 6 +- .../BaseHTMLEngineProvider.js | 42 ++++---- src/components/HTMLEngineProvider/index.js | 8 +- .../HTMLEngineProvider/index.native.js | 6 +- src/components/InlineCodeBlock/index.js | 31 +++--- .../InlineCodeBlock/index.native.js | 39 ++++---- .../KeyboardAvoidingView/index.ios.js | 21 ++-- src/components/KeyboardAvoidingView/index.js | 3 +- src/components/LocalePicker.js | 15 ++- src/components/MenuItem.js | 71 +++++--------- src/components/MenuItemList.js | 4 +- src/components/MultipleAvatars.js | 34 +++---- src/components/PDFView/index.js | 3 +- src/components/Picker/index.js | 34 +++---- .../ReimbursementAccountLoadingIndicator.js | 8 +- src/components/RenderHTML.js | 14 +-- src/components/ReportActionItem/IOUAction.js | 20 ++-- src/components/ReportActionItem/IOUPreview.js | 52 ++++------ src/components/ReportActionItem/IOUQuote.js | 17 ++-- src/components/SVGImage/index.js | 6 +- src/components/SVGImage/index.native.js | 8 +- src/components/Switch.js | 6 +- .../TextInputFocusable/index.ios.js | 4 +- src/components/UnorderedList.js | 4 +- src/components/withWindowDimensions.js | 1 + .../createCustomModalStackNavigator.js | 1 + src/pages/DetailsPage.js | 22 ++--- src/pages/NotFound.js | 6 +- src/pages/ReimbursementAccount/EnableStep.js | 21 ++-- .../ReimbursementAccount/IdentityForm.js | 68 ++++++------- src/pages/ReportParticipantsPage.js | 15 +-- src/pages/home/report/ReportActionCompose.js | 16 +-- .../home/report/ReportActionItemFragment.js | 33 +++---- .../home/report/ReportActionItemGrouped.js | 4 +- .../home/report/ReportActionItemMessage.js | 12 +-- .../home/report/ReportActionItemSingle.js | 36 +++---- src/pages/home/sidebar/OptionRow.js | 97 ++++++++----------- src/pages/settings/AboutPage.js | 16 +-- src/pages/settings/AppDownloadLinks.js | 6 +- src/pages/settings/InitialPage.js | 41 +++----- src/pages/settings/PreferencesPage.js | 30 +++--- src/pages/settings/Profile/ProfilePage.js | 37 +++---- src/pages/signin/ChangeExpensifyLoginLink.js | 12 +-- src/pages/signin/TermsAndLicenses.js | 16 +-- src/pages/workspace/WorkspaceInitialPage.js | 54 +++++------ src/pages/workspace/WorkspaceSection.js | 44 ++++----- src/pages/workspace/WorkspaceSettingsPage.js | 8 +- .../bills/WorkspaceBillsFirstSection.js | 21 ++-- .../bills/WorkspaceBillsNoVBAView.js | 12 +-- .../workspace/bills/WorkspaceBillsPage.js | 6 +- .../workspace/bills/WorkspaceBillsVBAView.js | 12 +-- .../workspace/card/WorkspaceCardNoVBAView.js | 18 ++-- src/pages/workspace/card/WorkspaceCardPage.js | 6 +- .../card/WorkspaceCardVBANoECardView.js | 18 ++-- .../card/WorkspaceCardVBAWithECardView.js | 18 ++-- .../invoices/WorkspaceInvoicesFirstSection.js | 12 +-- .../invoices/WorkspaceInvoicesNoVBAView.js | 12 +-- .../invoices/WorkspaceInvoicesPage.js | 6 +- .../invoices/WorkspaceInvoicesVBAView.js | 12 +-- .../reimburse/WorkspaceReimburseNoVBAView.js | 20 ++-- .../reimburse/WorkspaceReimbursePage.js | 6 +- .../reimburse/WorkspaceReimburseVBAView.js | 20 ++-- .../travel/WorkspaceTravelNoVBAView.js | 10 +- .../workspace/travel/WorkspaceTravelPage.js | 6 +- .../travel/WorkspaceTravelVBAView.js | 12 +-- 86 files changed, 714 insertions(+), 943 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 3f0bce754e16..ceab0235224b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,6 +4,7 @@ module.exports = { ignorePatterns: ['!.*', 'src/vendor', '.github/actions/**/index.js'], rules: { 'react/jsx-filename-extension': [1, {extensions: ['.js']}], + 'react/destructuring-assignment': ['error', 'never'], 'comma-dangle': ['error', 'always-multiline'], }, plugins: ['detox'], diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js index 9291774806c2..96978acb6964 100644 --- a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js +++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.js @@ -13,27 +13,19 @@ import fileDownload from '../../../libs/fileDownload'; /* * This is a default anchor component for regular links. */ -const BaseAnchorForCommentsOnly = ({ - href, - rel, - target, - children, - style, - fileName, - ...props -}) => { +const BaseAnchorForCommentsOnly = (props) => { let linkRef; return ( props.isAttachment ? ( { - fileDownload(href, fileName); + fileDownload(props.href, props.fileName); }} > @@ -45,7 +37,7 @@ const BaseAnchorForCommentsOnly = ({ showContextMenu( CONTEXT_MENU_TYPES.LINK, event, - href, + props.href, lodashGet(linkRef, 'current'), ); } @@ -53,14 +45,17 @@ const BaseAnchorForCommentsOnly = ({ > linkRef = el} - style={StyleSheet.flatten(style)} + style={StyleSheet.flatten(props.style)} accessibilityRole="link" - href={href} - hrefAttrs={{rel, target}} - // eslint-disable-next-line react/jsx-props-no-spreading + href={props.href} + hrefAttrs={{ + rel: props.rel, + target: props.target, + }} + // eslint-disable-next-line react/jsx-props-no-spreading {...props} > - {children} + {props.children} ) diff --git a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js index 0890ba75b23c..bc88cef6880e 100644 --- a/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js +++ b/src/components/AnchorForCommentsOnly/BaseAnchorForCommentsOnly/index.native.js @@ -13,27 +13,20 @@ import styles from '../../../styles/styles'; /* * This is a default anchor component for regular links. */ -const BaseAnchorForCommentsOnly = ({ - href, - children, - style, - isAttachment, - fileName, - ...props -}) => { +const BaseAnchorForCommentsOnly = (props) => { let linkRef; return ( - isAttachment + props.isAttachment ? ( { - fileDownload(href, fileName); + fileDownload(props.href, props.fileName); }} > @@ -45,20 +38,20 @@ const BaseAnchorForCommentsOnly = ({ showContextMenu( CONTEXT_MENU_TYPES.LINK, event, - href, + props.href, lodashGet(linkRef, 'current'), ); } } - onPress={() => Linking.openURL(href)} + onPress={() => Linking.openURL(props.href)} > linkRef = el} - style={StyleSheet.flatten(style)} + style={StyleSheet.flatten(props.style)} // eslint-disable-next-line react/jsx-props-no-spreading {...props} > - {children} + {props.children} ) diff --git a/src/components/AvatarWithImagePicker.js b/src/components/AvatarWithImagePicker.js index 9bbdd7064e3e..f33b456d7cc7 100644 --- a/src/components/AvatarWithImagePicker.js +++ b/src/components/AvatarWithImagePicker.js @@ -155,6 +155,7 @@ class AvatarWithImagePicker extends React.Component { } render() { + // eslint-disable-next-line react/destructuring-assignment const {DefaultAvatar} = this.props; const additionalStyles = _.isArray(this.props.style) ? this.props.style : [this.props.style]; diff --git a/src/components/BigNumberPad.js b/src/components/BigNumberPad.js index b373f07a36d9..26299c661b8f 100644 --- a/src/components/BigNumberPad.js +++ b/src/components/BigNumberPad.js @@ -17,7 +17,7 @@ const padNumbers = [ ['.', '0', '<'], ]; -const BigNumberPad = ({numberPressed}) => ( +const BigNumberPad = props => ( {_.map(padNumbers, (row, rowIndex) => ( @@ -30,7 +30,7 @@ const BigNumberPad = ({numberPressed}) => ( key={column} style={[styles.flex1, marginLeft]} text={column} - onPress={() => numberPressed(column)} + onPress={() => props.numberPressed(column)} /> ); })} diff --git a/src/components/Button.js b/src/components/Button.js index 3038e0c64f21..3152fe35852a 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -107,6 +107,7 @@ class Button extends Component { } renderContent() { + // eslint-disable-next-line react/destructuring-assignment const {ContentComponent} = this.props; if (ContentComponent) { return ; diff --git a/src/components/Checkbox.js b/src/components/Checkbox.js index afd939fa9a59..970363b193ee 100644 --- a/src/components/Checkbox.js +++ b/src/components/Checkbox.js @@ -24,22 +24,17 @@ const defaultProps = { disabled: false, }; -const Checkbox = ({ - isChecked, - onPress, - hasError, - disabled, -}) => ( +const Checkbox = props => ( onPress(!isChecked)} + disabled={props.disabled} + onPress={() => props.onPress(!props.isChecked)} > diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index db2d23ccb263..004f9f1fbe9f 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -38,26 +38,26 @@ const defaultProps = { errorText: '', }; -const CheckboxWithLabel = ({ - LabelComponent, isChecked, onPress, style, label, hasError, errorText, -}) => { +const CheckboxWithLabel = (props) => { + // eslint-disable-next-line react/destructuring-assignment + const {LabelComponent} = props; const defaultStyles = [styles.flexRow, styles.alignItemsCenter]; - const wrapperStyles = _.isArray(style) ? [...defaultStyles, ...style] : [...defaultStyles, style]; + const wrapperStyles = _.isArray(props.style) ? [...defaultStyles, ...props.style] : [...defaultStyles, props.style]; - if (!label && !LabelComponent) { + if (!props.label && !LabelComponent) { throw new Error('Must provide at least label or LabelComponent prop'); } return ( <> onPress(!isChecked)} - label={label} - hasError={hasError} + isChecked={props.isChecked} + onPress={() => props.onPress(!props.isChecked)} + label={props.label} + hasError={props.hasError} /> onPress(!isChecked)} + onPress={() => props.onPress(!props.isChecked)} style={[ styles.ml3, styles.pr2, @@ -68,17 +68,17 @@ const CheckboxWithLabel = ({ styles.alignItemsCenter, ]} > - {label && ( + {props.label && ( - {label} + {props.label} )} {LabelComponent && ()} - {!_.isEmpty(errorText) && ( + {!_.isEmpty(props.errorText) && ( - {errorText} + {props.errorText} )} diff --git a/src/components/DatePicker/index.android.js b/src/components/DatePicker/index.android.js index cbc726688d1b..d4b2bfa4cf50 100644 --- a/src/components/DatePicker/index.android.js +++ b/src/components/DatePicker/index.android.js @@ -38,36 +38,25 @@ class DatePicker extends React.Component { } render() { - const { - value, - label, - placeholder, - hasError, - errorText, - translateX, - containerStyles, - disabled, - } = this.props; - - const dateAsText = value ? moment(value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; + const dateAsText = this.props.value ? moment(this.props.value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; return ( <> {this.state.isPickerVisible && ( diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index 127523b267cc..b29ae3b02bc9 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -64,32 +64,20 @@ class Datepicker extends React.Component { } render() { - const { - value, - label, - placeholder, - hasError, - errorText, - translateX, - containerStyles, - disabled, - } = this.props; - - const dateAsText = value ? moment(value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; - + const dateAsText = this.props.value ? moment(this.props.value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; return ( <> this.inputRef = input} onFocus={this.showDatepicker} - label={label} + label={this.props.label} onChangeText={this.raiseDateChange} defaultValue={this.defaultValue} - placeholder={placeholder} - hasError={hasError} - errorText={errorText} - containerStyles={containerStyles} - translateX={translateX} - disabled={disabled} + placeholder={this.props.placeholder} + hasError={this.props.hasError} + errorText={this.props.errorText} + containerStyles={this.props.containerStyles} + translateX={this.props.translateX} + disabled={this.props.disabled} /> ); } diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index a3862e5eb147..6ccb53888fd6 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -3,13 +3,9 @@ import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. -const DisplayNames = ({ - fullTitle, - numberOfLines, - textStyles, -}) => ( - - {fullTitle} +const DisplayNames = props => ( + + {props.fullTitle} ); diff --git a/src/components/ExpensiPicker.js b/src/components/ExpensiPicker.js index bbb9db50b336..3f67d0352415 100644 --- a/src/components/ExpensiPicker.js +++ b/src/components/ExpensiPicker.js @@ -36,26 +36,24 @@ class ExpensiPicker extends PureComponent { } render() { - const { - label, isDisabled, ...pickerProps - } = this.props; + const pickerProps = _.omit(this.props, _.keys(propTypes)); return ( <> - {label && ( - {label} + {this.props.label && ( + {this.props.label} )} this.setState({isOpen: true})} onClose={() => this.setState({isOpen: false})} - disabled={isDisabled} + disabled={this.props.isDisabled} // eslint-disable-next-line react/jsx-props-no-spreading {...pickerProps} /> diff --git a/src/components/ExpensiTextInput/BaseExpensiTextInput.js b/src/components/ExpensiTextInput/BaseExpensiTextInput.js index 31f94248d571..11ff5c95a87c 100644 --- a/src/components/ExpensiTextInput/BaseExpensiTextInput.js +++ b/src/components/ExpensiTextInput/BaseExpensiTextInput.js @@ -138,28 +138,14 @@ class BaseExpensiTextInput extends Component { } render() { - const { - label, - value, - placeholder, - errorText, - hasError, - containerStyles, - inputStyle, - ignoreLabelTranslateX, - innerRef, - autoFocus, - multiline, - ...inputProps - } = this.props; - - const hasLabel = Boolean(label.length); + const inputProps = _.omit(this.props, _.keys(propTypes)); + const hasLabel = Boolean(this.props.label.length); return ( @@ -167,18 +153,18 @@ class BaseExpensiTextInput extends Component { style={[ styles.expensiTextInputContainer, this.state.isFocused && styles.borderColorFocus, - (hasError || errorText) && styles.borderColorDanger, + (this.props.hasError || this.props.errorText) && styles.borderColorDanger, ]} > {hasLabel ? ( <> {/* Adding this background to the label only for multiline text input, to prevent text overlaping with label when scrolling */} - {multiline && } + {this.props.multiline && } { - if (typeof innerRef === 'function') { innerRef(ref); } + if (typeof this.props.innerRef === 'function') { this.props.innerRef(ref); } this.input = ref; }} // eslint-disable-next-line {...inputProps} - value={value} - placeholder={(this.state.isFocused || !label) ? placeholder : null} + value={this.props.value} + placeholder={(this.state.isFocused || !this.props.label) ? this.props.placeholder : null} placeholderTextColor={themeColors.placeholderText} underlineColorAndroid="transparent" - style={[inputStyle, !hasLabel && styles.pv0]} - multiline={multiline} + style={[this.props.inputStyle, !hasLabel && styles.pv0]} + multiline={this.props.multiline} onFocus={this.onFocus} onBlur={this.onBlur} onChangeText={this.setValue} @@ -208,9 +194,9 @@ class BaseExpensiTextInput extends Component { - {!_.isEmpty(errorText) && ( + {!_.isEmpty(this.props.errorText) && ( - {errorText} + {this.props.errorText} )} diff --git a/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js index 88d5194a5f29..5293e90f7960 100644 --- a/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js +++ b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.js @@ -3,25 +3,20 @@ import {Animated} from 'react-native'; import styles from '../../../styles/styles'; import propTypes from './expensiTextInputLabelPropTypes'; -const ExpensiTextInputLabel = ({ - label, - labelTranslateY, - labelTranslateX, - labelScale, -}) => ( +const ExpensiTextInputLabel = props => ( - {label} + {props.label} ); diff --git a/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js index 082333f55254..8869584504e2 100644 --- a/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js +++ b/src/components/ExpensiTextInput/ExpensiTextInputLabel/index.native.js @@ -3,23 +3,18 @@ import {Animated} from 'react-native'; import styles from '../../../styles/styles'; import propTypes from './expensiTextInputLabelPropTypes'; -const ExpensiTextInputLabel = ({ - label, - labelTranslateX, - labelTranslateY, - labelScale, -}) => ( +const ExpensiTextInputLabel = props => ( - {label} + {props.label} ); diff --git a/src/components/FAB/index.ios.js b/src/components/FAB/index.ios.js index 863ac8f9fac0..3ebc24dcd62d 100644 --- a/src/components/FAB/index.ios.js +++ b/src/components/FAB/index.ios.js @@ -7,9 +7,9 @@ import FAB from './FAB'; import fabPropTypes from './fabPropTypes'; // KeyboardAvoidingView only need in IOS so that's the reason make platform specific FAB component. -const Fab = ({onPress, isActive}) => ( +const Fab = props => ( - + ); diff --git a/src/components/FormAlertWithSubmitButton.js b/src/components/FormAlertWithSubmitButton.js index 077814e11f2c..d7db46120845 100644 --- a/src/components/FormAlertWithSubmitButton.js +++ b/src/components/FormAlertWithSubmitButton.js @@ -51,48 +51,37 @@ const defaultProps = { isLoading: false, }; -const FormAlertWithSubmitButton = ({ - isAlertVisible, - isDisabled, - onSubmit, - buttonText, - translate, - onFixTheErrorsLinkPressed, - message, - isMessageHtml, - containerStyles, - isLoading, -}) => { +const FormAlertWithSubmitButton = (props) => { /** * @returns {React.Component} */ function getAlertPrompt() { let error = ''; - if (!_.isEmpty(message)) { - if (isMessageHtml) { + if (!_.isEmpty(props.message)) { + if (props.isMessageHtml) { error = ( - ${message}`} /> + ${props.message}`} /> ); } else { error = ( - {message} + {props.message} ); } } else { error = ( <> - {`${translate('common.please')} `} + {`${props.translate('common.please')} `} - {translate('common.fixTheErrors')} + {props.translate('common.fixTheErrors')} - {` ${translate('common.inTheFormBeforeContinuing')}.`} + {` ${props.translate('common.inTheFormBeforeContinuing')}.`} ); @@ -106,8 +95,8 @@ const FormAlertWithSubmitButton = ({ } return ( - - {isAlertVisible && ( + + {props.isAlertVisible && ( {getAlertPrompt()} @@ -116,10 +105,10 @@ const FormAlertWithSubmitButton = ({