feat(form): implement FormControl and ControlConfig prop triggerValidationOn (#224)

* Added ValidationHooks and attribute to FormControl

* Event listener uses data attribute to set type

* Changed default event listener to blur

* Add validation data attribute to other components

* Adjusted querySelector to remove condition

* Added nullish operator to prevent error

* Added optional chaining to validator
This commit is contained in:
Alexander Samaniego 2022-12-06 06:44:52 -05:00 committed by GitHub
parent 4b5b81a2b5
commit 5b539c809c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 33 additions and 21 deletions

View file

@ -1,4 +1,4 @@
import type { ValidatorRules } from "./validator.types";
import type { ValidationHooks, ValidatorRules } from "./validator.types";
/**
* `ControlType` determines the type of form control
@ -37,6 +37,7 @@ export interface ControlBase {
label?: string;
placeholder?: string;
validators?: ValidatorRules;
triggerValidationOn?: ValidationHooks;
}
export interface Checkbox extends ControlBase {

View file

@ -1,5 +1,7 @@
export type HookType = "onSubmit" | "onControlBlur" | "all";
export type ValidationHooks = "" | "blur" | "keypress" | "click"; // More to be added
export type CategoryType = "error" | "warn" | "info";
export type ValidatorRules =

View file

@ -26,6 +26,7 @@ const options = control.options.map((option: string | ControlOption) => {
name={control.name}
id={control.id}
disabled={readOnly || null}
data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null}
>
{
control?.placeholder && (
@ -36,10 +37,7 @@ const options = control.options.map((option: string | ControlOption) => {
}
{
options.map((option: ControlOption) => (
<option
value={option.value}
selected={option.value === control.value}
>
<option value={option.value} selected={option.value === control.value}>
{option.label}
</option>
))

View file

@ -46,6 +46,7 @@ const validatorAttributes: Record<string, string> = validators.reduce((prev, val
data-validator-error={hasError ? hasError.toString() : null}
data-validator-warn={hasWarn ? hasWarn.toString() : null}
data-validator-info={hasInfo ? hasInfo.toString() : null}
data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null}
readonly={readOnly || null}
disabled={(readOnly || null) && control.type === 'checkbox'}
{...validatorAttributes}

View file

@ -33,6 +33,7 @@ const options = control.options.map((option: string | ControlOption) => {
checked={option.value === control.value}
readonly={readOnly || null}
disabled={readOnly || null}
data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null}
/>
<label for={control.id + '-' + index}>{option.label}</label>
</div>

View file

@ -38,6 +38,7 @@ const validatorAttributes: Record<string, string> = validators.reduce((prev, val
cols={control?.cols ?? 21}
data-label={control?.label}
readonly={readOnly || null}
data-validation-on={control.triggerValidationOn ? control.triggerValidationOn : null}
{...validatorAttributes}
>
{control.value}

View file

@ -10,6 +10,7 @@ import type {
TextArea,
ControlBase,
ValidatorRules,
ValidationHooks,
} from '@astro-reactive/common';
import ShortUniqueId from 'short-unique-id';
@ -25,6 +26,7 @@ export class FormControl {
private _isPristine = true;
private _placeholder: string | null = null;
private _validators: ValidatorRules = [];
private _triggerValidationOn: ValidationHooks;
private _errors: ValidationError[] = [];
private _options: string[] | ControlOption[] = [];
private _rows: number | null = null;
@ -47,6 +49,7 @@ export class FormControl {
label = '',
placeholder = null,
validators = [],
triggerValidationOn = 'blur',
} = config;
const uid = new ShortUniqueId({ length: 9 });
@ -57,6 +60,7 @@ export class FormControl {
this._label = label;
this._placeholder = placeholder;
this._validators = validators;
this._triggerValidationOn = triggerValidationOn;
if (type === 'radio' || type === 'dropdown') {
const { options = [] } = config as Radio | Dropdown;
@ -111,6 +115,10 @@ export class FormControl {
return this._validators;
}
get triggerValidationOn() {
return this._triggerValidationOn;
}
get errors() {
return this._errors;
}

View file

@ -28,10 +28,10 @@ const { hook = 'all', displayErrorMessages = false } = Astro.props;
import { clearErrors, validate } from './core';
// const hook: HookType = (document.getElementById('hook') as HTMLInputElement).value as HookType;
const inputs = [...document.querySelectorAll('form input')] as HTMLInputElement[];
const inputs = [...document.querySelectorAll('body *[data-validation-on]')] as HTMLInputElement[];
inputs?.forEach((input) => {
input.addEventListener('blur', (e: Event) => {
input.addEventListener(input.dataset?.validationOn ?? 'blur', (e: Event) => {
// NOTE: event target attribute names are converted to lowercase
const element = e.target as HTMLInputElement;
const attributeNames = element?.getAttributeNames() || [];