Tweaks for Website signup form (#3635)
* WIP, only deep testing needed * Feedback suggestions * Uses early returns instead * Allows overriding t function via prop Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
parent
532723fb41
commit
298a6d5e15
|
@ -44,7 +44,7 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
|
|||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: [key],
|
||||
message: t(`password_hint_${key}`),
|
||||
message: key,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
import { useId } from "@radix-ui/react-id";
|
||||
import React, { forwardRef, ReactElement, ReactNode, Ref } from "react";
|
||||
import { Info, Circle, Check, X } from "react-feather";
|
||||
import { FieldValues, FormProvider, SubmitHandler, useFormContext, UseFormReturn } from "react-hook-form";
|
||||
import {
|
||||
FieldValues,
|
||||
FormProvider,
|
||||
SubmitHandler,
|
||||
useFormContext,
|
||||
UseFormReturn,
|
||||
FormState,
|
||||
FieldErrors,
|
||||
} from "react-hook-form";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
|
@ -33,6 +41,87 @@ export function Label(props: JSX.IntrinsicElements["label"]) {
|
|||
);
|
||||
}
|
||||
|
||||
function HintsOrErrors<T extends FieldValues = FieldValues>(props: {
|
||||
hintErrors?: string[];
|
||||
formState: FormState<T>;
|
||||
fieldName: string;
|
||||
t: (key: string) => string;
|
||||
}) {
|
||||
const { hintErrors, formState, fieldName, t } = props;
|
||||
const fieldErrors: FieldErrors<T> | undefined = formState.errors[fieldName];
|
||||
|
||||
if (!hintErrors && fieldErrors && !fieldErrors.message) {
|
||||
// no hints passed, field errors exist and they are custom ones
|
||||
return (
|
||||
<div className="text-gray mt-2 flex items-center text-sm text-gray-700">
|
||||
<ul className="ml-2">
|
||||
{Object.keys(fieldErrors).map((key: string) => {
|
||||
return (
|
||||
<li key={key} className="text-blue-700">
|
||||
{t(`${fieldName}_hint_${key}`)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (hintErrors && fieldErrors) {
|
||||
// hints passed, field errors exist
|
||||
return (
|
||||
<div className="text-gray mt-2 flex items-center text-sm text-gray-700">
|
||||
<ul className="ml-2">
|
||||
{hintErrors.map((key: string) => {
|
||||
const submitted = formState.isSubmitted;
|
||||
const error = fieldErrors[key] || fieldErrors.message;
|
||||
return (
|
||||
<li
|
||||
key={key}
|
||||
className={error !== undefined ? (submitted ? "text-red-700" : "") : "text-green-600"}>
|
||||
{error !== undefined ? (
|
||||
submitted ? (
|
||||
<X size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
|
||||
) : (
|
||||
<Circle fill="currentColor" size="5" className="mr-2 inline-block" />
|
||||
)
|
||||
) : (
|
||||
<Check size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
|
||||
)}
|
||||
{t(`${fieldName}_hint_${key}`)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hintErrors) return null;
|
||||
|
||||
// hints passed, no errors exist, proceed to just show hints
|
||||
return (
|
||||
<div className="text-gray mt-2 flex items-center text-sm text-gray-700">
|
||||
<ul className="ml-2">
|
||||
{hintErrors.map((key: string) => {
|
||||
// if field was changed, as no error exist, show checked status and color
|
||||
const dirty = formState.dirtyFields[fieldName];
|
||||
return (
|
||||
<li key={key} className={!!dirty ? "text-green-600" : ""}>
|
||||
{!!dirty ? (
|
||||
<Check size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
|
||||
) : (
|
||||
<Circle fill="currentColor" size="5" className="mr-2 inline-block" />
|
||||
)}
|
||||
{t(`${fieldName}_hint_${key}`)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function InputLeading(props: JSX.IntrinsicElements["div"]) {
|
||||
return (
|
||||
<span className="inline-flex flex-shrink-0 items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-gray-500 sm:text-sm">
|
||||
|
@ -51,13 +140,15 @@ type InputFieldProps = {
|
|||
error?: string;
|
||||
labelSrOnly?: boolean;
|
||||
containerClassName?: string;
|
||||
t?: (key: string) => string;
|
||||
} & React.ComponentProps<typeof Input> & {
|
||||
labelProps?: React.ComponentProps<typeof Label>;
|
||||
};
|
||||
|
||||
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputField(props, ref) {
|
||||
const id = useId();
|
||||
const { t } = useLocale();
|
||||
const { t: _t } = useLocale();
|
||||
const t = props.t || _t;
|
||||
const methods = useFormContext();
|
||||
const {
|
||||
label = t(props.name),
|
||||
|
@ -74,6 +165,8 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
|||
hintErrors,
|
||||
labelSrOnly,
|
||||
containerClassName,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
t: __t,
|
||||
...passThrough
|
||||
} = props;
|
||||
|
||||
|
@ -124,58 +217,13 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
|||
) : (
|
||||
<Input id={id} placeholder={placeholder} className={className} {...passThrough} ref={ref} />
|
||||
)}
|
||||
{methods?.formState?.errors[props.name] && methods?.formState?.errors[props.name].message && (
|
||||
{methods.formState.errors[props.name]?.message && (
|
||||
<div className="text-gray mt-2 flex items-center text-sm text-red-700">
|
||||
<Info className="mr-1 h-3 w-3" />
|
||||
{methods.formState.errors[props.name].message}
|
||||
</div>
|
||||
)}
|
||||
{hintErrors && (
|
||||
<div className="text-gray mt-2 flex items-center text-sm text-gray-700">
|
||||
{methods?.formState?.errors[props.name] !== undefined ? (
|
||||
<ul className="then ml-2">
|
||||
{hintErrors.map((errorKey) => {
|
||||
const error =
|
||||
methods?.formState?.errors[props.name][errorKey] ||
|
||||
methods?.formState?.errors[props.name].message;
|
||||
const submitted = methods?.formState.isSubmitted;
|
||||
return (
|
||||
<li
|
||||
key={errorKey}
|
||||
className={error !== undefined ? (submitted ? "text-red-700" : "") : "text-green-600"}>
|
||||
{error !== undefined ? (
|
||||
submitted ? (
|
||||
<X size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
|
||||
) : (
|
||||
<Circle fill="currentColor" size="5" className="mr-2 inline-block" />
|
||||
)
|
||||
) : (
|
||||
<Check size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
|
||||
)}
|
||||
{error?.message || t(`${props.name}_hint_${errorKey}`)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<ul className="otherwise ml-2">
|
||||
{hintErrors.map((errorKey) => {
|
||||
const dirty = methods?.formState.dirtyFields[props.name];
|
||||
return (
|
||||
<li key={errorKey} className={!!dirty ? "text-green-600" : ""}>
|
||||
{!!dirty ? (
|
||||
<Check size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
|
||||
) : (
|
||||
<Circle fill="currentColor" size="5" className="mr-2 inline-block" />
|
||||
)}
|
||||
{t(`${props.name}_hint_${errorKey}`)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<HintsOrErrors hintErrors={hintErrors} formState={methods.formState} fieldName={props.name} t={t} />
|
||||
{hint && <div className="text-gray mt-2 flex items-center text-sm text-gray-700">{hint}</div>}
|
||||
</div>
|
||||
);
|
||||
|
@ -237,6 +285,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function
|
|||
|
||||
type TextAreaFieldProps = {
|
||||
label?: ReactNode;
|
||||
t?: (key: string) => string;
|
||||
} & React.ComponentProps<typeof TextArea> & {
|
||||
labelProps?: React.ComponentProps<typeof Label>;
|
||||
};
|
||||
|
@ -246,7 +295,8 @@ export const TextAreaField = forwardRef<HTMLTextAreaElement, TextAreaFieldProps>
|
|||
ref
|
||||
) {
|
||||
const id = useId();
|
||||
const { t } = useLocale();
|
||||
const { t: _t } = useLocale();
|
||||
const t = props.t || _t;
|
||||
const methods = useFormContext();
|
||||
const {
|
||||
label = t(props.name as string),
|
||||
|
|
|
@ -19522,11 +19522,6 @@ react-gtm-module@^2.0.11:
|
|||
resolved "https://registry.yarnpkg.com/react-gtm-module/-/react-gtm-module-2.0.11.tgz#14484dac8257acd93614e347c32da9c5ac524206"
|
||||
integrity sha512-8gyj4TTxeP7eEyc2QKawEuQoAZdjKvMY4pgWfycGmqGByhs17fR+zEBs0JUDq4US/l+vbTl+6zvUIx27iDo/Vw==
|
||||
|
||||
react-hook-form@^7.16.2:
|
||||
version "7.33.1"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.33.1.tgz#8c4410e3420788d3b804d62cc4c142915c2e46d0"
|
||||
integrity sha512-ydTfTxEJdvgjCZBj5DDXRc58oTEfnFupEwwTAQ9FSKzykEJkX+3CiAkGtAMiZG7IPWHuzgT6AOBfogiKhUvKgg==
|
||||
|
||||
react-hook-form@^7.31.1:
|
||||
version "7.31.1"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.31.1.tgz#16c357dd366bc226172e6acbb5a1672873bbfb28"
|
||||
|
|
Loading…
Reference in New Issue
Block a user