From ca171a607abad02cd95aa15f10c6fc149bddb972 Mon Sep 17 00:00:00 2001 From: Joan Gabriel Peralta Santana Date: Wed, 7 Feb 2024 10:29:24 -0400 Subject: [PATCH] feat(sign-up): update password-requirements tests, lint issues --- .../project.pbxproj | 4 +- ios/Podfile.lock | 2 +- src/features/sign-up/ui/SignUpForm.test.tsx | 14 +- src/features/sign-up/ui/SignUpForm.tsx | 90 ++++---- .../ui/__snapshots__/SignUpForm.test.tsx.snap | 205 ++++++++++++++++++ src/screens/ui/SignUpScreen.tsx | 2 +- src/shared/ui/InputIcon.tsx | 32 --- src/shared/ui/form/ErrorMessage.tsx | 27 +-- src/shared/ui/form/PasswordRequirements.tsx | 4 +- 9 files changed, 280 insertions(+), 100 deletions(-) create mode 100644 src/features/sign-up/ui/__snapshots__/SignUpForm.test.tsx.snap delete mode 100644 src/shared/ui/InputIcon.tsx diff --git a/ios/MindloggerMobile.xcodeproj/project.pbxproj b/ios/MindloggerMobile.xcodeproj/project.pbxproj index c4ffe2b05..e13198291 100644 --- a/ios/MindloggerMobile.xcodeproj/project.pbxproj +++ b/ios/MindloggerMobile.xcodeproj/project.pbxproj @@ -102,7 +102,7 @@ 4E9EC54929BA279C002F69A5 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4E9EC54629BA279C002F69A5 /* MaterialIcons.ttf */; }; 4E9EC54A29BA279C002F69A5 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4E9EC54629BA279C002F69A5 /* MaterialIcons.ttf */; }; 4EBCD5B22971514E00D3C04C /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4EBCD5B12971514E00D3C04C /* Foundation.ttf */; }; - 51A6E1C8BEDF3FA779731DCF /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + 51A6E1C8BEDF3FA779731DCF /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 877235D0EBDAE5907B848DAB /* libPods-MindloggerMobileCommonPods-MindloggerMobileDev.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EE7C9F67E64152DFD9983278 /* libPods-MindloggerMobileCommonPods-MindloggerMobileDev.a */; }; 90CA1E95A0B7D5AEBD875DF2 /* libPods-MindloggerMobileCommonPods-MindloggerMobileStaging.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 372995A11045EA25FEF29C24 /* libPods-MindloggerMobileCommonPods-MindloggerMobileStaging.a */; }; @@ -318,7 +318,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 51A6E1C8BEDF3FA779731DCF /* (null) in Frameworks */, + 51A6E1C8BEDF3FA779731DCF /* BuildFile in Frameworks */, 90CA1E95A0B7D5AEBD875DF2 /* libPods-MindloggerMobileCommonPods-MindloggerMobileStaging.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5a2cdf652..f165443f5 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -951,4 +951,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 3fd9f605d2ca061c942789de32928fa0ba430aa8 -COCOAPODS: 1.11.3 +COCOAPODS: 1.15.0 diff --git a/src/features/sign-up/ui/SignUpForm.test.tsx b/src/features/sign-up/ui/SignUpForm.test.tsx index 8416416e9..c8f1ad194 100644 --- a/src/features/sign-up/ui/SignUpForm.test.tsx +++ b/src/features/sign-up/ui/SignUpForm.test.tsx @@ -1,5 +1,5 @@ import { Provider } from 'react-redux'; -import renderer from 'react-test-renderer'; +import renderer, { act } from 'react-test-renderer'; import ReactQueryProvider from '@app/app/ui/AppProvider/ReactQueryProvider'; import { reduxStore } from '@app/app/ui/AppProvider/ReduxProvider'; @@ -45,7 +45,7 @@ const FormTestWrapper = ({ children }: { children: React.ReactNode }) => { ); }; -const createTest = ({}: any = {}) => { +const createTest = () => { const tree = renderer.create( {}} /> @@ -57,10 +57,12 @@ const createTest = ({}: any = {}) => { describe('SignUp Form', () => { it('should render properly', () => { - const { getRequirements } = createTest(); - const [minCharacters, noBlankSpaces] = getRequirements(); - expect(minCharacters.props.isValid).toBe(false); - expect(noBlankSpaces.props.isValid).toBe(false); + const { tree, instance } = createTest(); + act(() => { + /* display password requirements */ + instance.findByProps({ name: 'password' }).props.onFocus(); + }); + expect(tree.toJSON()).toMatchSnapshot(); }); it.todo('should validate password requirements'); diff --git a/src/features/sign-up/ui/SignUpForm.tsx b/src/features/sign-up/ui/SignUpForm.tsx index 6d9d6b782..7245ee87a 100644 --- a/src/features/sign-up/ui/SignUpForm.tsx +++ b/src/features/sign-up/ui/SignUpForm.tsx @@ -1,11 +1,17 @@ /* eslint-disable react-native/no-inline-styles */ -import { FC, useCallback, useState } from 'react'; +import { FC, useMemo, useState } from 'react'; +import { TouchableWithoutFeedback } from 'react-native'; import { FormProvider } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { isTablet } from 'react-native-device-info'; -import { executeIfOnline, useAppForm, useFormChanges } from '@app/shared/lib'; +import { + executeIfOnline, + useAppForm, + useFormChanges, + colors, +} from '@app/shared/lib'; import { Box, BoxProps, YStack, SubmitButton } from '@shared/ui'; import { InputField, @@ -16,7 +22,6 @@ import { EyeIcon, EyeSlashIcon } from '@shared/ui/icons'; import { SignUpModel } from '../'; import { SignUpFormSchema } from '../validation'; -import { TouchableWithoutFeedback } from 'react-native'; type Props = BoxProps & { onLoginSuccess: () => void; @@ -25,6 +30,8 @@ type Props = BoxProps & { const SignUpForm: FC = props => { const { t } = useTranslation(); const [isPasswordHidden, setPasswordHidden] = useState(true); + const [displayPasswordRequirements, setDisplayPasswordRequirements] = + useState(false); const { isLoading, @@ -52,25 +59,20 @@ const SignUpForm: FC = props => { }, }); - const passwordRequirements = useCallback(() => { + const passwordHasLength = !!form.getValues().password.length; + const passwordRequirements = useMemo(() => { const errors = Object.values( form.formState.errors?.password?.types || {}, ).flat(); + return [ - { - label: 'password_requirements:at_least_characters', - isValid: - form.getValues().password.length > 0 && - !errors.includes('password_requirements:at_least_characters'), - }, - { - label: 'password_requirements:no_blank_spaces', - isValid: - form.getValues().password.length > 0 && - !errors.includes('password_requirements:no_blank_spaces'), - }, - ]; - }, [form.formState.errors?.password?.types]); + 'password_requirements:at_least_characters', + 'password_requirements:no_blank_spaces', + ].map(key => ({ + label: key, + isValid: passwordHasLength && !errors.includes(key), + })); + }, [passwordHasLength, form.formState.errors?.password?.types]); const ShowPasswordIcon = isPasswordHidden ? EyeSlashIcon : EyeIcon; @@ -105,10 +107,12 @@ const SignUpForm: FC = props => { setPasswordHidden(!isPasswordHidden)} > - + } - hideError + hideError={displayPasswordRequirements || passwordHasLength} + onFocus={() => setDisplayPasswordRequirements(true)} + onBlur={() => setDisplayPasswordRequirements(false)} /> {error && ( @@ -119,29 +123,31 @@ const SignUpForm: FC = props => { error={{ message: error?.evaluatedMessage! }} /> )} - - - - - {t('sign_up_form:sign_up')} - + {(displayPasswordRequirements || passwordHasLength) && ( + + )} + + + {t('sign_up_form:sign_up')} + + ); diff --git a/src/features/sign-up/ui/__snapshots__/SignUpForm.test.tsx.snap b/src/features/sign-up/ui/__snapshots__/SignUpForm.test.tsx.snap new file mode 100644 index 000000000..30ec782a4 --- /dev/null +++ b/src/features/sign-up/ui/__snapshots__/SignUpForm.test.tsx.snap @@ -0,0 +1,205 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SignUp Form should render properly 1`] = ` +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+ +  + +
+
+
+ + password_requirements:must_include + + + + + x + + + password_requirements:at_least_characters + + + + + x + + + password_requirements:no_blank_spaces + + + + +
+ + sign_up_form:sign_up + +
+
+
+`; diff --git a/src/screens/ui/SignUpScreen.tsx b/src/screens/ui/SignUpScreen.tsx index 15323697b..94b80d22a 100644 --- a/src/screens/ui/SignUpScreen.tsx +++ b/src/screens/ui/SignUpScreen.tsx @@ -34,7 +34,7 @@ const SignUpScreen: FC = () => { - + diff --git a/src/shared/ui/InputIcon.tsx b/src/shared/ui/InputIcon.tsx deleted file mode 100644 index 7d9bdd1fd..000000000 --- a/src/shared/ui/InputIcon.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { View, StyleSheet } from 'react-native'; - -import { IconProps } from 'react-native-vector-icons/Icon'; - -export type InputIconProps = Partial & { - iconProps?: Partial; - input: React.ReactNode; - icon: React.ReactNode; -}; - -const styles = StyleSheet.create({ - inputContainer: { - justifyContent: 'center', - }, - input: { - height: 'auto', - }, - icon: { - position: 'absolute', - right: 10, - }, -}); - -export function InputIcon({ input, icon }: InputIconProps) { - return ( - - {input} - {icon} - - ); -} diff --git a/src/shared/ui/form/ErrorMessage.tsx b/src/shared/ui/form/ErrorMessage.tsx index 0f2548bfb..52c0afc0f 100644 --- a/src/shared/ui/form/ErrorMessage.tsx +++ b/src/shared/ui/form/ErrorMessage.tsx @@ -16,19 +16,20 @@ const ErrorMessage: FC> = ({ ...props }) => { const { t } = useTranslation(); - const renderError = (err: string, params?: any) => { - return ( - - {t(err, params)} - - ); - }; - - return <>{error?.message && renderError(error?.message)}; + return ( + <> + {!!error?.message && ( + + {/* @ts-ignore */} + {t(error.message, error.params)} + + )} + + ); }; export default ErrorMessage; diff --git a/src/shared/ui/form/PasswordRequirements.tsx b/src/shared/ui/form/PasswordRequirements.tsx index 088e5731a..9819ace23 100644 --- a/src/shared/ui/form/PasswordRequirements.tsx +++ b/src/shared/ui/form/PasswordRequirements.tsx @@ -85,9 +85,7 @@ export const PasswordRequirements = ({ const { textColor } = themeColors.valid; return ( <> - - {t('password_requirements:must_include')} - + {t('password_requirements:must_include')} {requirements.map(requirement => (