Skip to content

Commit

Permalink
Merge pull request #168 from deriv-com/shayan/add-custom-error-messag…
Browse files Browse the repository at this point in the history
…e-to-password-input

[FEQ] / Shayan/ FEQ-2122 / add custom error message to passwordInput component
  • Loading branch information
shayan-deriv authored Apr 24, 2024
2 parents 96d11eb + b6c4e52 commit 292b23a
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/components/PasswordInput/PasswordConstants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type TScore = 0 | 1 | 2 | 3 | 4;
export type TScore = 0 | 1 | 2 | 3 | 4 | 5;

export type passwordKeys =
| "common"
Expand Down
1 change: 1 addition & 0 deletions src/components/PasswordInput/PasswordMeter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const PasswordStrengthClass: Record<TScore, string> = {
2: "deriv-password__meter__bar--moderate",
3: "deriv-password__meter__bar--strong",
4: "deriv-password__meter__bar--complete",
5: "deriv-password__meter__bar--error",
};

export const PasswordMeter = ({ score }: PasswordMeterProps) => (
Expand Down
3 changes: 2 additions & 1 deletion src/components/PasswordInput/PasswordUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ export const isPasswordStrong = (password: string) => {
);
};

export const calculateScore = (password: string) => {
export const calculateScore = (password: string, customErrorMessage = "") => {
if (password?.length === 0) return 0;
if (customErrorMessage) return 5;
if (!isPasswordValid(password)) return 1;
if (
!isPasswordStrong(password) &&
Expand Down
26 changes: 20 additions & 6 deletions src/components/PasswordInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {
ComponentProps,
FocusEvent,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
Expand All @@ -22,23 +23,28 @@ import { EyeIcon, EyeIconSlash } from "./PasswordIcon";
import { PasswordMeter } from "./PasswordMeter";
import "./PasswordInput.scss";

export const validatePassword = (password: string) => {
const score = calculateScore(password);
export const validatePassword = (password: string, customErrorMessage = "") => {
const score = calculateScore(password, customErrorMessage);
let errorMessage = "";

const options = { dictionary: { ...dictionary } };
zxcvbnOptions.setOptions(options);
if(!password){
if (!password) {
return { errorMessage, score };
}
const { feedback } = zxcvbn(password);
if (!passwordRegex.isLengthValid.test(password)) {
errorMessage = passwordErrorMessage.invalidLength;
} else if (!isPasswordValid(password)) {
errorMessage = passwordErrorMessage.missingCharacter;
} else {
}
else if (customErrorMessage) {
errorMessage = customErrorMessage;
}
else {
errorMessage = warningMessages[feedback.warning as passwordKeys] ?? "";
}

return { errorMessage, score };
};

Expand All @@ -47,6 +53,7 @@ type InputProps = ComponentProps<typeof Input>;
interface PasswordInputProps extends Omit<InputProps, "rightPlaceholder"> {
hidePasswordMeter?: boolean;
hint?: string;
customErrorMessage?: string;
}

const PasswordVariant: Record<TScore, InputProps["variant"]> = {
Expand All @@ -55,6 +62,7 @@ const PasswordVariant: Record<TScore, InputProps["variant"]> = {
2: "warning",
3: "success",
4: "success",
5: "error",
};

/**
Expand Down Expand Up @@ -86,19 +94,25 @@ export const PasswordInput = ({
onBlur,
onChange,
value,
customErrorMessage = "",
...rest
}: PasswordInputProps) => {
useEffect(() => {
setBackendErrorMessage(customErrorMessage);
}, [customErrorMessage]);
const [isTouched, setIsTouched] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [backendErrorMessage, setBackendErrorMessage] = useState(customErrorMessage);

const { errorMessage, score } = useMemo(
() => validatePassword(value as string),
[value],
() => validatePassword(value as string, backendErrorMessage),
[value, backendErrorMessage],
);

const handleChange = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
onChange?.(e);
setBackendErrorMessage("");
if (!isTouched) {
setIsTouched(true);
}
Expand Down
37 changes: 33 additions & 4 deletions stories/PasswordInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { StoryObj, Meta } from "@storybook/react";
import { PasswordInput } from "../src/main";
import { useState } from "react";
import { Button } from "../src/main";

const meta = {
title: "Components/PasswordInput",
Expand All @@ -12,7 +13,7 @@ const meta = {
hideMessage: false,
label: "Enter Password",
value: "",
onChange: () => {},
onChange: () => { },
hidePasswordMeter: false,
hint: "This is a hint message",
},
Expand Down Expand Up @@ -54,7 +55,7 @@ export const Default: Story = {
args: {
label: "Enter Password",
value: "",
onChange: () => {},
onChange: () => { },
hidePasswordMeter: false,
hint: "This is a hint message",
},
Expand All @@ -79,7 +80,7 @@ export const HideMessage: Story = {
hideMessage: true,
label: "Enter Password",
value: "",
onChange: () => {},
onChange: () => { },
hidePasswordMeter: false,
hint: "This is a hint message",
},
Expand All @@ -103,7 +104,7 @@ export const HidePasswordMeter: Story = {
args: {
label: "Enter Password",
value: "",
onChange: () => {},
onChange: () => { },
hidePasswordMeter: true,
hint: "This is a hint message",
},
Expand All @@ -121,3 +122,31 @@ export const HidePasswordMeter: Story = {
);
},
};

export const customErrorMessage: Story = {
name: "Password Input with custom error message",
args: {
label: "Enter Password",
value: "",
onChange: () => { },
hidePasswordMeter: true,
hint: "This is a hint message",
},
render: (args) => {
const [value, setValue] = useState(args.value);
const [errorMessage, setErrorMessage] = useState("");


return (
<div className="theme--light" style={{ display: "flex", flexDirection: "column" }}>
<PasswordInput
{...args}
value={value}
customErrorMessage={errorMessage}
onChange={(e) => setValue(e.target.value)}
/>
<Button onClick={() => setErrorMessage("This is a custom error message")}>submit</Button>
</div>
);
},
};

0 comments on commit 292b23a

Please sign in to comment.