Files
lti-web-client/src/lib/formik-helper.ts
T
2026-01-14 11:43:10 +07:00

135 lines
3.4 KiB
TypeScript

import { FormikErrors, FormikValues } from 'formik';
export type ErrorMessage = {
key: string;
message: string;
};
/**
* Parse Formik errors object into a flat array of error messages
* @param errors - Formik errors object
* @param parentKey - Parent key for nested objects (used internally for recursion)
* @returns Array of error messages
*/
export function parseFormikErrors<T>(
errors: FormikErrors<T>,
parentKey: string = ''
): ErrorMessage[] {
const errorList: ErrorMessage[] = [];
Object.keys(errors).forEach((key) => {
const value = errors[key as keyof typeof errors];
const fullKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === 'string') {
// Direct error message
errorList.push({ key: fullKey, message: value });
} else if (Array.isArray(value)) {
// Array of errors
value.forEach((item, index) => {
if (typeof item === 'string') {
errorList.push({ key: `${fullKey}[${index}]`, message: item });
} else if (item && typeof item === 'object') {
// Nested object in array
const nestedErrors = parseFormikErrors(
item as FormikErrors<unknown>,
`${fullKey}[${index}]`
);
errorList.push(...nestedErrors);
}
});
} else if (value && typeof value === 'object') {
// Nested object
const nestedErrors = parseFormikErrors(
value as FormikErrors<unknown>,
fullKey
);
errorList.push(...nestedErrors);
}
});
return errorList;
}
/**
* Get unique error messages from Formik errors
* @param errors - Formik errors object
* @returns Array of unique error messages
*/
export function getUniqueFormikErrors<T>(errors: FormikErrors<T>): string[] {
const errorList = parseFormikErrors(errors);
return Array.from(new Set(errorList.map((e) => e.message)));
}
/**
* Get all error messages from Formik errors
* @param errors - Formik errors object
* @returns Array of error messages
*/
export function getAllFormikErrors<T>(errors: FormikErrors<T>): ErrorMessage[] {
return parseFormikErrors(errors);
}
/**
* Check if a value is considered "filled" (not empty)
* @param value - Value to check
* @returns True if value is filled, false otherwise
*/
function isValueFilled(value: unknown): boolean {
// Check for null or undefined
if (value === null || value === undefined) {
return false;
}
// Check for empty string
if (typeof value === 'string' && value.trim() === '') {
return false;
}
// Check for empty array
if (Array.isArray(value) && value.length === 0) {
return false;
}
// Check for empty object (but not Date or other special objects)
if (
typeof value === 'object' &&
!Array.isArray(value) &&
!(value instanceof Date) &&
Object.keys(value).length === 0
) {
return false;
}
return true;
}
/**
* Count the number of filled (non-empty) values in Formik values object
* @param values - Formik values object
* @returns Number of filled values
* @example
* const values = {
* name: 'John',
* email: '',
* age: null,
* tags: ['tag1', 'tag2'],
* emptyArray: [],
* };
* getFilledFormikValuesCount(values); // Returns 2 (name and tags)
*/
export function getFilledFormikValuesCount<T extends FormikValues>(
values: T
): number {
let count = 0;
Object.keys(values).forEach((key) => {
const value = values[key];
if (isValueFilled(value)) {
count++;
}
});
return count;
}