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

fix/28334: Missing displayName prop leads to console error (testID undefined) #28813

Merged
merged 3 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ module.exports = {
},
],
curly: 'error',
'react/display-name': 'error',
},
},
{
Expand Down
7 changes: 6 additions & 1 deletion __mocks__/react-native-safe-area-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ function withSafeAreaInsets(WrappedComponent) {
/>
);
}
return forwardRef((props, ref) => (

const WithSafeAreaInsetsWithRef = forwardRef((props, ref) => (
<WithSafeAreaInsets
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

WithSafeAreaInsetsWithRef.displayName = 'WithSafeAreaInsetsWithRef';

return WithSafeAreaInsetsWithRef;
}

const SafeAreaView = View;
Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@
"electron-builder": "24.6.4",
"eslint": "^7.6.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-expensify": "^2.0.39",
"eslint-config-expensify": "^2.0.42",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsdoc": "^46.2.6",
Expand Down
23 changes: 11 additions & 12 deletions src/components/AddressSearch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,15 +499,14 @@ AddressSearch.propTypes = propTypes;
AddressSearch.defaultProps = defaultProps;
AddressSearch.displayName = 'AddressSearch';

export default compose(
withNetwork(),
withLocalize,
)(
React.forwardRef((props, ref) => (
<AddressSearch
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
innerRef={ref}
/>
)),
);
const AddressSearchWithRef = React.forwardRef((props, ref) => (
<AddressSearch
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
innerRef={ref}
/>
));

AddressSearchWithRef.displayName = 'AddressSearchWithRef';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I'm a bit confused here. We've already set the display name for the component here. Why do we need to set displayName for the ref as well?

@roryabraham Is it expected change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functions returned by React.forwardRef don't have an inherent name, therefore a displayName is required considering the new eslint rule added: "react/display-name": 'error', otherwise it would throw lint error.

As soon as this is sorted out, I have @Pujan92's requested modifications in a stash, ready to be pushed, and will also sort the existing conflict.

I didn't want to mess anything up by pushing to the PR before the initial review.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ikevin127 I would like to know @roryabraham's thought before proceeding.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the long delay in replies here, I think it's fine that we set a displayName on the wrapper. This looks a bit weird but makes sense when you think about it. It will just show as AddressSearchWithRef in the React Dev Tools inspector instead of Anonymous.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative here might be to move the React.forwardRef to the initial function declaration instead of having a separate constant for it. Like this:

diff --git a/src/components/AddressSearch/index.js b/src/components/AddressSearch/index.js
index baa03ecc3a..d7d8cd5462 100644
--- a/src/components/AddressSearch/index.js
+++ b/src/components/AddressSearch/index.js
@@ -140,7 +140,7 @@ const defaultProps = {
 // Do not convert to class component! It's been tried before and presents more challenges than it's worth.
 // Relevant thread: https://expensify.slack.com/archives/C03TQ48KC/p1634088400387400
 // Reference: https://github.com/FaridSafi/react-native-google-places-autocomplete/issues/609#issuecomment-886133839
-function AddressSearch(props) {
+const AddressSearch = React.forwardRef((props, ref) => {
     const [displayListViewBorder, setDisplayListViewBorder] = useState(false);
     const [isTyping, setIsTyping] = useState(false);
     const [isFocused, setIsFocused] = useState(false);
@@ -401,19 +401,8 @@ function AddressSearch(props) {
                         }}
                         textInputProps={{
                             InputComp: TextInput,
-                            ref: (node) => {
-                                if (!props.innerRef) {
-                                    return;
-                                }
-
-                                if (_.isFunction(props.innerRef)) {
-                                    props.innerRef(node);
-                                    return;
-                                }
-
-                                // eslint-disable-next-line no-param-reassign
-                                props.innerRef.current = node;
-                            },
+                            // eslint-disable-next-line no-param-reassign
+                            ref: (node) => (ref.current = node),
                             label: props.label,
                             containerStyles: props.containerStyles,
                             errorText: props.errorText,
@@ -493,23 +482,10 @@ function AddressSearch(props) {
             {isFetchingCurrentLocation && <FullScreenLoadingIndicator />}
         </>
     );
-}
+});
 
 AddressSearch.propTypes = propTypes;
 AddressSearch.defaultProps = defaultProps;
 AddressSearch.displayName = 'AddressSearch';
 
-const AddressSearchWithRef = React.forwardRef((props, ref) => (
-    <AddressSearch
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...props}
-        innerRef={ref}
-    />
-));
-
-AddressSearchWithRef.displayName = 'AddressSearchWithRef';
-
-export default compose(
-    withNetwork(),
-    withLocalize,
-)(AddressSearchWithRef);
+export default compose(withNetwork(), withLocalize)(AddressSearch);

Maybe we should discuss in slack


export default compose(withNetwork(), withLocalize)(AddressSearchWithRef);
6 changes: 5 additions & 1 deletion src/components/AmountTextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ AmountTextInput.propTypes = propTypes;
AmountTextInput.defaultProps = defaultProps;
AmountTextInput.displayName = 'AmountTextInput';

export default React.forwardRef((props, ref) => (
const AmountTextInputWithRef = React.forwardRef((props, ref) => (
<AmountTextInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

AmountTextInputWithRef.displayName = 'AmountTextInputWithRef';

export default AmountTextInputWithRef;
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,14 @@ function AttachmentCarouselPager({
AttachmentCarouselPager.propTypes = pagerPropTypes;
AttachmentCarouselPager.defaultProps = pagerDefaultProps;

export default React.forwardRef((props, ref) => (
const AttachmentCarouselPagerWithRef = React.forwardRef((props, ref) => (
<AttachmentCarouselPager
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

AttachmentCarouselPagerWithRef.displayName = 'AttachmentCarouselPagerWithRef';

export default AttachmentCarouselPagerWithRef;
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,14 @@ function BaseAutoCompleteSuggestions(props) {
BaseAutoCompleteSuggestions.propTypes = propTypes;
BaseAutoCompleteSuggestions.displayName = 'BaseAutoCompleteSuggestions';

export default React.forwardRef((props, ref) => (
const BaseAutoCompleteSuggestionsWithRef = React.forwardRef((props, ref) => (
<BaseAutoCompleteSuggestions
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

BaseAutoCompleteSuggestionsWithRef.displayName = 'BaseAutoCompleteSuggestionsWithRef';

export default BaseAutoCompleteSuggestionsWithRef;
6 changes: 5 additions & 1 deletion src/components/BaseMiniContextMenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,14 @@ BaseMiniContextMenuItem.propTypes = propTypes;
BaseMiniContextMenuItem.defaultProps = defaultProps;
BaseMiniContextMenuItem.displayName = 'BaseMiniContextMenuItem';

export default React.forwardRef((props, ref) => (
const BaseMiniContextMenuItemWithRef = React.forwardRef((props, ref) => (
<BaseMiniContextMenuItem
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
innerRef={ref}
/>
));

BaseMiniContextMenuItemWithRef.displayName = 'BaseMiniContextMenuItemWithRef';

export default BaseMiniContextMenuItemWithRef;
6 changes: 5 additions & 1 deletion src/components/CheckboxWithLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,14 @@ CheckboxWithLabel.propTypes = propTypes;
CheckboxWithLabel.defaultProps = defaultProps;
CheckboxWithLabel.displayName = 'CheckboxWithLabel';

export default React.forwardRef((props, ref) => (
const CheckboxWithLabelWithRef = React.forwardRef((props, ref) => (
<CheckboxWithLabel
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

CheckboxWithLabelWithRef.displayName = 'CheckboxWithLabelWithRef';

export default CheckboxWithLabelWithRef;
6 changes: 5 additions & 1 deletion src/components/Composer/index.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,14 @@ function Composer({shouldClear, onClear, isDisabled, maxLines, forwardedRef, isC
Composer.propTypes = propTypes;
Composer.defaultProps = defaultProps;

export default React.forwardRef((props, ref) => (
const ComposerWithRef = React.forwardRef((props, ref) => (
<Composer
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

ComposerWithRef.displayName = 'ComposerWithRef';

export default ComposerWithRef;
6 changes: 5 additions & 1 deletion src/components/Composer/index.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,14 @@ function Composer({shouldClear, onClear, isDisabled, maxLines, forwardedRef, isC
Composer.propTypes = propTypes;
Composer.defaultProps = defaultProps;

export default React.forwardRef((props, ref) => (
const ComposerWithRef = React.forwardRef((props, ref) => (
<Composer
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

ComposerWithRef.displayName = 'ComposerWithRef';

export default ComposerWithRef;
24 changes: 11 additions & 13 deletions src/components/Composer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,16 +489,14 @@ function Composer({
Composer.propTypes = propTypes;
Composer.defaultProps = defaultProps;

export default compose(
withLocalize,
withWindowDimensions,
withNavigation,
)(
React.forwardRef((props, ref) => (
<Composer
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
const ComposerWithRef = React.forwardRef((props, ref) => (
<Composer
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

ComposerWithRef.displayName = 'ComposerWithRef';

export default compose(withLocalize, withWindowDimensions, withNavigation)(ComposerWithRef);
6 changes: 5 additions & 1 deletion src/components/ContextMenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,14 @@ ContextMenuItem.propTypes = propTypes;
ContextMenuItem.defaultProps = defaultProps;
ContextMenuItem.displayName = 'ContextMenuItem';

export default forwardRef((props, ref) => (
const ContextMenuItemWithRef = forwardRef((props, ref) => (
<ContextMenuItem
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
innerRef={ref}
/>
));

ContextMenuItemWithRef.displayName = 'ContextMenuItemWithRef';

export default ContextMenuItemWithRef;
6 changes: 5 additions & 1 deletion src/components/CountrySelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,14 @@ CountrySelector.propTypes = propTypes;
CountrySelector.defaultProps = defaultProps;
CountrySelector.displayName = 'CountrySelector';

export default React.forwardRef((props, ref) => (
const CountrySelectorWithRef = React.forwardRef((props, ref) => (
<CountrySelector
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

CountrySelectorWithRef.displayName = 'CountrySelectorWithRef';

export default CountrySelectorWithRef;
6 changes: 5 additions & 1 deletion src/components/DatePicker/index.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,14 @@ DatePicker.displayName = 'DatePicker';
* locale. Otherwise the spinner would be present in the system locale and it would be weird if it happens
* that the modal buttons are in one locale (app) while the (spinner) month names are another (system)
*/
export default React.forwardRef((props, ref) => (
const DatePickerWithRef = React.forwardRef((props, ref) => (
<DatePicker
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
innerRef={ref}
/>
));

DatePickerWithRef.displayName = 'DatePickerWithRef';

export default DatePickerWithRef;
6 changes: 5 additions & 1 deletion src/components/DatePicker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,14 @@ DatePicker.displayName = 'DatePicker';
DatePicker.propTypes = propTypes;
DatePicker.defaultProps = defaultProps;

export default React.forwardRef((props, ref) => (
const DatePickerWithRef = React.forwardRef((props, ref) => (
<DatePicker
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
innerRef={ref}
/>
));

DatePickerWithRef.displayName = 'DatePickerWithRef';

export default DatePickerWithRef;
21 changes: 12 additions & 9 deletions src/components/EmojiPicker/EmojiPickerButtonDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@ function EmojiPickerButtonDropdown(props) {
EmojiPickerButtonDropdown.propTypes = propTypes;
EmojiPickerButtonDropdown.defaultProps = defaultProps;
EmojiPickerButtonDropdown.displayName = 'EmojiPickerButtonDropdown';
export default withLocalize(
React.forwardRef((props, ref) => (
<EmojiPickerButtonDropdown
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);

const EmojiPickerButtonDropdownWithRef = React.forwardRef((props, ref) => (
<EmojiPickerButtonDropdown
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

EmojiPickerButtonDropdownWithRef.displayName = 'EmojiPickerButtonDropdownWithRef';

export default withLocalize(EmojiPickerButtonDropdownWithRef);
20 changes: 11 additions & 9 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,16 @@ function EmojiPickerMenu(props) {
EmojiPickerMenu.propTypes = propTypes;
EmojiPickerMenu.defaultProps = defaultProps;

const EmojiPickerMenuWithRef = React.forwardRef((props, ref) => (
<EmojiPickerMenu
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

EmojiPickerMenuWithRef.displayName = 'EmojiPickerMenuWithRef';

export default compose(
withLocalize,
withOnyx({
Expand All @@ -536,12 +546,4 @@ export default compose(
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
},
}),
)(
React.forwardRef((props, ref) => (
<EmojiPickerMenu
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
)(EmojiPickerMenuWithRef);
20 changes: 11 additions & 9 deletions src/components/EmojiPicker/EmojiPickerMenu/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,16 @@ EmojiPickerMenu.displayName = 'EmojiPickerMenu';
EmojiPickerMenu.propTypes = propTypes;
EmojiPickerMenu.defaultProps = defaultProps;

const EmojiPickerMenuWithRef = React.forwardRef((props, ref) => (
<EmojiPickerMenu
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

EmojiPickerMenuWithRef.displayName = 'EmojiPickerMenuWithRef';

export default compose(
withLocalize,
withOnyx({
Expand All @@ -211,12 +221,4 @@ export default compose(
key: ONYXKEYS.FREQUENTLY_USED_EMOJIS,
},
}),
)(
React.forwardRef((props, ref) => (
<EmojiPickerMenu
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
)(EmojiPickerMenuWithRef);
Loading
Loading