@matthew.ngo/reform
Version:
A flexible and powerful React form management library with advanced validation, state observation, and multi-group support
110 lines (95 loc) • 3.1 kB
text/typescript
import { FieldError, FieldErrors } from 'react-hook-form';
import { FormGroup } from '../form/form-groups';
const extractFieldName = (path: string): string => {
// Match pattern: groups[0].data.fieldName or groups.0.data.fieldName
const matches = path.match(/groups(?:\[(\d+)\]|\.\d+)\.data\.(.+)$/);
return matches ? matches[2] : path;
};
const formatErrorMessage = (
message: string,
fieldName: string,
label?: string
): string => {
// Replace all occurrences of groups[n].data.fieldName or groups.n.data.fieldName with label
return message.replace(
/groups(?:\[\d+\]|\.\d+)\.data\.[^\ ]+/g,
label || fieldName
);
};
const normalizeErrorType = (type: any): FieldError['type'] => {
// Convert any error type to valid FieldError type
const validTypes = [
'required',
'min',
'max',
'minLength',
'maxLength',
'pattern',
'validate',
] as const;
return validTypes.includes(type as any) ? type : 'validate';
};
// Add this new utility function
const getNestedLabel = (
labels: Record<string, any> | undefined,
path: string
): string | undefined => {
if (!labels) return undefined;
const parts = path.split('.');
let current = labels;
for (const part of parts) {
if (!current || typeof current !== 'object') return undefined;
current = current[part];
}
return typeof current === 'string' ? current : undefined;
};
export const transformFormErrors = <T>(
errors: FieldErrors<{ groups: FormGroup<T>[] }>,
labels?: Record<string, any>
): Record<string, FieldError> => {
const result: Record<string, FieldError> = {};
// Transform root error
if (errors.root) {
result.root = {
type: normalizeErrorType(errors.root.type),
message: errors.root.message || '',
};
}
// Transform group errors
if (errors.groups) {
Array.isArray(errors.groups) &&
errors.groups.forEach((group, index) => {
if (group?.message) {
result[`groups.${index}`] = {
type: normalizeErrorType(group.type),
message: group.message,
};
}
if (group?.data) {
const processNestedErrors = (
errors: Record<string, any>,
basePath: string
) => {
Object.entries(errors).forEach(([field, error]) => {
if (error?.message) {
const fieldPath = `${basePath}${field}`;
const fieldName = extractFieldName(
error.ref?.name || fieldPath
);
const label = getNestedLabel(labels, fieldName);
result[`groups.${index}.${fieldPath}`] = {
type: normalizeErrorType(error.type),
message: formatErrorMessage(error.message, fieldName, label),
};
} else if (error && typeof error === 'object') {
// Handle nested objects
processNestedErrors(error, `${field}.`);
}
});
};
processNestedErrors(group.data, '');
}
});
}
return result;
};