Skip to content

Block activation of primary buttons on background screens #8802

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

Merged
merged 8 commits into from
May 2, 2022
Merged
8 changes: 7 additions & 1 deletion src/components/Button.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {Component} from 'react';
import {withNavigationFocus} from '@react-navigation/compat';
import {Pressable, ActivityIndicator, View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
Expand All @@ -10,6 +11,8 @@ import Icon from './Icon';
import CONST from '../CONST';
import * as StyleUtils from '../styles/StyleUtils';
import HapticFeedback from '../libs/HapticFeedback';
import withNavigationFallback from './withNavigationFallback';
import compose from '../libs/compose';
import * as Expensicons from './Icon/Expensicons';
import colors from '../styles/colors';

Expand Down Expand Up @@ -277,4 +280,7 @@ class Button extends Component {
Button.propTypes = propTypes;
Button.defaultProps = defaultProps;

export default Button;
export default compose(
withNavigationFallback,
withNavigationFocus,
Copy link
Contributor

Choose a reason for hiding this comment

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

What does the withNavigationFocus do here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, I forgot to push the final commit. Good catch.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still not seeing how it is used, could you explain please?

Copy link
Member Author

Choose a reason for hiding this comment

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

this HOC gives is a new prop called isFocused and we are using that to determine if the button is visible on the currently focused/active screen. This way all the buttons on the backgrounded screens will not trigge with enter press.

)(Button);
46 changes: 46 additions & 0 deletions src/components/withNavigationFallback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {NavigationContext} from '@react-navigation/core';
import getComponentDisplayName from '../libs/getComponentDisplayName';

export default function (WrappedComponent) {
class WithNavigationFallback extends Component {
render() {
if (!this.context) {
return (
<NavigationContext.Provider
value={{
isFocused: () => true,
addListener: () => () => {},
removeListener: () => () => {},
}}
>
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...this.props}
ref={this.props.forwardedRef}
/>
</NavigationContext.Provider>
);
}

// eslint-disable-next-line react/jsx-props-no-spreading
return <WrappedComponent {...this.props} ref={this.props.forwardedRef} />;
}
}
WithNavigationFallback.contextType = NavigationContext;
WithNavigationFallback.displayName = `WithNavigationFocusWithFallback(${getComponentDisplayName(WrappedComponent)})`;
WithNavigationFallback.propTypes = {
forwardedRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({current: PropTypes.instanceOf(React.Component)}),
]),
};
WithNavigationFallback.defaultProps = {
forwardedRef: undefined,
};
return React.forwardRef((props, ref) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<WithNavigationFallback {...props} forwardedRef={ref} />
));
}
1 change: 1 addition & 0 deletions src/pages/RequestCallPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ class RequestCallPage extends Component {
)}
<Button
success
pressOnEnter
onPress={this.onSubmit}
style={[styles.w100]}
text={this.props.translate('requestCallPage.callMe')}
Expand Down
26 changes: 25 additions & 1 deletion src/stories/Button.stories.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useCallback, useState} from 'react';
import Button from '../components/Button';

/**
Expand All @@ -18,6 +18,23 @@ const Template = args => <Button {...args} />;
// See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Default = Template.bind({});
const Loading = Template.bind({});
const PressOnEnter = (props) => {
const [text, setText] = useState('');
const onPress = useCallback(() => {
setText('Button Pressed!');
setTimeout(() => setText(''), 500);
});
return (
<Button
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
// eslint-disable-next-line react/prop-types
text={text || props.text}
onPress={onPress}
/>
);
};

Default.args = {
text: 'Save & Continue',
success: true,
Expand All @@ -28,8 +45,15 @@ Loading.args = {
success: true,
};

PressOnEnter.args = {
text: 'Press Enter',
pressOnEnter: true,
success: true,
};

export default story;
export {
Default,
Loading,
PressOnEnter,
};