From b9a12f97a2019191fa5a5881276355504ac0b55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Wed, 25 Oct 2023 11:47:40 -0300 Subject: [PATCH 1/6] Migrate Button to function component --- src/components/Button/index.js | 301 ++++++++++++++++++--------------- 1 file changed, 166 insertions(+), 135 deletions(-) diff --git a/src/components/Button/index.js b/src/components/Button/index.js index dc12a4ded5c2..0f26ccdb4a63 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -1,21 +1,21 @@ -import React, {Component} from 'react'; -import {ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; -import styles from '../../styles/styles'; -import themeColors from '../../styles/themes/default'; -import Text from '../Text'; -import KeyboardShortcut from '../../libs/KeyboardShortcut'; -import Icon from '../Icon'; +import React, {useEffect} from 'react'; +import {ActivityIndicator, View} from 'react-native'; import CONST from '../../CONST'; -import * as StyleUtils from '../../styles/StyleUtils'; import HapticFeedback from '../../libs/HapticFeedback'; -import withNavigationFallback from '../withNavigationFallback'; +import KeyboardShortcut from '../../libs/KeyboardShortcut'; import compose from '../../libs/compose'; +import * as StyleUtils from '../../styles/StyleUtils'; +import styles from '../../styles/styles'; +import themeColors from '../../styles/themes/default'; +import Icon from '../Icon'; import * as Expensicons from '../Icon/Expensicons'; -import withNavigationFocus from '../withNavigationFocus'; -import validateSubmitShortcut from './validateSubmitShortcut'; import PressableWithFeedback from '../Pressable/PressableWithFeedback'; +import Text from '../Text'; import refPropTypes from '../refPropTypes'; +import withNavigationFallback from '../withNavigationFallback'; +import withNavigationFocus from '../withNavigationFocus'; +import validateSubmitShortcut from './validateSubmitShortcut'; const propTypes = { /** Should the press event bubble across multiple instances when Enter key triggers it. */ @@ -161,49 +161,82 @@ const defaultProps = { forwardedRef: undefined, }; -class Button extends Component { - constructor(props) { - super(props); - - this.renderContent = this.renderContent.bind(this); - } - - componentDidMount() { - if (!this.props.pressOnEnter) { - return; +function Button({ + allowBubble, + text, + shouldShowRightIcon, + + icon, + iconRight, + iconFill, + iconStyles, + iconRightStyles, + + small, + large, + medium, + + isLoading, + isDisabled, + + onPress, + onLongPress, + onPressIn, + onPressOut, + onMouseDown, + + pressOnEnter, + enterKeyEventListenerPriority, + + style, + innerStyles, + textStyles, + + shouldUseDefaultHover, + success, + danger, + children, + + shouldRemoveRightBorderRadius, + shouldRemoveLeftBorderRadius, + shouldEnableHapticFeedback, + + isFocused, + nativeID, + accessibilityLabel, + forwardedRef, +}) { + useEffect(() => { + if (pressOnEnter) { + return () => {}; } const shortcutConfig = CONST.KEYBOARD_SHORTCUTS.ENTER; // Setup and attach keypress handler for pressing the button with Enter key - this.unsubscribe = KeyboardShortcut.subscribe( + return KeyboardShortcut.subscribe( shortcutConfig.shortcutKey, (e) => { - if (!validateSubmitShortcut(this.props.isFocused, this.props.isDisabled, this.props.isLoading, e)) { + if (!validateSubmitShortcut(isFocused, isDisabled, isLoading, e)) { return; } - this.props.onPress(); + onPress(); }, shortcutConfig.descriptionKey, shortcutConfig.modifiers, true, - this.props.allowBubble, - this.props.enterKeyEventListenerPriority, + allowBubble, + enterKeyEventListenerPriority, false, ); - } - componentWillUnmount() { - // Cleanup event listeners - if (!this.unsubscribe) { - return; - } - this.unsubscribe(); - } + // This effect should run only once during mounting + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - renderContent() { - if (this.props.children) { - return this.props.children; + const renderContent = () => { + if (children) { + return children; } const textComponent = ( @@ -211,44 +244,44 @@ class Button extends Component { numberOfLines={1} selectable={false} style={[ - this.props.isLoading && styles.opacity0, + isLoading && styles.opacity0, styles.pointerEventsNone, styles.buttonText, - this.props.small && styles.buttonSmallText, - this.props.medium && styles.buttonMediumText, - this.props.large && styles.buttonLargeText, - this.props.success && styles.buttonSuccessText, - this.props.danger && styles.buttonDangerText, - this.props.icon && styles.textAlignLeft, - ...this.props.textStyles, + small && styles.buttonSmallText, + medium && styles.buttonMediumText, + large && styles.buttonLargeText, + success && styles.buttonSuccessText, + danger && styles.buttonDangerText, + icon && styles.textAlignLeft, + ...textStyles, ]} dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} > - {this.props.text} + {text} ); - if (this.props.icon || this.props.shouldShowRightIcon) { + if (icon || shouldShowRightIcon) { return ( - {this.props.icon && ( - + {icon && ( + )} {textComponent} - {this.props.shouldShowRightIcon && ( - + {shouldShowRightIcon && ( + )} @@ -257,86 +290,84 @@ class Button extends Component { } return textComponent; - } - - render() { - return ( - { - if (e && e.type === 'click') { - e.currentTarget.blur(); - } - - if (this.props.shouldEnableHapticFeedback) { - HapticFeedback.press(); - } - return this.props.onPress(e); - }} - onLongPress={(e) => { - if (this.props.shouldEnableHapticFeedback) { - HapticFeedback.longPress(); - } - this.props.onLongPress(e); - }} - onPressIn={this.props.onPressIn} - onPressOut={this.props.onPressOut} - onMouseDown={this.props.onMouseDown} - disabled={this.props.isLoading || this.props.isDisabled} - wrapperStyle={[ - this.props.isDisabled ? {...styles.cursorDisabled, ...styles.noSelect} : {}, - styles.buttonContainer, - this.props.shouldRemoveRightBorderRadius ? styles.noRightBorderRadius : undefined, - this.props.shouldRemoveLeftBorderRadius ? styles.noLeftBorderRadius : undefined, - ...StyleUtils.parseStyleAsArray(this.props.style), - ]} - style={[ - styles.button, - this.props.small ? styles.buttonSmall : undefined, - this.props.medium ? styles.buttonMedium : undefined, - this.props.large ? styles.buttonLarge : undefined, - this.props.success ? styles.buttonSuccess : undefined, - this.props.danger ? styles.buttonDanger : undefined, - this.props.isDisabled && (this.props.success || this.props.danger) ? styles.buttonOpacityDisabled : undefined, - this.props.isDisabled && !this.props.danger && !this.props.success ? styles.buttonDisabled : undefined, - this.props.shouldRemoveRightBorderRadius ? styles.noRightBorderRadius : undefined, - this.props.shouldRemoveLeftBorderRadius ? styles.noLeftBorderRadius : undefined, - ...this.props.innerStyles, - ]} - hoverStyle={[ - this.props.shouldUseDefaultHover && !this.props.isDisabled ? styles.buttonDefaultHovered : undefined, - this.props.success && !this.props.isDisabled ? styles.buttonSuccessHovered : undefined, - this.props.danger && !this.props.isDisabled ? styles.buttonDangerHovered : undefined, - ]} - nativeID={this.props.nativeID} - accessibilityLabel={this.props.accessibilityLabel} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} - hoverDimmingValue={1} - > - {this.renderContent()} - {this.props.isLoading && ( - - )} - - ); - } + }; + + return ( + { + if (e && e.type === 'click') { + e.currentTarget.blur(); + } + + if (shouldEnableHapticFeedback) { + HapticFeedback.press(); + } + return onPress(e); + }} + onLongPress={(e) => { + if (shouldEnableHapticFeedback) { + HapticFeedback.longPress(); + } + onLongPress(e); + }} + onPressIn={onPressIn} + onPressOut={onPressOut} + onMouseDown={onMouseDown} + disabled={isLoading || isDisabled} + wrapperStyle={[ + isDisabled ? {...styles.cursorDisabled, ...styles.noSelect} : {}, + styles.buttonContainer, + shouldRemoveRightBorderRadius ? styles.noRightBorderRadius : undefined, + shouldRemoveLeftBorderRadius ? styles.noLeftBorderRadius : undefined, + ...StyleUtils.parseStyleAsArray(style), + ]} + style={[ + styles.button, + small ? styles.buttonSmall : undefined, + medium ? styles.buttonMedium : undefined, + large ? styles.buttonLarge : undefined, + success ? styles.buttonSuccess : undefined, + danger ? styles.buttonDanger : undefined, + isDisabled && (success || danger) ? styles.buttonOpacityDisabled : undefined, + isDisabled && !danger && !success ? styles.buttonDisabled : undefined, + shouldRemoveRightBorderRadius ? styles.noRightBorderRadius : undefined, + shouldRemoveLeftBorderRadius ? styles.noLeftBorderRadius : undefined, + ...innerStyles, + ]} + hoverStyle={[ + shouldUseDefaultHover && !isDisabled ? styles.buttonDefaultHovered : undefined, + success && !isDisabled ? styles.buttonSuccessHovered : undefined, + danger && !isDisabled ? styles.buttonDangerHovered : undefined, + ]} + nativeID={nativeID} + accessibilityLabel={accessibilityLabel} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON} + hoverDimmingValue={1} + > + {renderContent()} + {isLoading && ( + + )} + + ); } Button.propTypes = propTypes; Button.defaultProps = defaultProps; +Button.displayName = 'Button'; + +const ButtonWithRef = React.forwardRef((props, ref) => ( +