From 1e9ae6e157cc0e6ec3823b7d09a3e3bc0194f2d2 Mon Sep 17 00:00:00 2001 From: Alex Junior Date: Mon, 27 Jun 2022 15:46:59 -0300 Subject: [PATCH] Chore: Migration to Hooks - FormTextInput (#4256) * chore: migrate TextInput from class to functional * removing the theme props * adding tests for the FormTextInput * minor tweak * applying changes requested * changing the way we import/export the FormTextInput and TextInput * removing left comments * minor tweak * fix import Co-authored-by: GleidsonDaniel --- app/containers/MessageBox/index.tsx | 3 +- .../__snapshots__/SearchBox.stories.storyshot | 2 +- app/containers/SearchBox/index.tsx | 5 +- app/containers/SearchHeader.tsx | 3 +- .../TextInput/FormTextInput.test.tsx | 54 ++++ app/containers/TextInput/FormTextInput.tsx | 244 ++++++++---------- app/containers/TextInput/TextInput.stories.js | 8 +- app/containers/TextInput/TextInput.tsx | 29 +++ app/containers/TextInput/index.ts | 2 + app/containers/TextInput/index.tsx | 29 --- app/containers/TwoFactor/index.tsx | 3 +- app/containers/UIKit/MultiSelect/index.tsx | 3 +- app/containers/UIKit/index.tsx | 4 +- app/lib/constants/defaultSettings.ts | 2 +- app/lib/services/restApi.ts | 2 - app/views/CreateChannelView.tsx | 3 +- app/views/CreateDiscussionView/index.tsx | 6 +- app/views/E2EEncryptionSecurityView.tsx | 3 +- app/views/E2EEnterYourPasswordView.tsx | 3 +- app/views/ForgotPasswordView.tsx | 3 +- app/views/InviteUsersView/index.tsx | 4 +- app/views/LivechatEditView.tsx | 8 +- app/views/LoginView.tsx | 4 +- app/views/NewServerView/ServerInput/index.tsx | 5 +- .../DeleteAccountActionSheetContent/index.tsx | 4 +- app/views/ProfileView/index.tsx | 13 +- app/views/RegisterView.tsx | 10 +- app/views/RoomInfoEditView/index.tsx | 7 +- app/views/RoomView/JoinCode.tsx | 3 +- app/views/RoomsListView/Header/Header.tsx | 5 +- app/views/SearchMessagesView/index.tsx | 3 +- app/views/SendEmailConfirmationView.tsx | 5 +- app/views/SetUsernameView.tsx | 6 +- app/views/ShareListView/Header/Header.tsx | 3 +- app/views/ShareListView/Header/SearchBox.tsx | 3 +- app/views/ShareView/index.tsx | 8 +- app/views/StatusView/index.tsx | 6 +- 37 files changed, 233 insertions(+), 275 deletions(-) create mode 100644 app/containers/TextInput/FormTextInput.test.tsx create mode 100644 app/containers/TextInput/TextInput.tsx create mode 100644 app/containers/TextInput/index.ts delete mode 100644 app/containers/TextInput/index.tsx diff --git a/app/containers/MessageBox/index.tsx b/app/containers/MessageBox/index.tsx index dd604d602..107edd64d 100644 --- a/app/containers/MessageBox/index.tsx +++ b/app/containers/MessageBox/index.tsx @@ -9,7 +9,7 @@ import { Q } from '@nozbe/watermelondb'; import { TouchableWithoutFeedback } from 'react-native-gesture-handler'; import { generateTriggerId } from '../../lib/methods/actions'; -import TextInput, { IThemedTextInput } from '../TextInput'; +import { TextInput, IThemedTextInput } from '../TextInput'; import { userTyping as userTypingAction } from '../../actions/room'; import styles from './styles'; import database from '../../lib/database'; @@ -1135,7 +1135,6 @@ class MessageBox extends Component { defaultValue='' multiline testID={`messagebox-input${tmid ? '-thread' : ''}`} - theme={theme} {...isAndroidTablet} /> { - const { theme } = useTheme(); const [text, setText] = useState(''); const internalOnChangeText = useCallback(value => { @@ -34,7 +32,6 @@ const SearchBox = ({ onChangeText, onSubmitEditing, testID }: TextInputProps): J onChangeText={internalOnChangeText} onSubmitEditing={onSubmitEditing} value={text} - theme={theme} testID={testID} onClearInput={() => internalOnChangeText('')} iconRight={'search'} diff --git a/app/containers/SearchHeader.tsx b/app/containers/SearchHeader.tsx index 9f637a446..fff3390a5 100644 --- a/app/containers/SearchHeader.tsx +++ b/app/containers/SearchHeader.tsx @@ -5,7 +5,7 @@ import I18n from '../i18n'; import { useTheme } from '../theme'; import sharedStyles from '../views/Styles'; import { themes } from '../lib/constants'; -import TextInput from './TextInput'; +import { TextInput } from './TextInput'; import { isIOS, isTablet } from '../lib/methods/helpers'; import { useOrientation } from '../dimensions'; @@ -39,7 +39,6 @@ const SearchHeader = ({ onSearchChangeText, testID }: ISearchHeaderProps): JSX.E style={[styles.title, isLight && { color: themes[theme].headerTitleColor }, { fontSize: titleFontSize }]} placeholder={I18n.t('Search')} onChangeText={onSearchChangeText} - theme={theme} testID={testID} /> diff --git a/app/containers/TextInput/FormTextInput.test.tsx b/app/containers/TextInput/FormTextInput.test.tsx new file mode 100644 index 000000000..2838203ce --- /dev/null +++ b/app/containers/TextInput/FormTextInput.test.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { render } from '@testing-library/react-native'; + +import { FormTextInput } from '.'; + +const FormTextInputID = 'form-text-input-id'; + +describe('FormTextInput', () => { + test('should render the component', async () => { + const { findByTestId } = render(); + const component = await findByTestId('form-text-input-id'); + expect(component).toBeTruthy(); + }); + + test('should render the component with left icon', async () => { + const { findByTestId } = render(); + const component = await findByTestId(`${FormTextInputID}-icon-left`); + expect(component).toBeTruthy(); + }); + + test('should render the component with right icon', async () => { + const { findByTestId } = render(); + const component = await findByTestId(`${FormTextInputID}-icon-right`); + expect(component).toBeTruthy(); + }); + + test('should render the component with password icon', async () => { + const { findByTestId } = render(); + const component = await findByTestId(`${FormTextInputID}-icon-password`); + expect(component).toBeTruthy(); + }); + + test('should render the component with loading', async () => { + const { findByTestId } = render(); + const component = await findByTestId(`${FormTextInputID}-loading`); + expect(component).toBeTruthy(); + }); + + test('should render the component with label', async () => { + const { findByText } = render(); + const component = await findByText('form text input'); + expect(component).toBeTruthy(); + }); + + test('should render the component with error', async () => { + const error = { + reason: 'An error occurred' + }; + + const { findByText } = render(); + const component = await findByText(error.reason); + expect(component).toBeTruthy(); + }); +}); diff --git a/app/containers/TextInput/FormTextInput.tsx b/app/containers/TextInput/FormTextInput.tsx index 02b09957f..2c7e09a8d 100644 --- a/app/containers/TextInput/FormTextInput.tsx +++ b/app/containers/TextInput/FormTextInput.tsx @@ -1,14 +1,13 @@ import { BottomSheetTextInput } from '@gorhom/bottom-sheet'; -import React from 'react'; +import React, { useState } from 'react'; import { StyleProp, StyleSheet, Text, TextInput as RNTextInput, TextInputProps, TextStyle, View, ViewStyle } from 'react-native'; import Touchable from 'react-native-platform-touchable'; -import { themes } from '../../lib/constants'; -import { TSupportedThemes } from '../../theme'; +import { useTheme } from '../../theme'; import sharedStyles from '../../views/Styles'; import ActivityIndicator from '../ActivityIndicator'; import { CustomIcon, TIconsName } from '../CustomIcon'; -import TextInput from './index'; +import { TextInput } from './TextInput'; const styles = StyleSheet.create({ error: { @@ -59,149 +58,118 @@ export interface IRCTextInputProps extends TextInputProps { containerStyle?: StyleProp; inputStyle?: StyleProp; inputRef?: React.Ref; - testID?: string; iconLeft?: TIconsName; iconRight?: TIconsName; left?: JSX.Element; - theme: TSupportedThemes; bottomSheet?: boolean; onClearInput?: () => void; } -interface IRCTextInputState { - showPassword: boolean; -} +export const FormTextInput = ({ + label, + error, + loading, + containerStyle, + inputStyle, + inputRef, + iconLeft, + iconRight, + onClearInput, + value, + left, + testID, + secureTextEntry, + bottomSheet, + placeholder, + ...inputProps +}: IRCTextInputProps): React.ReactElement => { + const { colors } = useTheme(); + const [showPassword, setShowPassword] = useState(false); + const showClearInput = onClearInput && value && value.length > 0; + const Input = bottomSheet ? BottomSheetTextInput : TextInput; + return ( + + {label ? ( + {label} + ) : null} -export default class FormTextInput extends React.PureComponent { - static defaultProps = { - error: {}, - theme: 'light' - }; - - state = { - showPassword: false - }; - - get iconLeft() { - const { testID, iconLeft, theme } = this.props; - return iconLeft ? ( - - ) : null; - } - - get iconRight() { - const { iconRight, theme, onClearInput, value } = this.props; - if (onClearInput && value && value.length > 0) { - return ( - - - - ); - } - - return iconRight ? ( - - ) : null; - } - - get iconPassword() { - const { showPassword } = this.state; - const { testID, theme } = this.props; - return ( - - + - - ); - } - get loading() { - const { theme } = this.props; - return ; - } - - tooglePassword = () => { - this.setState(prevState => ({ showPassword: !prevState.showPassword })); - }; - - render() { - const { showPassword } = this.state; - const { - label, - left, - error, - loading, - secureTextEntry, - containerStyle, - inputRef, - iconLeft, - iconRight, - inputStyle, - testID, - placeholder, - theme, - bottomSheet, - ...inputProps - } = this.props; - const { dangerColor } = themes[theme]; - const Input = bottomSheet ? BottomSheetTextInput : TextInput; - return ( - - {label ? ( - {label} - ) : null} - - - {iconLeft ? this.iconLeft : null} - {iconRight ? this.iconRight : null} - {secureTextEntry ? this.iconPassword : null} - {loading ? this.loading : null} - {left} - - {error && error.reason ? {error.reason} : null} + ) : null} + + {showClearInput ? ( + + + + ) : null} + + {iconRight && !showClearInput ? ( + + ) : null} + + {secureTextEntry ? ( + setShowPassword(!showPassword)} style={[styles.iconContainer, styles.iconRight]}> + + + ) : null} + + {loading ? ( + + ) : null} + {left} - ); - } -} + {error && error.reason ? {error.reason} : null} + + ); +}; diff --git a/app/containers/TextInput/TextInput.stories.js b/app/containers/TextInput/TextInput.stories.js index c4a985136..2c1f06941 100644 --- a/app/containers/TextInput/TextInput.stories.js +++ b/app/containers/TextInput/TextInput.stories.js @@ -3,7 +3,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react-native'; import { View, StyleSheet } from 'react-native'; -import FormTextInput from './FormTextInput'; +import { FormTextInput } from '.'; const styles = StyleSheet.create({ paddingHorizontal: { @@ -18,14 +18,12 @@ const item = { longText: 'https://open.rocket.chat/images/logo/android-chrome-512x512.png' }; -const theme = 'light'; - stories.add('Short and Long Text', () => ( <> - + - + )); diff --git a/app/containers/TextInput/TextInput.tsx b/app/containers/TextInput/TextInput.tsx new file mode 100644 index 000000000..0d9325991 --- /dev/null +++ b/app/containers/TextInput/TextInput.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { I18nManager, StyleProp, StyleSheet, TextInput as RNTextInput, TextStyle } from 'react-native'; + +import { IRCTextInputProps } from './FormTextInput'; +import { themes } from '../../lib/constants'; +import { useTheme } from '../../theme'; + +const styles = StyleSheet.create({ + input: { + ...(I18nManager.isRTL ? { textAlign: 'right' } : { textAlign: 'auto' }) + } +}); + +export interface IThemedTextInput extends IRCTextInputProps { + style: StyleProp; +} + +export const TextInput = React.forwardRef(({ style, ...props }, ref) => { + const { theme } = useTheme(); + return ( + + ); +}); diff --git a/app/containers/TextInput/index.ts b/app/containers/TextInput/index.ts new file mode 100644 index 000000000..48a640fc2 --- /dev/null +++ b/app/containers/TextInput/index.ts @@ -0,0 +1,2 @@ +export * from './TextInput'; +export * from './FormTextInput'; diff --git a/app/containers/TextInput/index.tsx b/app/containers/TextInput/index.tsx deleted file mode 100644 index 449ea2d5e..000000000 --- a/app/containers/TextInput/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { I18nManager, StyleProp, StyleSheet, TextInput, TextStyle } from 'react-native'; - -import { IRCTextInputProps } from './FormTextInput'; -import { themes } from '../../lib/constants'; -import { TSupportedThemes } from '../../theme'; - -const styles = StyleSheet.create({ - input: { - ...(I18nManager.isRTL ? { textAlign: 'right' } : { textAlign: 'auto' }) - } -}); - -export interface IThemedTextInput extends IRCTextInputProps { - style: StyleProp; - theme: TSupportedThemes; -} - -const ThemedTextInput = React.forwardRef(({ style, theme, ...props }, ref) => ( - -)); - -export default ThemedTextInput; diff --git a/app/containers/TwoFactor/index.tsx b/app/containers/TwoFactor/index.tsx index d060f31da..a171fd77b 100644 --- a/app/containers/TwoFactor/index.tsx +++ b/app/containers/TwoFactor/index.tsx @@ -6,7 +6,7 @@ import Modal from 'react-native-modal'; import useDeepCompareEffect from 'use-deep-compare-effect'; import { connect } from 'react-redux'; -import FormTextInput from '../TextInput/FormTextInput'; +import { FormTextInput } from '../TextInput'; import I18n from '../../i18n'; import EventEmitter from '../../lib/methods/helpers/events'; import { useTheme } from '../../theme'; @@ -116,7 +116,6 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) = {method?.text ? {I18n.t(method.text)} : null} InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())} returnKeyType='send' autoCapitalize='none' diff --git a/app/containers/UIKit/MultiSelect/index.tsx b/app/containers/UIKit/MultiSelect/index.tsx index 2b0092a34..daafbd33d 100644 --- a/app/containers/UIKit/MultiSelect/index.tsx +++ b/app/containers/UIKit/MultiSelect/index.tsx @@ -13,7 +13,7 @@ import { import { BlockContext } from '@rocket.chat/ui-kit'; import Button from '../../Button'; -import FormTextInput from '../../TextInput/FormTextInput'; +import { FormTextInput } from '../../TextInput'; import { textParser } from '../utils'; import { themes } from '../../../lib/constants'; import I18n from '../../../i18n'; @@ -143,7 +143,6 @@ export const MultiSelect = React.memo( testID='multi-select-search' onChangeText={onSearch || onSearchChange} placeholder={I18n.t('Search')} - theme={theme} /> diff --git a/app/containers/UIKit/index.tsx b/app/containers/UIKit/index.tsx index eef565f7f..92e51ce30 100644 --- a/app/containers/UIKit/index.tsx +++ b/app/containers/UIKit/index.tsx @@ -13,7 +13,7 @@ import { import Markdown, { MarkdownPreview } from '../markdown'; import Button from '../Button'; -import FormTextInput from '../TextInput/FormTextInput'; +import { FormTextInput } from '../TextInput'; import { textParser, useBlockContext } from './utils'; import { themes } from '../../lib/constants'; import sharedStyles from '../../views/Styles'; @@ -188,7 +188,6 @@ class ModalParser extends UiKitParserModal { plainInput(element: IElement, context: BlockContext) { const [{ loading, value, error }, action] = useBlockContext(element, context); - const { theme } = useContext(ThemeContext); const { multiline, actionId, placeholder } = element; return ( { containerStyle={styles.input} value={value} error={{ error }} - theme={theme} /> ); } diff --git a/app/lib/constants/defaultSettings.ts b/app/lib/constants/defaultSettings.ts index 90b928d9c..3a6017b44 100644 --- a/app/lib/constants/defaultSettings.ts +++ b/app/lib/constants/defaultSettings.ts @@ -222,7 +222,7 @@ export const defaultSettings = { type: 'valueAsBoolean' }, VideoConf_Enable_Teams: { - type: 'valueAsBoolean' + type: 'valueAsBoolean' }, Accounts_AllowDeleteOwnAccount: { type: 'valueAsBoolean' diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts index 566c55c60..b51a3627f 100644 --- a/app/lib/services/restApi.ts +++ b/app/lib/services/restApi.ts @@ -918,7 +918,6 @@ export function getUserInfo(userId: string) { export const toggleFavorite = (roomId: string, favorite: boolean) => sdk.post('rooms.favorite', { roomId, favorite }); - export const videoConferenceJoin = (callId: string, cam: boolean) => sdk.post('video-conference.join', { callId, state: { cam } }); @@ -936,4 +935,3 @@ export const saveUserProfileMethod = ( export const deleteOwnAccount = (password: string, confirmRelinquish = false): any => // RC 0.67.0 sdk.post('users.deleteOwnAccount', { password, confirmRelinquish }); - diff --git a/app/views/CreateChannelView.tsx b/app/views/CreateChannelView.tsx index f31cd86c8..4f66db11a 100644 --- a/app/views/CreateChannelView.tsx +++ b/app/views/CreateChannelView.tsx @@ -4,7 +4,7 @@ import { FlatList, ScrollView, StyleSheet, Switch, Text, View, SwitchProps } fro import { dequal } from 'dequal'; import * as List from '../containers/List'; -import TextInput from '../containers/TextInput'; +import { TextInput } from '../containers/TextInput'; import Loading from '../containers/Loading'; import { createChannelRequest } from '../actions/createChannel'; import { removeUser } from '../actions/selectedUsers'; @@ -390,7 +390,6 @@ class CreateChannelView extends React.Component diff --git a/app/views/CreateDiscussionView/index.tsx b/app/views/CreateDiscussionView/index.tsx index 89580aa90..775af6e51 100644 --- a/app/views/CreateDiscussionView/index.tsx +++ b/app/views/CreateDiscussionView/index.tsx @@ -11,10 +11,9 @@ import * as HeaderButton from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; import { withTheme } from '../../theme'; import { getUserSelector } from '../../selectors/login'; -import FormTextInput from '../../containers/TextInput/FormTextInput'; +import { FormTextInput } from '../../containers/TextInput'; import Navigation from '../../lib/navigation/appNavigation'; import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion'; -import { showErrorAlert } from '../../lib/methods/helpers/info'; import SafeAreaView from '../../containers/SafeAreaView'; import { goRoom } from '../../lib/methods/helpers/goRoom'; import { events, logEvent } from '../../lib/methods/helpers/log'; @@ -24,7 +23,7 @@ import SelectChannel from './SelectChannel'; import { ICreateChannelViewProps, IResult, IError, ICreateChannelViewState } from './interfaces'; import { IApplicationState, ISearchLocal, ISubscription } from '../../definitions'; import { E2E_ROOM_TYPES, SWITCH_TRACK_COLOR, themes } from '../../lib/constants'; -import { getRoomTitle } from '../../lib/methods/helpers'; +import { getRoomTitle, showErrorAlert } from '../../lib/methods/helpers'; class CreateChannelView extends React.Component { private channel: ISubscription; @@ -174,7 +173,6 @@ class CreateChannelView extends React.Component this.setState({ name: text })} - theme={theme} />