* feat:add prefilcheck check box in the field creation * chore:formatted the li8 translations * feat:add e2e test case for this feature * refactor:reanmed the key from prefilledCheck to disableOnPrefill and moved the readOnly logic to formbuilderField * chore:fixed the type check * fix:if prefilled has error. do not disabble it * chore:removed the e2e tests for prefill in e2e * chore:revert back to old changes * chore:remove unwanted code frome2e * Support name field as well * refactor:handled the feedback changes. still unit test cases pending * feat: add unit tests for disable field on prefill logic * chore: resolved the type check and typos * chore:renamed the files and type clean up * Apply suggestions from code review * chore:renaming test files and moving the disableonprefill hook to formbuilderFeild. * refactor: add unit test for serach params and form values check and minor code formatting and file name changes * fix: validating the input value partially at the intialising phase. so that form response values and from ui values are same and making sure invalid data not sent to backend on submit. * chore:fix the type check issue * fix: field value valdiation at initilisation and add the unit test cases for that change * chore:add comments to the tests * chore:resolve type check * chore: moved the tests to the formBuilder * Simplify codebase by ensuring select and multiselect changes the value when no option is found * Relocate and refactor tests * handle special cases * Dont disable for dirty fields * Update schema.prisma --------- Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: Hariom <hariombalhara@gmail.com>
119 lines
4.1 KiB
TypeScript
119 lines
4.1 KiB
TypeScript
import { useFormContext } from "react-hook-form";
|
|
import type { z } from "zod";
|
|
|
|
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
|
|
|
import type { fieldsSchema } from "./schema";
|
|
|
|
type RhfForm = {
|
|
fields: z.infer<typeof fieldsSchema>;
|
|
};
|
|
|
|
type RhfFormFields = RhfForm["fields"];
|
|
|
|
type RhfFormField = RhfFormFields[number];
|
|
|
|
type FieldProps = Pick<
|
|
RhfFormField,
|
|
"name" | "type" | "disableOnPrefill" | "variantsConfig" | "optionsInputs"
|
|
>;
|
|
|
|
export const getFieldNameFromErrorMessage = (errorMessage: string): string => {
|
|
const name = errorMessage?.replace(/\{([^}]+)\}.*/, "$1");
|
|
return name;
|
|
};
|
|
|
|
function toArray(value: string[] | string): string[] {
|
|
return Array.isArray(value) ? value : [value];
|
|
}
|
|
|
|
function intersected(arr1: string[], arr2: string[]): boolean {
|
|
return !!arr1.find((value) => arr2.find((innerValue) => innerValue?.toString() == value?.toString()));
|
|
}
|
|
|
|
function isEqual(searchParamValue: string | string[], formValue: string[] | string): boolean {
|
|
if (typeof formValue === "string") {
|
|
return searchParamValue?.toString() == formValue?.toString();
|
|
}
|
|
|
|
const formValueToArray = toArray(formValue as string | string[]);
|
|
const urlValueToArray = toArray(searchParamValue);
|
|
|
|
return intersected(formValueToArray, urlValueToArray);
|
|
}
|
|
|
|
export const useShouldBeDisabledDueToPrefill = (field: FieldProps): boolean => {
|
|
const { getValues, formState } = useFormContext();
|
|
const toPrefillValues = useRouterQuery();
|
|
if (!field.disableOnPrefill) {
|
|
return false;
|
|
}
|
|
// If the field is dirty, it means that some change has been made to the input by user, so we shouldn't disable it anymore
|
|
// It avoids a scenario like this
|
|
// 1. Input Prefilled and thus disabled
|
|
// 2. User submits the form but error is shown for that field(as it was invalid value)
|
|
// 3. Field is enabled back to correct the value
|
|
// 3. User tries to correct the value and enters the previous value(partially typed) but input suddenly gets disabled
|
|
if (formState.dirtyFields?.responses?.[field.name]) {
|
|
return false;
|
|
}
|
|
|
|
const prefilledValues = getValues()?.responses || {};
|
|
// radioInput and variantsConfig type fields have complex transformations, so we can't directly compare them with search params
|
|
// e.g. name = John Doe in search params will be transformed to {firstName: John, lastName: Doe} and we can't directly compare it with toPrefill value
|
|
const shouldMatchPrefilledValue = field.type !== "radioInput" && !field.variantsConfig;
|
|
const errorMessage = (formState?.errors?.responses?.message || "") as string;
|
|
const fieldNameThatHasError = getFieldNameFromErrorMessage(errorMessage);
|
|
if (fieldNameThatHasError === field.name) {
|
|
return false;
|
|
}
|
|
|
|
const searchParamValue = toPrefillValues[field.name];
|
|
const prefilledValue = prefilledValues[field.name];
|
|
|
|
if (!shouldMatchPrefilledValue) {
|
|
// In this case we just ensure they have some values
|
|
if (isValueSet(searchParamValue) && isValueSet(prefilledValue)) {
|
|
if (field.type === "radioInput") {
|
|
return hasValidRadioInputValue({ value: prefilledValue, optionsInputs: field.optionsInputs });
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!isValueSet(prefilledValue)) {
|
|
return false;
|
|
}
|
|
|
|
if (!toPrefillValues) {
|
|
// If there are no toPrefillValues, nothing can be prefilled and thus nothing should be disabled
|
|
return false;
|
|
}
|
|
|
|
// If the specified prefill value is filled, then we disable the field. If it is changed due to some reason, then we don't disable the field
|
|
if (searchParamValue == prefilledValue?.toString()) {
|
|
return true;
|
|
}
|
|
|
|
return isEqual(searchParamValue, prefilledValue);
|
|
|
|
function isValueSet(value: string | string[] | null | undefined): boolean {
|
|
return value !== null && value !== undefined && value !== "";
|
|
}
|
|
|
|
function hasValidRadioInputValue({
|
|
value,
|
|
optionsInputs,
|
|
}: {
|
|
value: { optionValue: string; value: string };
|
|
optionsInputs: Record<string, unknown> | undefined;
|
|
}): boolean {
|
|
const requiresOptionInputField = optionsInputs?.[value.value];
|
|
if (!requiresOptionInputField) {
|
|
return !!value.value;
|
|
}
|
|
|
|
return !!value.value && !!value.optionValue;
|
|
}
|
|
};
|