diff --git a/packages/authentication/CHANGELOG.md b/packages/authentication/CHANGELOG.md index 6e818498..7a19028c 100644 --- a/packages/authentication/CHANGELOG.md +++ b/packages/authentication/CHANGELOG.md @@ -1,5 +1,12 @@ # @baseapp-frontend/authentication +## 4.2.1 + +### Patch Changes + +- Rename `useChangeExpiredPassword` to `useChangePassword` +- Modify `useChangePassword` to include the `/change-password` endpoint + ## 4.2.0 ### Minor changes @@ -7,7 +14,7 @@ - Enhanced the registration process to support separate first and last name inputs, improving user profile detail. - Introduced a configurable option that allows toggling between separate name fields and a consolidated name field. - Updated form validation to ensure correct input for the revised registration fields. -- If you're using `useSignUp` with name only, you should set `useNameField` to `true` on the hook's option +- If you're using `useSignUp` with name only, you should set `useNameField` to `true` on the hook's option ## 4.1.9 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 72% rename from packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx rename to packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx index 2cf63513..42897cc8 100644 --- a/packages/authentication/modules/access/useChangeExpiredPassword/__tests__/useChangeExpiredPassword.test.tsx +++ b/packages/authentication/modules/access/useChangePassword/__tests__/useChangePassword.test.tsx @@ -7,13 +7,12 @@ import { import { z } from 'zod' -import useChangeExpiredPassword from '../index' +import useChangePassword from '../index' -describe('useChangeExpiredPassword', () => { +describe('useChangePassword', () => { const currentPassword = '1234' const password = '12345#Abcde' - const token = 'fake-token' - const changePasswordUrl = '/change-expired-password' + const changePasswordUrl = '/users/change-password' afterEach(() => { ;(global.fetch as jest.Mock).mockClear() // Clear the mock between tests @@ -27,7 +26,6 @@ describe('useChangeExpiredPassword', () => { response: { currentPassword, newPassword: password, - token, }, }) @@ -35,8 +33,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: { currentPassword, newPassword: password, @@ -66,8 +63,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: { currentPassword, newPassword: password, @@ -101,8 +97,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: { currentPassword, newPassword: password, @@ -148,8 +143,7 @@ describe('useChangeExpiredPassword', () => { const { result } = renderHook( () => - useChangeExpiredPassword({ - token, + useChangePassword({ defaultValues: customDefaultValues, validationSchema: customValidationSchema, options: { @@ -168,3 +162,54 @@ describe('useChangeExpiredPassword', () => { expect(hasOnSuccessRan).toBe(true) }) }) + +describe('useChangePassword with token for expired passwords', () => { + const currentPassword = '1234' + const password = 'abcABC@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/package.json b/packages/authentication/package.json index 28e8ab49..497fea18 100644 --- a/packages/authentication/package.json +++ b/packages/authentication/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/authentication", "description": "Authentication modules.", - "version": "4.2.0", + "version": "4.2.1", "main": "./index.ts", "types": "dist/index.d.ts", "sideEffects": false, 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 df3153c4..b050252d 100644 --- a/packages/authentication/types/auth.ts +++ b/packages/authentication/types/auth.ts @@ -51,8 +51,11 @@ export interface CustomJWTKeyNames { refreshKeyName?: string } -export interface ChangeExpiredPasswordRequest { +export interface ChangePasswordRequest { currentPassword: string newPassword: string +} + +export interface ChangeExpiredPasswordRequest extends ChangePasswordRequest { token: string } diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index f81c714a..a780f79b 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,13 @@ # @baseapp-frontend/components +## 1.0.28 + +### Patch Changes + +- Updated dependencies + - @baseapp-frontend/authentication@4.2.1 + - @baseapp-frontend/design-system@1.0.11 + ## 1.0.27 ### Patch Changes diff --git a/packages/components/package.json b/packages/components/package.json index 46b13191..037c197d 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/components", "description": "BaseApp components modules such as comments, notifications, messages, and more.", - "version": "1.0.27", + "version": "1.0.28", "sideEffects": false, "scripts": { "babel:transpile": "babel modules -d tmp-babel --extensions .ts,.tsx --ignore '**/__tests__/**','**/__storybook__/**'", diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index fe48740a..3409773a 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -1,5 +1,15 @@ # @baseapp-frontend/design-system +## 1.0.11 + +### Patch Changes + +- Create `ScrollView` component with support for `KeyboardAvoidingView` functionality +- Modified native `TextInput` to adjust the error message view so it doesn't overflow offscreen +- Added `AlertTriangleIcon` to native components +- Fixed text color for `Button` `outlined` variant + + ## 1.0.10 ### Patch Changes diff --git a/packages/design-system/components/native/buttons/Button/styles.ts b/packages/design-system/components/native/buttons/Button/styles.ts index 36679857..fc5a7445 100644 --- a/packages/design-system/components/native/buttons/Button/styles.ts +++ b/packages/design-system/components/native/buttons/Button/styles.ts @@ -51,7 +51,7 @@ export const createOutlinedStyles = ( { disabled, variant }: ButtonStylesOptions, ) => { const borderColor = variant === 'inherit' ? colors.surface.border : colors[variant].low - const textColor = variant === 'inherit' ? colors.object.contrast : colors[variant].high + const textColor = variant === 'inherit' ? colors.object.high : colors[variant].high return StyleSheet.create({ ...baseButtonStyles, @@ -69,7 +69,7 @@ export const createOutlinedStyles = ( export const createSoftStyles = ({ colors }: Theme, { disabled, variant }: ButtonStylesOptions) => { const backgroundColor = variant === 'inherit' ? colors.surface.active : colors[variant].overlay const textColor = variant === 'inherit' ? colors.object.high : colors[variant].high - // if variant inherid display no shadow to the button + // if variant inherit display no shadow to the button return StyleSheet.create({ ...baseButtonStyles, wrapper: { 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' 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, diff --git a/packages/design-system/components/native/views/ScrollView/index.tsx b/packages/design-system/components/native/views/ScrollView/index.tsx new file mode 100644 index 00000000..858a0f3d --- /dev/null +++ b/packages/design-system/components/native/views/ScrollView/index.tsx @@ -0,0 +1,29 @@ +import { FC } from 'react' + +import { KeyboardAvoidingView } from 'react-native' +import { ScrollView as NativeScrollView } from 'react-native-gesture-handler' + +import { createStyles } from './styles' +import type { ScrollViewProps } from './types' + +const ScrollView: FC = ({ style, avoidKeyboard = true, children, ...props }) => { + const styles = createStyles() + + if (avoidKeyboard) { + return ( + + + {children} + + + ) + } + + return ( + + {children} + + ) +} + +export default ScrollView diff --git a/packages/design-system/components/native/views/ScrollView/styles.ts b/packages/design-system/components/native/views/ScrollView/styles.ts new file mode 100644 index 00000000..6fbc1619 --- /dev/null +++ b/packages/design-system/components/native/views/ScrollView/styles.ts @@ -0,0 +1,12 @@ +import { StyleSheet } from 'react-native' + +export const createStyles = () => + StyleSheet.create({ + scrollViewcontainer: { + padding: 0, + flex: 1, + }, + keyboardHideContainer: { + flex: 1, + }, + }) diff --git a/packages/design-system/components/native/views/ScrollView/types.ts b/packages/design-system/components/native/views/ScrollView/types.ts new file mode 100644 index 00000000..7a90b4e5 --- /dev/null +++ b/packages/design-system/components/native/views/ScrollView/types.ts @@ -0,0 +1,7 @@ +import { ComponentProps } from 'react' + +import { ScrollView } from 'react-native' + +export interface ScrollViewProps extends ComponentProps { + avoidKeyboard?: boolean +} diff --git a/packages/design-system/components/native/views/index.ts b/packages/design-system/components/native/views/index.ts index c4ac9eee..da23c5ff 100644 --- a/packages/design-system/components/native/views/index.ts +++ b/packages/design-system/components/native/views/index.ts @@ -6,5 +6,7 @@ export { default as PageViewWithHeader } from './PageViewWithHeader' export type * from './PageViewWithHeader/types' export { default as ParallaxScrollView } from './ParallaxScrollView' export type * from './ParallaxScrollView/types' +export { default as ScrollView } from './ScrollView' +export type * from './ScrollView/types' export { default as View } from './View' export type * from './View/types' diff --git a/packages/design-system/package.json b/packages/design-system/package.json index b0f74505..6ab06bc6 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,7 +1,7 @@ { "name": "@baseapp-frontend/design-system", "description": "Design System components and configurations.", - "version": "1.0.10", + "version": "1.0.11", "sideEffects": false, "scripts": { "tsup:bundle": "tsup --tsconfig tsconfig.build.json",