Skip to content

Commit

Permalink
Merge pull request #2029 from AntaresSimulatorTeam/feature/validation…
Browse files Browse the repository at this point in the history
…-utils
  • Loading branch information
skamril authored May 16, 2024
2 parents d8f13d6 + 6afb77c commit d51a5b0
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 39 deletions.
3 changes: 2 additions & 1 deletion webapp/public/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@
"form.asyncDefaultValues.error": "Failed to get values",
"form.field.required": "Field required",
"form.field.duplicate": "Value already exists",
"form.field.minLength": "{{0}} character(s) minimum",
"form.field.minLength": "{{length}} character(s) minimum",
"form.field.maxLength": "{{length}} character(s) maximum",
"form.field.minValue": "The minimum value is {{0}}",
"form.field.maxValue": "The maximum value is {{0}}",
"form.field.invalidNumber": "Invalid number",
Expand Down
3 changes: 2 additions & 1 deletion webapp/public/locales/fr/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@
"form.asyncDefaultValues.error": "Impossible d'obtenir les valeurs",
"form.field.required": "Champ requis",
"form.field.duplicate": "Cette valeur existe déjà",
"form.field.minLength": "{{0}} caractère(s) minimum",
"form.field.minLength": "{{length}} caractère(s) minimum",
"form.field.maxLength": "{{length}} caractère(s) maximum",
"form.field.minValue": "La valeur minimum est {{0}}",
"form.field.maxValue": "La valeur maximum est {{0}}",
"form.field.invalidNumber": "Nombre invalide",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function Fields() {
name="unitCount"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 1 }),
validate: validateNumber({ min: 1 }),
setValueAs: Math.floor,
}}
/>
Expand All @@ -83,7 +83,7 @@ function Fields() {
name="nominalCapacity"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
/>
<NumberFE
Expand All @@ -96,15 +96,15 @@ function Fields() {
name="spinning"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0, max: 100 }),
validate: validateNumber({ min: 0, max: 100 }),
}}
/>
<NumberFE
label={t("study.modelization.clusters.minUpTime")}
name="minUpTime"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 1, max: 168 }),
validate: validateNumber({ min: 1, max: 168 }),
setValueAs: Math.floor,
}}
/>
Expand All @@ -113,7 +113,7 @@ function Fields() {
name="minDownTime"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 1, max: 168 }),
validate: validateNumber({ min: 1, max: 168 }),
setValueAs: Math.floor,
}}
/>
Expand All @@ -134,7 +134,7 @@ function Fields() {
name="efficiency"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
disabled={!isTSCost}
/>
Expand All @@ -143,7 +143,7 @@ function Fields() {
name="marginalCost"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
/>

Expand All @@ -152,31 +152,31 @@ function Fields() {
name="startupCost"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
/>
<NumberFE
label={t("study.modelization.clusters.marketBidCost")}
name="marketBidCost"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
/>
<NumberFE
label={t("study.modelization.clusters.fixedCost")}
name="fixedCost"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
/>
<NumberFE
label={t("study.modelization.clusters.variableOMCost")}
name="variableOMCost"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
disabled={!isTSCost}
/>
Expand All @@ -196,7 +196,7 @@ function Fields() {
name={name}
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0 }),
validate: validateNumber({ min: 0 }),
}}
/>
),
Expand All @@ -218,7 +218,7 @@ function Fields() {
name="volatilityForced"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0, max: 1 }),
validate: validateNumber({ min: 0, max: 1 }),
}}
inputProps={{ step: 0.1 }}
/>
Expand All @@ -227,7 +227,7 @@ function Fields() {
name="volatilityPlanned"
control={control}
rules={{
validate: (v) => validateNumber(v, { min: 0, max: 1 }),
validate: validateNumber({ min: 0, max: 1 }),
}}
inputProps={{ step: 0.1 }}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ function AddDialog({
rules={{
validate: (v) =>
validateString(v, {
max: 20,
maxLength: 20,
specialChars: "-",
}),
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function Fields({ study, constraintId }: Props) {
rules={{
validate: (v) =>
validateString(v, {
max: 20,
maxLength: 20,
specialChars: "-",
}),
}}
Expand Down
72 changes: 51 additions & 21 deletions webapp/src/utils/validationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@ import { t } from "i18next";
// Types
////////////////////////////////////////////////////////////////

interface ValidationOptions {
interface NumberValidationOptions {
min?: number;
max?: number;
}

interface StringValidationOptions {
existingValues?: string[];
excludedValues?: string[];
isCaseSensitive?: boolean;
allowSpecialChars?: boolean;
specialChars?: string;
allowSpaces?: boolean;
editedValue?: string;
min?: number;
max?: number;
minLength?: number;
maxLength?: number;
}

////////////////////////////////////////////////////////////////
Expand All @@ -35,13 +40,13 @@ interface ValidationOptions {
* @param [options.specialChars="&()_-"] - A string representing additional allowed characters outside the typical alphanumeric scope.
* @param [options.allowSpaces=true] - Flags if spaces are allowed in the value.
* @param [options.editedValue=""] - The current value being edited, to exclude it from duplicate checks.
* @param [options.min=0] - Minimum length required for the string. Defaults to 0.
* @param [options.max=255] - Maximum allowed length for the string. Defaults to 255.
* @param [options.minLength=0] - Minimum length required for the string. Defaults to 0.
* @param [options.maxLength=255] - Maximum allowed length for the string. Defaults to 255.
* @returns True if validation is successful, or a localized error message if it fails.
*/
export function validateString(
value: string,
options?: ValidationOptions,
options?: StringValidationOptions,
): string | true {
const {
existingValues = [],
Expand All @@ -51,8 +56,8 @@ export function validateString(
allowSpaces = true,
specialChars = "&()_-",
editedValue = "",
min = 0,
max = 255,
minLength = 0,
maxLength = 255,
} = options || {};

const trimmedValue = value.trim();
Expand All @@ -65,12 +70,12 @@ export function validateString(
return t("form.field.spacesNotAllowed");
}

if (trimmedValue.length < min) {
return t("form.field.minValue", { 0: min });
if (trimmedValue.length < minLength) {
return t("form.field.minLength", { length: minLength });
}

if (trimmedValue.length > max) {
return t("form.field.maxValue", { 0: max });
if (trimmedValue.length > maxLength) {
return t("form.field.maxLength", { length: maxLength });
}

// Compiles a regex pattern based on allowed characters and flags.
Expand Down Expand Up @@ -124,11 +129,11 @@ export function validatePassword(password: string): string | true {
}

if (trimmedPassword.length < 8) {
return t("form.field.minValue", { 0: 8 });
return t("form.field.minLength", { length: 8 });
}

if (trimmedPassword.length > 50) {
return t("form.field.maxValue", { 0: 50 });
return t("form.field.maxLength", { length: 50 });
}

if (!/[a-z]/.test(trimmedPassword)) {
Expand All @@ -153,22 +158,47 @@ export function validatePassword(password: string): string | true {
/**
* Validates a number against specified numerical limits.
*
* @example
* validateNumber(5, { min: 0, max: 10 }); // true
* validateNumber(9, { min: 10, max: 20 }); // Error message
*
*
* @example <caption>With currying.</caption>
* const fn = validateNumber({ min: 0, max: 10 });
* fn(5); // true
* fn(11); // Error message
*
* @param value - The number to validate.
* @param options - Configuration options for validation including min and max values. (Optional)
* @param [options.min=Number.MIN_SAFE_INTEGER] - Minimum allowed value for the number.
* @param [options.max=Number.MAX_SAFE_INTEGER] - Maximum allowed value for the number.
* @param [options] - Configuration options for validation.
* @param [options.min=Number.MIN_SAFE_INTEGER] - Minimum allowed value.
* @param [options.max=Number.MAX_SAFE_INTEGER] - Maximum allowed value.
* @returns True if validation is successful, or a localized error message if it fails.
*/
export function validateNumber(
value: number,
options?: ValidationOptions,
): string | true {
if (typeof value !== "number" || isNaN(value) || !isFinite(value)) {
options?: NumberValidationOptions,
): string | true;

export function validateNumber(
options?: NumberValidationOptions,
): (value: number) => string | true;

export function validateNumber(
valueOrOpts?: number | NumberValidationOptions,
options: NumberValidationOptions = {},
): (string | true) | ((value: number) => string | true) {
if (typeof valueOrOpts !== "number") {
return (v: number) => validateNumber(v, valueOrOpts);
}

const value = valueOrOpts;

if (!isFinite(value)) {
return t("form.field.invalidNumber", { value });
}

const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER } =
options || {};
options;

if (value < min) {
return t("form.field.minValue", { 0: min });
Expand Down

0 comments on commit d51a5b0

Please sign in to comment.