Skip to content

Commit

Permalink
Update handle validate in PasswordInput
Browse files Browse the repository at this point in the history
  • Loading branch information
OKendigelyan committed Sep 25, 2024
1 parent 88817cc commit 994249b
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type ChangePasswordFormValues = {

export const ChangePasswordForm = () => {
const { onClose } = useDynamicModalContext();
const form = useForm<ChangePasswordFormValues>({ mode: "onBlur" });
const form = useForm<ChangePasswordFormValues>({ mode: "all" });
const toast = useToast();
const dispatch = useAppDispatch();
const { handleAsyncAction, isLoading } = useAsyncActionHandler();
Expand Down Expand Up @@ -66,6 +66,7 @@ export const ChangePasswordForm = () => {
data-testid="current-password"
inputName="currentPassword"
label="Current Password"
minLength={0}
placeholder="Enter your current password"
required="Current password is required"
/>
Expand All @@ -78,6 +79,7 @@ export const ChangePasswordForm = () => {

<FormControl isInvalid={!!errors.newPassword} marginY={6}>
<PasswordInput
checkPasswordStrength
data-testid="new-password"
inputName="newPassword"
label="New Password"
Expand All @@ -99,6 +101,7 @@ export const ChangePasswordForm = () => {
data-testid="new-password-confirmation"
inputName="newPasswordConfirmation"
label="Confirm New Password"
minLength={0}
placeholder="Confirm new password"
required="Confirmation is required"
validate={(val: string) =>
Expand Down
21 changes: 18 additions & 3 deletions apps/desktop/src/components/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { type FieldValues, type Path, type RegisterOptions, useFormContext } fro
import { EyeIcon, EyeSlashIcon } from "../assets/icons";
import colors from "../style/colors";

const MIN_LENGTH = 8;
const MIN_LENGTH = 12;

// <T extends FieldValues> is needed to be compatible with the useForm's type parameter (FormData)
// <U extends Path<T>> makes sure that we can pass in only valid inputName that exists in FormData
Expand All @@ -24,7 +24,7 @@ type PasswordInputProps<T extends FieldValues, U extends Path<T>> = {
checkPasswordStrength?: boolean;
required?: string | boolean;
minLength?: RegisterOptions<T, U>["minLength"];
validate?: RegisterOptions<T, U>["validate"];
validate?: (val: string) => string | boolean;
} & InputProps;

// TODO: add an error message and make it nested under FormControl
Expand All @@ -42,8 +42,23 @@ export const PasswordInput = <T extends FieldValues, U extends Path<T>>({
const [showPassword, setShowPassword] = useState<boolean>(false);
const { validatePassword, PasswordStrengthBar } = usePasswordValidation({
colorScheme: colors.gray[500],
inputName,
});

const handleValidate = (val: string) => {
if (validate) {
const validateResult = validate(val);

if (checkPasswordStrength && validateResult === true) {
return validatePassword(val);
}

return validateResult;
} else if (checkPasswordStrength) {
return validatePassword(val);
}
};

return (
<>
<FormLabel>{label}</FormLabel>
Expand All @@ -62,7 +77,7 @@ export const PasswordInput = <T extends FieldValues, U extends Path<T>>({
message: `Your password must be at least ${minLength} characters long`,
}
: undefined,
validate: checkPasswordStrength ? validatePassword : validate,
validate: handleValidate,
})}
{...rest}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type ChangePasswordMenuValues = {
};

export const ChangePasswordMenu = () => {
const form = useForm<ChangePasswordMenuValues>({ mode: "onBlur" });
const form = useForm<ChangePasswordMenuValues>({ mode: "all" });
const toast = useToast();
const dispatch = useAppDispatch();
const { handleAsyncAction, isLoading } = useAsyncActionHandler();
Expand All @@ -37,11 +37,13 @@ export const ChangePasswordMenu = () => {
data-testid="current-password"
inputName="currentPassword"
label="Current Password"
minLength={0}
placeholder="Your password"
required="Current password is required"
/>
<Divider />
<PasswordInput
checkPasswordStrength
data-testid="new-password"
inputName="newPassword"
label="New Password"
Expand All @@ -55,6 +57,7 @@ export const ChangePasswordMenu = () => {
data-testid="new-password-confirmation"
inputName="newPasswordConfirmation"
label="Confirm password"
minLength={0}
placeholder="Confirm password"
required="Confirmation is required"
validate={(val: string) =>
Expand Down
22 changes: 19 additions & 3 deletions apps/web/src/components/PasswordInput/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type PasswordInputProps<T extends FieldValues, U extends Path<T>> = {
required?: string | boolean;
checkPasswordStrength?: boolean;
minLength?: RegisterOptions<T, U>["minLength"];
validate?: RegisterOptions<T, U>["validate"];
validate?: (val: string) => string | boolean;
} & InputProps & {
"data-testid"?: string;
};
Expand All @@ -46,13 +46,29 @@ export const PasswordInput = <T extends FieldValues, U extends Path<T>>({
formState: { errors },
} = useFormContext<T>();
const [showPassword, setShowPassword] = useState<boolean>(false);
const { validatePassword, PasswordStrengthBar } = usePasswordValidation();
const { validatePassword, PasswordStrengthBar } = usePasswordValidation({
inputName,
});

const color = useColor();

const error = errors[inputName];
const errorMessage = error?.message as string;

const handleValidate = (val: string) => {
if (validate) {
const validateResult = validate(val);

if (checkPasswordStrength && validateResult === true) {
return validatePassword(val);
}

return validateResult;
} else if (checkPasswordStrength) {
return validatePassword(val);
}
};

return (
<FormControl isInvalid={!!error}>
<FormLabel>{label}</FormLabel>
Expand All @@ -71,7 +87,7 @@ export const PasswordInput = <T extends FieldValues, U extends Path<T>>({
message: `Your password must be at least ${minLength} characters long`,
}
: undefined,
validate: checkPasswordStrength ? validatePassword : validate,
validate: handleValidate,
})}
{...rest}
/>
Expand Down
13 changes: 9 additions & 4 deletions packages/components/src/hooks/usePasswordValidation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ const DEFAULT_COLOR_SCHEME = "gray.100";
type PasswordStrengthBarProps = {
score: number;
colorScheme: string;
inputName: string;
};

const PasswordStrengthBar = ({ score, colorScheme }: PasswordStrengthBarProps) => {
const PasswordStrengthBar = ({ score, colorScheme, inputName }: PasswordStrengthBarProps) => {
const form = useFormContext();

const colors = [colorScheme, "red.500", "yellow.500", "green.500"];
const passwordError = form.formState.errors.password;
const passwordError = form.formState.errors[inputName];

const getColor = (index: number) => {
switch (score) {
Expand Down Expand Up @@ -76,15 +77,17 @@ const PasswordStrengthBar = ({ score, colorScheme }: PasswordStrengthBarProps) =

type UsePasswordValidationProps = {
colorScheme?: string;
inputName?: string;
};

export const usePasswordValidation = ({
colorScheme = DEFAULT_COLOR_SCHEME,
inputName = "password",
}: UsePasswordValidationProps = {}) => {
const [passwordScore, setPasswordScore] = useState(DEFAULT_SCORE);
const form = useFormContext();

const passwordError = form.formState.errors.password;
const passwordError = form.formState.errors[inputName];

useEffect(() => {
if (passwordError?.type === "required") {
Expand All @@ -105,6 +108,8 @@ export const usePasswordValidation = ({

return {
validatePassword,
PasswordStrengthBar: <PasswordStrengthBar colorScheme={colorScheme} score={passwordScore} />,
PasswordStrengthBar: (
<PasswordStrengthBar colorScheme={colorScheme} inputName={inputName} score={passwordScore} />
),
};
};

0 comments on commit 994249b

Please sign in to comment.