Chore: Migrate containers/Button to Typescript and Hooks (#4071)

This commit is contained in:
Danish Ahmed Mirza 2022-05-20 22:07:57 +05:30 committed by GitHub
parent 1e09589eca
commit 6384d60efc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 178 additions and 115 deletions

View File

@ -0,0 +1,28 @@
import React from 'react';
import { storiesOf } from '@storybook/react-native';
import Button from '.';
const buttonProps = {
title: 'Press me!',
type: 'primary',
onPress: () => {},
testID: 'testButton',
fontSize: 16,
style: {
padding: 10,
justifyContent: 'center'
}
};
const stories = storiesOf('Button', module);
stories.add('primary button', () => <Button {...buttonProps} />);
stories.add('secondary button', () => <Button {...buttonProps} type='secondary' />);
stories.add('loading button', () => <Button loading {...buttonProps} />);
stories.add('disabled button', () => <Button disabled {...buttonProps} />);
stories.add('disabled loading button', () => <Button disabled loading {...buttonProps} />);

View File

@ -0,0 +1,71 @@
import React from 'react';
import { View } from 'react-native';
import { fireEvent, render } from '@testing-library/react-native';
import Button from '.';
const onPressMock = jest.fn();
const testProps = {
title: 'Press me!',
type: 'primary',
onPress: onPressMock,
testID: 'testButton',
initialText: 'Initial text',
textAfterPress: 'Button pressed!'
};
const TestButton = ({ loading = false, disabled = false }) => (
<View>
<Button
title={testProps.title}
type={testProps.title}
onPress={testProps.onPress}
testID={testProps.testID}
accessibilityLabel={testProps.title}
disabled={disabled}
loading={loading}
/>
</View>
);
describe('ButtonTests', () => {
test('rendered', async () => {
const { findByTestId } = render(<TestButton />);
const Button = await findByTestId(testProps.testID);
expect(Button).toBeTruthy();
});
test('rendered with correct title', async () => {
const { findByText } = render(<TestButton />);
const ButtonTitle = await findByText(testProps.title);
expect(ButtonTitle).toBeTruthy();
expect(ButtonTitle.props.children).toEqual(testProps.title);
});
test('find button using accessibilityLabel', async () => {
const { findByA11yLabel } = render(<TestButton />);
const Button = await findByA11yLabel(testProps.title);
expect(Button).toBeTruthy();
});
test('title not visible while loading', async () => {
const { queryByText } = render(<TestButton loading={true} />);
const ButtonTitle = await queryByText(testProps.title);
expect(ButtonTitle).toBeNull();
});
test('should not trigger onPress on disabled button', async () => {
const { findByTestId } = render(<TestButton disabled={true} />);
const Button = await findByTestId(testProps.testID);
fireEvent.press(Button);
expect(onPressMock).not.toHaveBeenCalled();
});
test('should trigger onPress function on button press', async () => {
const { findByTestId } = render(<TestButton />);
const Button = await findByTestId(testProps.testID);
fireEvent.press(Button);
expect(onPressMock).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots Button disabled button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3,\\"padding\\":10}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
exports[`Storyshots Button disabled loading button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3,\\"padding\\":10}},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#ffffff\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null]},\\"children\\":null}]}"`;
exports[`Storyshots Button loading button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#ffffff\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null]},\\"children\\":null}]}"`;
exports[`Storyshots Button primary button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
exports[`Storyshots Button secondary button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"Press me!\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#ffffff\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;

View File

@ -1,25 +1,20 @@
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import { StyleProp, StyleSheet, Text, TextStyle } from 'react-native';
import Touchable, { PlatformTouchableProps } from 'react-native-platform-touchable';
import { TSupportedThemes } from '../../theme';
import { themes } from '../../lib/constants';
import { useTheme } from '../../theme';
import sharedStyles from '../../views/Styles';
import ActivityIndicator from '../ActivityIndicator';
interface IButtonProps {
interface IButtonProps extends PlatformTouchableProps {
title: string;
type: string;
onPress(): void;
disabled: boolean;
backgroundColor: string;
loading: boolean;
theme: TSupportedThemes;
color: string;
fontSize: any;
style: any;
styleText?: any;
testID: string;
onPress: () => void;
type?: string;
backgroundColor?: string;
loading?: boolean;
color?: string;
fontSize?: number;
styleText?: StyleProp<TextStyle>[];
}
const styles = StyleSheet.create({
@ -31,7 +26,6 @@ const styles = StyleSheet.create({
marginBottom: 12
},
text: {
fontSize: 16,
...sharedStyles.textMedium,
...sharedStyles.textAlignCenter
},
@ -40,21 +34,23 @@ const styles = StyleSheet.create({
}
});
export default class Button extends React.PureComponent<Partial<IButtonProps>, any> {
static defaultProps = {
title: 'Press me!',
type: 'primary',
onPress: () => alert('It works!'),
disabled: false,
loading: false
};
render() {
const { title, type, onPress, disabled, backgroundColor, color, loading, style, theme, fontSize, styleText, ...otherProps } =
this.props;
const Button = ({
type = 'primary',
disabled = false,
loading = false,
fontSize = 16,
title,
onPress,
backgroundColor,
color,
style,
styleText,
...otherProps
}: IButtonProps): React.ReactElement => {
const { colors } = useTheme();
const isPrimary = type === 'primary';
let textColor = isPrimary ? themes[theme!].buttonText : themes[theme!].bodyText;
let textColor = isPrimary ? colors.buttonText : colors.bodyText;
if (color) {
textColor = color;
}
@ -65,9 +61,7 @@ export default class Button extends React.PureComponent<Partial<IButtonProps>, a
disabled={disabled || loading}
style={[
styles.container,
backgroundColor
? { backgroundColor }
: { backgroundColor: isPrimary ? themes[theme!].actionTintColor : themes[theme!].backgroundColor },
backgroundColor ? { backgroundColor } : { backgroundColor: isPrimary ? colors.actionTintColor : colors.backgroundColor },
disabled && styles.disabled,
style
]}
@ -76,11 +70,12 @@ export default class Button extends React.PureComponent<Partial<IButtonProps>, a
{loading ? (
<ActivityIndicator color={textColor} />
) : (
<Text style={[styles.text, { color: textColor }, fontSize && { fontSize }, styleText]} accessibilityLabel={title}>
<Text style={[styles.text, { color: textColor, fontSize }, styleText]} accessibilityLabel={title}>
{title}
</Text>
)}
</Touchable>
);
}
}
};
export default Button;

View File

@ -327,7 +327,6 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, ILoginServi
title={collapsed ? I18n.t('Onboarding_more_options') : I18n.t('Onboarding_less_options')}
type='secondary'
onPress={this.toggleServices}
theme={theme}
style={styles.options}
color={themes[theme].actionTintColor}
/>

View File

@ -139,16 +139,8 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) =
backgroundColor={themes[theme].chatComponentBackground}
style={styles.button}
onPress={onCancel}
theme={theme}
/>
<Button
title={I18n.t('Send')}
type='primary'
style={styles.button}
onPress={onSubmit}
theme={theme}
testID='two-factor-send'
/>
<Button title={I18n.t('Send')} type='primary' style={styles.button} onPress={onSubmit} testID='two-factor-send' />
</View>
</View>
</View>

View File

@ -4,10 +4,8 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import Button from '../Button';
import I18n from '../../i18n';
import { IActions } from './interfaces';
import { useTheme } from '../../theme';
export const Actions = ({ blockId, appId, elements, parser }: IActions) => {
const { theme } = useTheme();
const [showMoreVisible, setShowMoreVisible] = useState(() => elements && elements.length > 5);
const renderedElements = showMoreVisible ? elements?.slice(0, 5) : elements;
@ -18,7 +16,7 @@ export const Actions = ({ blockId, appId, elements, parser }: IActions) => {
return (
<>
<Elements />
{showMoreVisible && <Button theme={theme} title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />}
{showMoreVisible && <Button title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />}
</>
);
};

View File

@ -56,9 +56,7 @@ export const DatePicker = ({ element, language, action, context, loading, value,
}
};
let button = placeholder ? (
<Button title={textParser([placeholder])} onPress={() => onShow(!show)} loading={loading} theme={theme} />
) : null;
let button = placeholder ? <Button title={textParser([placeholder])} onPress={() => onShow(!show)} loading={loading} /> : null;
if (context === BLOCK_CONTEXT.FORM) {
button = (

View File

@ -157,7 +157,7 @@ export const MultiSelect = React.memo(
});
let button = multiselect ? (
<Button title={`${selected.length} selecteds`} onPress={onShow} loading={loading} theme={theme} />
<Button title={`${selected.length} selecteds`} onPress={onShow} loading={loading} />
) : (
<Input
onPress={onShow}

View File

@ -63,7 +63,6 @@ class MessageParser extends UiKitParserMessage {
button(element: IButton, context: BlockContext) {
const { text, value, actionId, style } = element;
const [{ loading }, action] = useBlockContext(element, context);
const { theme } = useContext(ThemeContext);
return (
<Button
key={actionId}
@ -72,7 +71,6 @@ class MessageParser extends UiKitParserMessage {
loading={loading}
onPress={() => action({ value })}
style={styles.button}
theme={theme}
/>
);
}

View File

@ -43,7 +43,7 @@ const AttachedActions = ({ attachment }: { attachment: IAttachment }) => {
};
if (element.type === 'button') {
return <Button theme={theme} onPress={onPress} title={element.text} />;
return <Button onPress={onPress} title={element.text} />;
}
return null;

View File

@ -159,13 +159,7 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps)
</View>
</View>
</View>
<Button
title={I18n.t('Use')}
theme={theme}
style={styles.button}
type='primary'
onPress={() => navigateToRoom(cannedResponse)}
/>
<Button title={I18n.t('Use')} style={styles.button} type='primary' onPress={() => navigateToRoom(cannedResponse)} />
</ScrollView>
</SafeAreaView>
);

View File

@ -40,7 +40,6 @@ const CannedResponseItem = ({
fontSize={12}
color={themes[theme].titleText}
style={[styles.cannedUseButton, { backgroundColor: themes[theme].chatComponentBackground }]}
theme={theme}
onPress={onPressUse}
/>
</View>

File diff suppressed because one or more lines are too long

View File

@ -144,7 +144,6 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
<Button
onPress={this.changePassword}
title={I18n.t('Save_Changes')}
theme={theme}
disabled={!newPassword.trim()}
style={styles.changePasswordButton}
testID='e2e-encryption-security-view-change-password'
@ -175,7 +174,6 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
<Button
onPress={this.resetOwnKey}
title={I18n.t('E2E_encryption_reset_button')}
theme={theme}
type='secondary'
backgroundColor={themes[theme].chatComponentBackground}
testID='e2e-encryption-security-view-reset-key'

View File

@ -93,7 +93,6 @@ class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordView
onPress={this.submit}
title={I18n.t('Confirm')}
disabled={!password}
theme={theme}
testID='e2e-enter-your-password-view-confirm'
/>
<Text style={[styles.info, { color: themes[theme].bodyText }]}>{I18n.t('Enter_Your_Encryption_Password_desc1')}</Text>

View File

@ -146,7 +146,6 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
title={I18n.t('Copy')}
type='secondary'
fontSize={12}
theme={theme}
/>
</View>
<Text style={[styles.info, { color: themes[theme].bodyText }]}>{I18n.t('Save_Your_Encryption_Password_info')}</Text>
@ -155,13 +154,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
title={I18n.t('How_It_Works')}
type='secondary'
theme={theme}
testID='e2e-save-password-view-how-it-works'
/>
<Button
onPress={this.onSaved}
title={I18n.t('I_Saved_My_E2E_Password')}
theme={theme}
testID='e2e-save-password-view-saved-password'
/>
</View>

View File

@ -110,7 +110,6 @@ class ForgotPasswordView extends React.Component<IForgotPasswordViewProps, IForg
testID='forgot-password-view-submit'
loading={isFetching}
disabled={invalidEmail}
theme={theme}
/>
</FormContainerInner>
</FormContainer>

View File

@ -121,7 +121,6 @@ class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any
};
render() {
const { theme } = this.props;
return (
<SafeAreaView>
<List.Container>
@ -134,7 +133,7 @@ class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any
<List.Separator />
</List.Section>
<View style={styles.innerContainer}>
<Button title={I18n.t('Generate_New_Link')} type='primary' onPress={this.createInviteLink} theme={theme} />
<Button title={I18n.t('Generate_New_Link')} type='primary' onPress={this.createInviteLink} />
</View>
</List.Container>
</SafeAreaView>

View File

@ -95,8 +95,8 @@ const InviteUsersView = ({ route, navigation }: IInviteUsersViewProps): React.Re
<FormTextInput label={I18n.t('Invite_Link')} theme={theme} value={invite && invite.url} editable={false} />
{renderExpiration()}
<View style={[styles.divider, { backgroundColor: colors.separatorColor }]} />
<Button title={I18n.t('Share_Link')} type='primary' onPress={share} theme={theme} />
<Button title={I18n.t('Edit_Invite')} type='secondary' onPress={edit} theme={theme} />
<Button title={I18n.t('Share_Link')} type='primary' onPress={share} />
<Button title={I18n.t('Edit_Invite')} type='secondary' onPress={edit} />
</View>
</ScrollView>
</SafeAreaView>

View File

@ -298,7 +298,7 @@ const LivechatEditView = ({
/>
))}
<Button title={I18n.t('Save')} onPress={submit} theme={theme} />
<Button title={I18n.t('Save')} onPress={submit} />
</SafeAreaView>
</ScrollView>
</KeyboardView>

View File

@ -196,7 +196,6 @@ class LoginView extends React.Component<ILoginViewProps, ILoginViewState> {
testID='login-view-submit'
loading={isFetching}
disabled={!this.valid()}
theme={theme}
style={styles.loginButton}
/>
{Accounts_PasswordReset && (
@ -205,7 +204,6 @@ class LoginView extends React.Component<ILoginViewProps, ILoginViewState> {
type='secondary'
onPress={this.forgotPassword}
testID='login-view-forgot-password'
theme={theme}
color={themes[theme].auxiliaryText}
fontSize={14}
/>

View File

@ -374,7 +374,6 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
disabled={!text || connecting}
loading={!connectingOpen && connecting}
style={[styles.connectButton, { marginTop: verticalScale({ size: 16, height }) }]}
theme={theme}
testID='new-server-view-button'
/>
<OrSeparator theme={theme} />
@ -396,7 +395,6 @@ class NewServerView extends React.Component<INewServerViewProps, INewServerViewS
onPress={this.connectOpen}
disabled={connecting}
loading={connectingOpen && connecting}
theme={theme}
testID='new-server-view-open'
/>
</FormContainerInner>

View File

@ -597,7 +597,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
disabled={!this.formIsChanged()}
testID='profile-view-submit'
loading={saving}
theme={theme}
/>
<Button
title={I18n.t('Logout_from_other_logged_in_locations')}
@ -605,7 +604,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
backgroundColor={themes[theme].chatComponentBackground}
onPress={this.logoutOtherLocations}
testID='profile-view-logout-other-locations'
theme={theme}
/>
</ScrollView>
</SafeAreaView>

View File

@ -310,7 +310,6 @@ class RegisterView extends React.Component<IProps, any> {
testID='register-view-submit'
disabled={!this.valid()}
loading={saving}
theme={theme}
style={styles.registerButton}
/>

View File

@ -106,7 +106,6 @@ const JoinCode = React.memo(
type='secondary'
style={styles.button}
backgroundColor={themes[theme].chatComponentBackground}
theme={theme}
testID='join-code-cancel'
onPress={hide}
/>
@ -114,7 +113,6 @@ const JoinCode = React.memo(
title={I18n.t('Join')}
type='primary'
style={styles.button}
theme={theme}
testID='join-code-submit'
onPress={handleJoinRoom}
/>

View File

@ -250,7 +250,6 @@ class ServerDropdown extends Component<IServerDropdownProps, IServerDropdownStat
title={I18n.t('Create_a_new_workspace')}
type='secondary'
onPress={this.createWorkspace}
theme={theme}
testID='rooms-list-header-create-workspace-button'
style={styles.buttonCreateWorkspace}
color={themes[theme].tintColor}

View File

@ -78,7 +78,6 @@ const SendEmailConfirmationView = ({ navigation, route }: ISendEmailConfirmation
testID='send-email-confirmation-view-submit'
loading={isFetching}
disabled={invalidEmail}
theme={theme}
/>
</FormContainerInner>
</FormContainer>

View File

@ -137,7 +137,6 @@ class SetUsernameView extends React.Component<ISetUsernameViewProps, ISetUsernam
testID='set-username-view-submit'
disabled={!username}
loading={saving}
theme={theme}
/>
</SafeAreaView>
</ScrollView>

View File

@ -81,7 +81,7 @@ class WorkspaceView extends React.Component<IWorkSpaceProp, any> {
<Text style={[styles.serverUrl, { color: themes[theme].auxiliaryText }]}>{Site_Url}</Text>
</View>
{showLoginButton ? (
<Button title={I18n.t('Login')} type='primary' onPress={this.login} theme={theme} testID='workspace-view-login' />
<Button title={I18n.t('Login')} type='primary' onPress={this.login} testID='workspace-view-login' />
) : null}
{this.showRegistrationButton ? (
<Button
@ -89,7 +89,6 @@ class WorkspaceView extends React.Component<IWorkSpaceProp, any> {
type='secondary'
backgroundColor={themes[theme].chatComponentBackground}
onPress={this.register}
theme={theme}
testID='workspace-view-register'
/>
) : (

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,7 @@ import '../../app/views/RoomView/LoadMore/LoadMore.stories';
import '../../app/views/CannedResponsesListView/CannedResponseItem.stories';
import '../../app/containers/TextInput/TextInput.stories';
import '../../app/containers/message/Components/CollapsibleQuote/CollapsibleQuote.stories';
import '../../app/containers/Button/Button.stories';
// Change here to see themed storybook
export const theme = 'light';