feat(validator): Adding error, warn, info categories support. (#204)
This commit is contained in:
parent
2f2c28b13f
commit
965a16aaec
12 changed files with 141 additions and 60 deletions
|
@ -11,12 +11,20 @@ const form = new FormGroup([
|
||||||
{
|
{
|
||||||
name: "username",
|
name: "username",
|
||||||
label: "Username",
|
label: "Username",
|
||||||
validators: [Validators.required],
|
validators: [
|
||||||
|
{
|
||||||
|
validator: Validators.required,
|
||||||
|
category: "info",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "email",
|
name: "email",
|
||||||
label: "Email",
|
label: "Email",
|
||||||
validators: [Validators.email, Validators.required],
|
validators: [
|
||||||
|
{ validator: Validators.required },
|
||||||
|
{ validator: Validators.email, category: "warn" },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "password",
|
name: "password",
|
||||||
|
@ -52,7 +60,7 @@ const form = new FormGroup([
|
||||||
name: "comment",
|
name: "comment",
|
||||||
label: "Feedback",
|
label: "Feedback",
|
||||||
type: "textarea",
|
type: "textarea",
|
||||||
value: "Nice!"
|
value: "Nice!",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import type { ValidatorRules } from "./validator.types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `ControlType` determines the type of form control
|
* `ControlType` determines the type of form control
|
||||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types
|
||||||
|
@ -34,7 +36,7 @@ export interface ControlBase {
|
||||||
value?: string | number | string[];
|
value?: string | number | string[];
|
||||||
label?: string;
|
label?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
validators?: string[]; // TODO: implement validator type
|
validators?: ValidatorRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Checkbox extends ControlBase {
|
export interface Checkbox extends ControlBase {
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
export type HookType = "onSubmit" | "onControlBlur" | "all";
|
export type HookType = "onSubmit" | "onControlBlur" | "all";
|
||||||
|
|
||||||
|
export type CategoryType = "error" | "warn" | "info";
|
||||||
|
|
||||||
|
export type ValidatorRules =
|
||||||
|
| string[]
|
||||||
|
| { validator: string; category?: CategoryType }[];
|
||||||
|
|
||||||
export type ValidationResult = true | ValidationError;
|
export type ValidationResult = true | ValidationError;
|
||||||
|
|
||||||
export type ValidationError = {
|
export type ValidationError = {
|
||||||
error: string;
|
error: string;
|
||||||
value: string;
|
value: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
category?: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,19 +20,24 @@ export interface Props {
|
||||||
|
|
||||||
const { control, showValidationHints, showErrors = false, readOnly = false } = Astro.props;
|
const { control, showValidationHints, showErrors = false, readOnly = false } = Astro.props;
|
||||||
|
|
||||||
const hasErrors: boolean | null = !!control.errors?.length;
|
const hasError: boolean = control.errors?.length ? control.errors[0]?.category === 'error' : false;
|
||||||
|
const hasWarn: boolean = control.errors?.length ? control.errors[0]?.category === 'warn' : false;
|
||||||
|
const hasInfo: boolean = control.errors?.length ? control.errors[0]?.category === 'info' : false;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="field" data-validator-haserrors={hasErrors ? hasErrors.toString() : null}>
|
<div
|
||||||
|
class="field"
|
||||||
|
data-validator-error={hasError.toString()}
|
||||||
|
data-validator-warn={hasWarn.toString()}
|
||||||
|
data-validator-info={hasInfo.toString()}
|
||||||
|
>
|
||||||
<Label control={control} showValidationHints={showValidationHints}>
|
<Label control={control} showValidationHints={showValidationHints}>
|
||||||
{
|
{
|
||||||
control.type === 'radio' ? (
|
control.type === 'radio' ? (
|
||||||
<RadioGroup control={control as Radio} readOnly={readOnly} />
|
<RadioGroup control={control as Radio} readOnly={readOnly} />
|
||||||
) :
|
) : control.type === 'dropdown' ? (
|
||||||
control.type === 'dropdown' ? (
|
|
||||||
<DropdownControl control={control as Dropdown} readOnly={readOnly} />
|
<DropdownControl control={control as Dropdown} readOnly={readOnly} />
|
||||||
) :
|
) : control.type === 'textarea' ? (
|
||||||
control.type === 'textarea' ? (
|
|
||||||
<TextAreaControl control={control as TextArea} readOnly={readOnly} />
|
<TextAreaControl control={control as TextArea} readOnly={readOnly} />
|
||||||
) : (
|
) : (
|
||||||
<Input control={control} readOnly={readOnly} />
|
<Input control={control} readOnly={readOnly} />
|
||||||
|
|
|
@ -60,9 +60,19 @@ const formId = Array.isArray(formGroups) ? uid() : formGroups?.id || null;
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style is:global>
|
<style is:global>
|
||||||
[data-validator-hints='true'][data-validator-haserrors='true'],
|
[data-validator-hints='true'][data-validator-error='true'],
|
||||||
[data-validator-hints='true'] [data-validator-haserrors='true'] {
|
[data-validator-hints='true'] [data-validator-error='true'] {
|
||||||
color: red;
|
color: red;
|
||||||
border-color: red;
|
border-color: red;
|
||||||
}
|
}
|
||||||
|
[data-validator-hints='true'][data-validator-warn='true'],
|
||||||
|
[data-validator-hints='true'] [data-validator-warn='true'] {
|
||||||
|
color: orange;
|
||||||
|
border-color: orange;
|
||||||
|
}
|
||||||
|
[data-validator-hints='true'][data-validator-info='true'],
|
||||||
|
[data-validator-hints='true'] [data-validator-info='true'] {
|
||||||
|
color: blue;
|
||||||
|
border-color: blue;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,12 +11,18 @@ const { control, showValidationHints } = Astro.props;
|
||||||
|
|
||||||
const { validators = [] } = control;
|
const { validators = [] } = control;
|
||||||
|
|
||||||
const isRequired: boolean = showValidationHints && validators.includes('validator-required');
|
const isRequired: boolean =
|
||||||
|
showValidationHints &&
|
||||||
|
validators.some((validator) => {
|
||||||
|
if (typeof validator === 'string' && validator == 'validator-required') return true;
|
||||||
|
if (typeof validator === 'object' && validator.validator == 'validator-required') return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
{
|
{
|
||||||
control.label && control.type !== "checkbox" && (
|
control.label && control.type !== 'checkbox' && (
|
||||||
<label for={control.id} data-validation-required={isRequired ? "true" : null}>
|
<label for={control.id} data-validation-required={isRequired ? 'true' : null}>
|
||||||
{control.label}
|
{control.label}
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
|
@ -25,15 +31,15 @@ const isRequired: boolean = showValidationHints && validators.includes('validato
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
{
|
{
|
||||||
control.label && control.type === "checkbox" && (
|
control.label && control.type === 'checkbox' && (
|
||||||
<label for={control.id} data-validation-required={isRequired ? "true" : null}>
|
<label for={control.id} data-validation-required={isRequired ? 'true' : null}>
|
||||||
{control.label}
|
{control.label}
|
||||||
</label>
|
</label>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
label[data-validation-required="true"]::before {
|
label[data-validation-required='true']::before {
|
||||||
content: "*";
|
content: '*';
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,23 +7,30 @@ import type { FormControl } from '../../core/form-control';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
control: FormControl;
|
control: FormControl;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { control, readOnly } = Astro.props;
|
const { control, readOnly } = Astro.props;
|
||||||
|
|
||||||
const { validators = [] } = control;
|
const { validators = [] } = control;
|
||||||
|
|
||||||
const hasErrors: boolean = !!control.errors?.length;
|
const hasError: boolean = control.errors?.length ? control.errors[0]?.category === 'error' : false;
|
||||||
|
const hasWarn: boolean = control.errors?.length ? control.errors[0]?.category === 'warn' : false;
|
||||||
|
const hasInfo: boolean = control.errors?.length ? control.errors[0]?.category === 'info' : false;
|
||||||
|
|
||||||
const validatorAttributes: Record<string, string> = validators?.reduce((prev, validator) => {
|
// @ts-ignore
|
||||||
|
const validatorAttributes: Record<string, string> = validators.reduce((prev, val) => {
|
||||||
|
const validator = typeof val === 'string' ? val : val.validator;
|
||||||
const split: string[] = validator.split(':');
|
const split: string[] = validator.split(':');
|
||||||
const label: string = `data-${split[0]}` || 'invalid';
|
const label: string = `data-${split[0]}` || 'invalid';
|
||||||
const value: string | null = split.length > 1 ? split[1] ?? null : 'true';
|
const value: string | null = split.length > 1 ? split[1] ?? null : 'true';
|
||||||
|
const category = typeof val === 'string' ? 'error' : val.category || 'error';
|
||||||
|
const categoryLabel: string = `data-${split[0]}-category`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[label]: value,
|
[label]: value,
|
||||||
|
[categoryLabel]: category,
|
||||||
};
|
};
|
||||||
}, {});
|
}, {});
|
||||||
---
|
---
|
||||||
|
@ -36,8 +43,10 @@ const validatorAttributes: Record<string, string> = validators?.reduce((prev, va
|
||||||
checked={control.value === 'checked'}
|
checked={control.value === 'checked'}
|
||||||
placeholder={control.placeholder}
|
placeholder={control.placeholder}
|
||||||
data-label={control.label}
|
data-label={control.label}
|
||||||
data-validator-haserrors={hasErrors.toString()}
|
data-validator-error={hasError.toString()}
|
||||||
|
data-validator-warn={hasWarn.toString()}
|
||||||
|
data-validator-info={hasInfo.toString()}
|
||||||
readonly={readOnly || null}
|
readonly={readOnly || null}
|
||||||
disabled={(readOnly || null) && control.type === 'checkbox'}
|
disabled={(readOnly || null) && control.type === 'checkbox'}
|
||||||
{...validatorAttributes}
|
{...validatorAttributes}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,14 +13,19 @@ const { control, readOnly = false } = Astro.props;
|
||||||
|
|
||||||
const { validators = [] } = control;
|
const { validators = [] } = control;
|
||||||
|
|
||||||
const validatorAttributes: Record<string, string> = validators?.reduce((prev, validator) => {
|
// @ts-ignore
|
||||||
|
const validatorAttributes: Record<string, string> = validators.reduce((prev, val) => {
|
||||||
|
const validator = typeof val === 'string' ? val : val.validator;
|
||||||
const split: string[] = validator.split(':');
|
const split: string[] = validator.split(':');
|
||||||
const label: string = `data-${split[0]}` || 'invalid';
|
const label: string = `data-${split[0]}` || 'invalid';
|
||||||
const value: string | null = split.length > 1 ? split[1] ?? null : 'true';
|
const value: string | null = split.length > 1 ? split[1] ?? null : 'true';
|
||||||
|
const category = typeof val === 'string' ? 'error' : val.category || 'error';
|
||||||
|
const categoryLabel: string = `data-${split[0]}-category`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[label]: value,
|
[label]: value,
|
||||||
|
[categoryLabel]: category,
|
||||||
};
|
};
|
||||||
}, {});
|
}, {});
|
||||||
---
|
---
|
||||||
|
@ -35,5 +40,5 @@ const validatorAttributes: Record<string, string> = validators?.reduce((prev, va
|
||||||
readonly={readOnly || null}
|
readonly={readOnly || null}
|
||||||
{...validatorAttributes}
|
{...validatorAttributes}
|
||||||
>
|
>
|
||||||
{ control.value }
|
{control.value}
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import type {
|
||||||
ValidationError,
|
ValidationError,
|
||||||
TextArea,
|
TextArea,
|
||||||
ControlBase,
|
ControlBase,
|
||||||
|
ValidatorRules,
|
||||||
} from '@astro-reactive/common';
|
} from '@astro-reactive/common';
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
|
|
||||||
|
@ -23,15 +24,15 @@ export class FormControl {
|
||||||
private _isValid = true;
|
private _isValid = true;
|
||||||
private _isPristine = true;
|
private _isPristine = true;
|
||||||
private _placeholder: string | null = null;
|
private _placeholder: string | null = null;
|
||||||
private _validators: string[] = [];
|
private _validators: ValidatorRules = [];
|
||||||
private _errors: ValidationError[] = [];
|
private _errors: ValidationError[] = [];
|
||||||
private _options: string[] | ControlOption[] = [];
|
private _options: string[] | ControlOption[] = [];
|
||||||
private _rows: number | null = null;
|
private _rows: number | null = null;
|
||||||
private _cols: number | null = null;
|
private _cols: number | null = null;
|
||||||
|
|
||||||
private validate: (value: string, validators: string[]) => ValidationError[] = (
|
private validate: (value: string, validators: ValidatorRules) => ValidationError[] = (
|
||||||
value: string,
|
value: string,
|
||||||
validators: string[]
|
validators: ValidatorRules
|
||||||
) => {
|
) => {
|
||||||
value;
|
value;
|
||||||
validators;
|
validators;
|
||||||
|
|
|
@ -52,7 +52,7 @@ describe('Field.astro test', () => {
|
||||||
|
|
||||||
it('Should server-render validation error attributes', async () => {
|
it('Should server-render validation error attributes', async () => {
|
||||||
// arrange
|
// arrange
|
||||||
const expectedResult = 'data-validator-haserrors="true"';
|
const expectedResult = 'data-validator-error="true"';
|
||||||
const props = {
|
const props = {
|
||||||
control: {
|
control: {
|
||||||
label: 'FAKE LABEL',
|
label: 'FAKE LABEL',
|
||||||
|
@ -61,6 +61,7 @@ describe('Field.astro test', () => {
|
||||||
errors: [
|
errors: [
|
||||||
{
|
{
|
||||||
error: 'required',
|
error: 'required',
|
||||||
|
category: 'error',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
value: '',
|
value: '',
|
||||||
|
|
|
@ -24,6 +24,7 @@ const { hook = 'all', displayErrorMessages = false } = Astro.props;
|
||||||
// TODO: handle hooks / when to attach event listeners
|
// TODO: handle hooks / when to attach event listeners
|
||||||
// const form = document.querySelector('form');
|
// const form = document.querySelector('form');
|
||||||
|
|
||||||
|
import type { ValidatorRules } from '@astro-reactive/common';
|
||||||
import { clearErrors, validate } from './core';
|
import { clearErrors, validate } from './core';
|
||||||
|
|
||||||
// const hook: HookType = (document.getElementById('hook') as HTMLInputElement).value as HookType;
|
// const hook: HookType = (document.getElementById('hook') as HTMLInputElement).value as HookType;
|
||||||
|
@ -38,22 +39,27 @@ const { hook = 'all', displayErrorMessages = false } = Astro.props;
|
||||||
.filter((attribute) => attribute.includes('data-validator-'))
|
.filter((attribute) => attribute.includes('data-validator-'))
|
||||||
.map((attribute) => {
|
.map((attribute) => {
|
||||||
const limit = element.getAttribute(attribute);
|
const limit = element.getAttribute(attribute);
|
||||||
return `${attribute}:${limit}`;
|
return {
|
||||||
});
|
validator: `${attribute}:${limit}`,
|
||||||
|
category: element.getAttribute(`${attribute}-category`),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((val) => val.category !== null); // Filter out validators without a category
|
||||||
|
|
||||||
const value =
|
const value =
|
||||||
element.type === 'checkbox' ? (element.checked ? 'checked' : '') : element.value;
|
element.type === 'checkbox' ? (element.checked ? 'checked' : '') : element.value;
|
||||||
const errors = validate(value, validators);
|
const errors = validate(value, validators as ValidatorRules);
|
||||||
|
|
||||||
// set element hasErrors
|
// set element hasErrors
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
element.parentElement?.setAttribute('data-validator-haserrors', 'true');
|
element.parentElement?.setAttribute(`data-validator-${errors[0]?.category}`, 'true');
|
||||||
element.setAttribute('data-validator-haserrors', 'true');
|
element.setAttribute(`data-validator-${errors[0]?.category}`, 'true');
|
||||||
element.classList.add('has-errors');
|
element.classList.add(`has-errors`);
|
||||||
// TODO: display error messages
|
// TODO: display error messages
|
||||||
} else {
|
} else {
|
||||||
element.parentElement?.removeAttribute('data-validator-haserrors');
|
element.parentElement?.removeAttribute(`data-validator-${errors[0]?.category}`);
|
||||||
element.removeAttribute('data-validator-haserrors');
|
element.removeAttribute(`data-validator-${errors[0]?.category}`);
|
||||||
element.classList.remove('has-errors');
|
element.classList.remove(`has-${errors[0]?.category}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { ValidationError } from '@astro-reactive/common';
|
import type { ValidationError, ValidatorRules } from '@astro-reactive/common';
|
||||||
import { Validators } from './validator-names';
|
import { Validators } from './validator-names';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,10 +7,19 @@ import { Validators } from './validator-names';
|
||||||
* @param validators - names of validation logic to be applied
|
* @param validators - names of validation logic to be applied
|
||||||
* @returns errors - array of errors `ValidationError`
|
* @returns errors - array of errors `ValidationError`
|
||||||
*/
|
*/
|
||||||
export function validate(value: string, validators: string[]): ValidationError[] {
|
export function validate(value: string, validators: ValidatorRules): ValidationError[] {
|
||||||
return validators
|
return validators
|
||||||
.map((validator) => validator.replace('data-', ''))
|
.map((validator) => {
|
||||||
.map((attribute): ValidationError | null => {
|
if (typeof validator === 'string') {
|
||||||
|
return { attribute: validator.replace('data-', ''), category: 'error' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
attribute: validator.validator.replace('data-', ''),
|
||||||
|
category: validator.category || 'error',
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.map(({ attribute, category }): ValidationError | null => {
|
||||||
// TODO: implement dynamic import of function depending on validators
|
// TODO: implement dynamic import of function depending on validators
|
||||||
const split = attribute.split(':');
|
const split = attribute.split(':');
|
||||||
const validator = split[0];
|
const validator = split[0];
|
||||||
|
@ -18,31 +27,31 @@ export function validate(value: string, validators: string[]): ValidationError[]
|
||||||
const limit = parseInt(limitStr || '0', 10);
|
const limit = parseInt(limitStr || '0', 10);
|
||||||
|
|
||||||
if (validator === Validators.min()) {
|
if (validator === Validators.min()) {
|
||||||
return validateMin(value, limit);
|
return validateMin(value, limit, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validator === Validators.max()) {
|
if (validator === Validators.max()) {
|
||||||
return validateMax(value, limit);
|
return validateMax(value, limit, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validator === Validators.required) {
|
if (validator === Validators.required) {
|
||||||
return validateRequired(value);
|
return validateRequired(value, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validator === Validators.requiredChecked) {
|
if (validator === Validators.requiredChecked) {
|
||||||
return validateRequiredChecked(value);
|
return validateRequiredChecked(value, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validator === Validators.email) {
|
if (validator === Validators.email) {
|
||||||
return validateEmail(value);
|
return validateEmail(value, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validator === Validators.minLength()) {
|
if (validator === Validators.minLength()) {
|
||||||
return validateMinLength(value, limit);
|
return validateMinLength(value, limit, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validator === Validators.maxLength()) {
|
if (validator === Validators.maxLength()) {
|
||||||
return validateMaxLength(value, limit);
|
return validateMaxLength(value, limit, category);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -51,12 +60,17 @@ export function validate(value: string, validators: string[]): ValidationError[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearErrors(event: Event) {
|
export function clearErrors(event: Event) {
|
||||||
|
const categories = ['error', 'warn', 'info'];
|
||||||
const element = event.target as HTMLInputElement;
|
const element = event.target as HTMLInputElement;
|
||||||
element.parentElement?.setAttribute('data-validator-haserrors', 'false');
|
const parent = element.parentElement as HTMLElement;
|
||||||
element.setAttribute('data-validator-haserrors', 'false');
|
|
||||||
|
categories.forEach((category) => {
|
||||||
|
parent.setAttribute(`data-validator-${category}`, 'false');
|
||||||
|
element.setAttribute(`data-validator-${category}`, 'false');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateMin(value: string, limit: number): ValidationError | null {
|
function validateMin(value: string, limit: number, category: string): ValidationError | null {
|
||||||
const isValid = parseInt(value, 10) >= limit;
|
const isValid = parseInt(value, 10) >= limit;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
@ -64,13 +78,14 @@ function validateMin(value: string, limit: number): ValidationError | null {
|
||||||
value,
|
value,
|
||||||
error: 'min',
|
error: 'min',
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateMax(value: string, limit: number): ValidationError | null {
|
function validateMax(value: string, limit: number, category: string): ValidationError | null {
|
||||||
const isValid = parseInt(value, 10) <= limit;
|
const isValid = parseInt(value, 10) <= limit;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
@ -78,32 +93,35 @@ function validateMax(value: string, limit: number): ValidationError | null {
|
||||||
value,
|
value,
|
||||||
error: 'max',
|
error: 'max',
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRequired(value: string): ValidationError | null {
|
function validateRequired(value: string, category: string): ValidationError | null {
|
||||||
const isValid = !!value;
|
const isValid = !!value;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
error: 'required',
|
error: 'required',
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateRequiredChecked(value: string): ValidationError | null {
|
function validateRequiredChecked(value: string, category: string): ValidationError | null {
|
||||||
const isValid = value === 'checked';
|
const isValid = value === 'checked';
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
error: 'requiredChecked',
|
error: 'requiredChecked',
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +129,7 @@ function validateRequiredChecked(value: string): ValidationError | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: review regexp vulnerability
|
// TODO: review regexp vulnerability
|
||||||
function validateEmail(value: string): ValidationError | null {
|
function validateEmail(value: string, category: string): ValidationError | null {
|
||||||
const isValid = String(value)
|
const isValid = String(value)
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.match(
|
.match(
|
||||||
|
@ -122,13 +140,14 @@ function validateEmail(value: string): ValidationError | null {
|
||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
error: 'email',
|
error: 'email',
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateMinLength(value: string, limit: number): ValidationError | null {
|
function validateMinLength(value: string, limit: number, category: string): ValidationError | null {
|
||||||
const isValid = value.length >= limit;
|
const isValid = value.length >= limit;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
@ -136,13 +155,14 @@ function validateMinLength(value: string, limit: number): ValidationError | null
|
||||||
value,
|
value,
|
||||||
error: 'minLength',
|
error: 'minLength',
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateMaxLength(value: string, limit: number): ValidationError | null {
|
function validateMaxLength(value: string, limit: number, category: string): ValidationError | null {
|
||||||
const isValid = value.length <= limit;
|
const isValid = value.length <= limit;
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
@ -150,6 +170,7 @@ function validateMaxLength(value: string, limit: number): ValidationError | null
|
||||||
value,
|
value,
|
||||||
error: 'minLength',
|
error: 'minLength',
|
||||||
limit: limit,
|
limit: limit,
|
||||||
|
category,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue