From 452bf5b3e97b2c1abbbea1d0018ec16d6002e72a Mon Sep 17 00:00:00 2001 From: Pedro Tiburcio Date: Wed, 12 Mar 2025 10:18:09 -0400 Subject: [PATCH 1/3] feat: migrate change password from baseapp-frontend-template --- .../authentication/modules/access/index.ts | 4 +- .../__tests__/useChangePassword.test.tsx} | 72 ++++++++++++++++--- .../constants.ts | 4 +- .../index.ts | 14 ++-- .../types.ts | 12 ++-- packages/authentication/services/auth.ts | 8 +++ packages/authentication/types/auth.ts | 5 +- 7 files changed, 91 insertions(+), 28 deletions(-) rename packages/authentication/modules/access/{useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx => useChangePassword/__tests__/useChangePassword.test.tsx} (67%) rename packages/authentication/modules/access/{useChangeExpiredPassword => useChangePassword}/constants.ts (84%) rename packages/authentication/modules/access/{useChangeExpiredPassword => useChangePassword}/index.ts (77%) rename packages/authentication/modules/access/{useChangeExpiredPassword => useChangePassword}/types.ts (55%) diff --git a/packages/authentication/modules/access/index.ts b/packages/authentication/modules/access/index.ts index 942bc185..b029e310 100644 --- a/packages/authentication/modules/access/index.ts +++ b/packages/authentication/modules/access/index.ts @@ -16,5 +16,5 @@ export type * from './useResetPassword/types' export { default as useSignUp } from './useSignUp' export type * from './useSignUp/types' -export { default as useChangeExpiredPassword } from './useChangeExpiredPassword' -export type * from './useChangeExpiredPassword/types' +export { default as useChangePassword } from './useChangePassword' +export type * from './useChangePassword/types' diff --git a/packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx similarity index 67% rename from packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx rename to packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx index 2cf63513..512d08f2 100644 --- a/packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx +++ b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx @@ -7,12 +7,16 @@ import { import { z } from 'zod' -import useChangeExpiredPassword from '../index' +import useChangePassword from '../index' -describe('useChangeExpiredPassword', () => { +describe('useChangePassword', () => { const currentPassword = '1234' +<<<<<<< HEAD:packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx const password = '12345#Abcde' const token = 'fake-token' +======= + const password = '123456' +>>>>>>> 3034abb (feat: migrate change password from baseapp-frontend-template):packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx const changePasswordUrl = '/change-expired-password' afterEach(() => { @@ -27,7 +31,6 @@ describe('useChangeExpiredPassword', () => { response: { currentPassword, newPassword: password, - token, }, }) @@ -35,8 +38,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: { currentPassword, newPassword: password, @@ -66,8 +68,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: { currentPassword, newPassword: password, @@ -101,8 +102,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: { currentPassword, newPassword: password, @@ -148,8 +148,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: customDefaultValues, validationSchema: customValidationSchema, options: { @@ -168,3 +167,54 @@ describe('useChangeExpiredPassword', () => { expect(hasOnSuccessRan).toBe(true) }) }) + +describe('useChangePassword with token for expired passwords', () => { + const currentPassword = '1234' + const password = '123456' + const token = 'fake-token' + const changePasswordUrl = '/change-expired-password' + + afterEach(() => { + ;(global.fetch as jest.Mock).mockClear() // Clear the mock between tests + }) + + // This is just to ensure that running with token has the same behavior as running without token + test('should run onSuccess', async () => { + // Mock the fetch call with a success response for POST method + mockFetch(changePasswordUrl, { + method: 'POST', + status: 200, + response: { + currentPassword, + newPassword: password, + token, + }, + }) + + let hasOnSuccessRan = false + + const { result } = renderHook( + () => + useChangePassword({ + token, + defaultValues: { + currentPassword, + newPassword: password, + confirmNewPassword: password, + }, + options: { + onSuccess: () => { + hasOnSuccessRan = true + }, + }, + }), + { + wrapper: ComponentWithProviders, + }, + ) + + await result.current.form.handleSubmit() + + expect(hasOnSuccessRan).toBe(true) + }) +}) diff --git a/packages/authentication/modules/access/useChangeExpiredPassword/constants.ts b/packages/authentication/modules/access/useChangePassword/constants.ts similarity index 84% rename from packages/authentication/modules/access/useChangeExpiredPassword/constants.ts rename to packages/authentication/modules/access/useChangePassword/constants.ts index e21f4e8b..73851133 100644 --- a/packages/authentication/modules/access/useChangeExpiredPassword/constants.ts +++ b/packages/authentication/modules/access/useChangePassword/constants.ts @@ -2,7 +2,7 @@ import { PASSWORD_REGEX, ZOD_MESSAGE } from '@baseapp-frontend/utils' import { z } from 'zod' -import type { ChangeExpiredPasswordForm } from './types' +import type { ChangePasswordForm } from './types' export const DEFAULT_VALIDATION_SCHEMA = z .object({ @@ -17,7 +17,7 @@ export const DEFAULT_VALIDATION_SCHEMA = z path: ['confirmNewPassword'], }) -export const DEFAULT_INITIAL_VALUES: ChangeExpiredPasswordForm = { +export const DEFAULT_INITIAL_VALUES: ChangePasswordForm = { currentPassword: '', newPassword: '', confirmNewPassword: '', diff --git a/packages/authentication/modules/access/useChangeExpiredPassword/index.ts b/packages/authentication/modules/access/useChangePassword/index.ts similarity index 77% rename from packages/authentication/modules/access/useChangeExpiredPassword/index.ts rename to packages/authentication/modules/access/useChangePassword/index.ts index 87b8b104..feaaf1b7 100644 --- a/packages/authentication/modules/access/useChangeExpiredPassword/index.ts +++ b/packages/authentication/modules/access/useChangePassword/index.ts @@ -6,16 +6,16 @@ import { type SubmitHandler, useForm } from 'react-hook-form' import AuthApi from '../../../services/auth' import { DEFAULT_INITIAL_VALUES, DEFAULT_VALIDATION_SCHEMA } from './constants' -import type { ChangeExpiredPasswordForm, UseChangeExpiredPassword } from './types' +import type { ChangePasswordForm, UseChangePassword } from './types' -const useChangeExpiredPassword = ({ +const useChangePassword = ({ token, validationSchema = DEFAULT_VALIDATION_SCHEMA, defaultValues = DEFAULT_INITIAL_VALUES, ApiClass = AuthApi, enableFormApiErrors = true, options = {}, -}: UseChangeExpiredPassword) => { +}: UseChangePassword) => { const form = useForm({ defaultValues, resolver: zodResolver(validationSchema), @@ -24,7 +24,9 @@ const useChangeExpiredPassword = ({ const mutation = useMutation({ mutationFn: ({ currentPassword, newPassword }) => - ApiClass.changeExpiredPassword({ currentPassword, newPassword, token }), + token + ? ApiClass.changeExpiredPassword({ currentPassword, newPassword, token }) + : ApiClass.changePassword({ currentPassword, newPassword }), ...options, // needs to be placed below all overridable options onError: (err, variables, context) => { options?.onError?.(err, variables, context) @@ -37,7 +39,7 @@ const useChangeExpiredPassword = ({ }, }) - const handleSubmit: SubmitHandler = async (values) => { + const handleSubmit: SubmitHandler = async (values) => { try { await mutation.mutateAsync(values) } catch (error) { @@ -55,4 +57,4 @@ const useChangeExpiredPassword = ({ } } -export default useChangeExpiredPassword +export default useChangePassword diff --git a/packages/authentication/modules/access/useChangeExpiredPassword/types.ts b/packages/authentication/modules/access/useChangePassword/types.ts similarity index 55% rename from packages/authentication/modules/access/useChangeExpiredPassword/types.ts rename to packages/authentication/modules/access/useChangePassword/types.ts index e6a09e27..6488f866 100644 --- a/packages/authentication/modules/access/useChangeExpiredPassword/types.ts +++ b/packages/authentication/modules/access/useChangePassword/types.ts @@ -3,19 +3,19 @@ import { z } from 'zod' import AuthApi from '../../../services/auth' -type ApiClass = Pick +type ApiClass = Pick -export type ChangeExpiredPasswordForm = { +export type ChangePasswordForm = { currentPassword: string newPassword: string confirmNewPassword: string } -export interface UseChangeExpiredPassword { - token: string +export interface UseChangePassword { + token?: string validationSchema?: z.ZodObject | z.ZodEffects> - defaultValues?: ChangeExpiredPasswordForm - options?: UseMutationOptions + defaultValues?: ChangePasswordForm + options?: UseMutationOptions ApiClass?: ApiClass enableFormApiErrors?: boolean } diff --git a/packages/authentication/services/auth.ts b/packages/authentication/services/auth.ts index f689ee46..38016a36 100644 --- a/packages/authentication/services/auth.ts +++ b/packages/authentication/services/auth.ts @@ -2,6 +2,7 @@ import { baseAppFetch } from '@baseapp-frontend/utils' import type { ChangeExpiredPasswordRequest, + ChangePasswordRequest, ForgotPasswordRequest, LoginRequest, LoginResponse, @@ -26,6 +27,13 @@ export default class AuthApi { return baseAppFetch(`/register`, { method: 'POST', body: request }) } + static changePassword({ currentPassword, newPassword }: ChangePasswordRequest) { + return baseAppFetch('/users/change-password', { + method: 'POST', + body: { currentPassword, newPassword }, + }) + } + static changeExpiredPassword({ currentPassword, newPassword, diff --git a/packages/authentication/types/auth.ts b/packages/authentication/types/auth.ts index a64ac1bd..f12f8cfd 100644 --- a/packages/authentication/types/auth.ts +++ b/packages/authentication/types/auth.ts @@ -49,8 +49,11 @@ export interface CustomJWTKeyNames { refreshKeyName?: string } -export interface ChangeExpiredPasswordRequest { +export interface ChangePasswordRequest { currentPassword: string newPassword: string +} + +export interface ChangeExpiredPasswordRequest extends ChangePasswordRequest { token: string } From fcefd834a66b578b66db14e8c31ec50aad3d28da Mon Sep 17 00:00:00 2001 From: Pedro Tiburcio Date: Wed, 12 Mar 2025 14:07:36 -0400 Subject: [PATCH 2/3] feat: add AlertTriangleIcon to native --- .../__tests__/useChangePassword.test.tsx | 7 +++- .../native/icons/AlertTriangleIcon/index.tsx | 32 +++++++++++++++++++ .../components/native/icons/index.ts | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 packages/design-system/components/native/icons/AlertTriangleIcon/index.tsx diff --git a/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx index 512d08f2..9348d836 100644 --- a/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx +++ b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx @@ -11,6 +11,7 @@ import useChangePassword from '../index' describe('useChangePassword', () => { const currentPassword = '1234' +<<<<<<< HEAD <<<<<<< HEAD:packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx const password = '12345#Abcde' const token = 'fake-token' @@ -18,6 +19,10 @@ describe('useChangePassword', () => { const password = '123456' >>>>>>> 3034abb (feat: migrate change password from baseapp-frontend-template):packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx const changePasswordUrl = '/change-expired-password' +======= + const password = 'abcABC@123456' + const changePasswordUrl = '/users/change-password' +>>>>>>> 426f513 (feat: add AlertTriangleIcon to native) afterEach(() => { ;(global.fetch as jest.Mock).mockClear() // Clear the mock between tests @@ -170,7 +175,7 @@ describe('useChangePassword', () => { describe('useChangePassword with token for expired passwords', () => { const currentPassword = '1234' - const password = '123456' + const password = 'abcABC@123456' const token = 'fake-token' const changePasswordUrl = '/change-expired-password' diff --git a/packages/design-system/components/native/icons/AlertTriangleIcon/index.tsx b/packages/design-system/components/native/icons/AlertTriangleIcon/index.tsx new file mode 100644 index 00000000..8b374a56 --- /dev/null +++ b/packages/design-system/components/native/icons/AlertTriangleIcon/index.tsx @@ -0,0 +1,32 @@ +import { FC } from 'react' + +import Svg, { Path } from 'react-native-svg' + +import { useTheme } from '../../../../providers/native' +import { SvgIconProps } from '../types' + +const AlertTriangleIcon: FC = ({ + isActive = false, + color, + width = '24', + height = '25', + ...props +}) => { + const { colors } = useTheme() + + const defaultColor = color ?? colors.object.high + const svgColor = isActive ? colors.primary.high : defaultColor + + return ( + + + + ) +} + +export default AlertTriangleIcon diff --git a/packages/design-system/components/native/icons/index.ts b/packages/design-system/components/native/icons/index.ts index 1776fd12..44c522de 100644 --- a/packages/design-system/components/native/icons/index.ts +++ b/packages/design-system/components/native/icons/index.ts @@ -1,3 +1,4 @@ +export { default as AlertTriangleIcon } from './AlertTriangleIcon' export { default as BellIcon } from './BellIcon' export { default as BiometricsIcon } from './BiometricsIcon' export { default as BlockIcon } from './BlockIcon' From 4b91e0534cd517a0176a3c800fb5c12608e9b499 Mon Sep 17 00:00:00 2001 From: Pedro Tiburcio Date: Tue, 18 Mar 2025 10:19:24 -0400 Subject: [PATCH 3/3] feat: add wrap to erro container on native TextInput --- .../__tests__/useChangePassword.test.tsx | 10 ---------- .../components/native/inputs/TextInput/index.tsx | 13 +++++++++++-- .../components/native/inputs/TextInput/styles.ts | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx index 9348d836..42897cc8 100644 --- a/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx +++ b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx @@ -11,18 +11,8 @@ import useChangePassword from '../index' describe('useChangePassword', () => { const currentPassword = '1234' -<<<<<<< HEAD -<<<<<<< HEAD:packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx const password = '12345#Abcde' - const token = 'fake-token' -======= - const password = '123456' ->>>>>>> 3034abb (feat: migrate change password from baseapp-frontend-template):packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx - const changePasswordUrl = '/change-expired-password' -======= - const password = 'abcABC@123456' const changePasswordUrl = '/users/change-password' ->>>>>>> 426f513 (feat: add AlertTriangleIcon to native) afterEach(() => { ;(global.fetch as jest.Mock).mockClear() // Clear the mock between tests diff --git a/packages/design-system/components/native/inputs/TextInput/index.tsx b/packages/design-system/components/native/inputs/TextInput/index.tsx index fdf8c1dd..d5071561 100644 --- a/packages/design-system/components/native/inputs/TextInput/index.tsx +++ b/packages/design-system/components/native/inputs/TextInput/index.tsx @@ -1,6 +1,7 @@ import { FC, useState } from 'react' import { Ionicons } from '@expo/vector-icons' +import { LayoutChangeEvent } from 'react-native' import { TextInput as PaperTextInput } from 'react-native-paper' import { useTheme } from '../../../../providers/native' @@ -14,8 +15,14 @@ const TextInput: FC = (props) => { const { disabled, helperText } = props const [isFocused, setIsFocused] = useState(false) + const [errorContainerWidth, setErrorContainerWidth] = useState(0) const theme = useTheme() + const onLayout = (event: LayoutChangeEvent) => { + const { width } = event.nativeEvent.layout + setErrorContainerWidth(width) + } + const outlinedStyles = createOutlinedStyles(theme, { isFocused, isError: !!helperText, @@ -23,7 +30,7 @@ const TextInput: FC = (props) => { }) return ( - + setIsFocused(true)} onBlur={() => setIsFocused(false)} @@ -32,7 +39,9 @@ const TextInput: FC = (props) => { {...props} /> {helperText && !disabled && ( - + // Had to do this adjustment to the error container width because the error text was overflowing the container + // The 12px subtraction is to account for the padding of the error container + {helperText} diff --git a/packages/design-system/components/native/inputs/TextInput/styles.ts b/packages/design-system/components/native/inputs/TextInput/styles.ts index 8509d73e..a958c473 100644 --- a/packages/design-system/components/native/inputs/TextInput/styles.ts +++ b/packages/design-system/components/native/inputs/TextInput/styles.ts @@ -53,7 +53,7 @@ export const styles = StyleSheet.create({ gap: 8, }, errorContainer: { - alignItems: 'center', + alignItems: 'flex-start', flexDirection: 'row', gap: 4, paddingLeft: 12,