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:
parent
4b5b81a2b5
commit
5b539c809c
8 changed files with 33 additions and 21 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -6,7 +6,7 @@ import type { Dropdown, ControlOption } from '@astro-reactive/common';
|
|||
|
||||
export interface Props {
|
||||
control: Dropdown;
|
||||
readOnly?: boolean;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
const { control, readOnly } = Astro.props;
|
||||
|
@ -26,22 +26,20 @@ 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 && (
|
||||
<option value="" disabled selected={!control?.value}>
|
||||
{control.placeholder}
|
||||
</option>
|
||||
)
|
||||
}
|
||||
{
|
||||
options.map((option: ControlOption) => (
|
||||
<option
|
||||
value={option.value}
|
||||
selected={option.value === control.value}
|
||||
>
|
||||
{option.label}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
{
|
||||
control?.placeholder && (
|
||||
<option value="" disabled selected={!control?.value}>
|
||||
{control.placeholder}
|
||||
</option>
|
||||
)
|
||||
}
|
||||
{
|
||||
options.map((option: ControlOption) => (
|
||||
<option value={option.value} selected={option.value === control.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))
|
||||
}
|
||||
</select>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() || [];
|
||||
|
|
Loading…
Reference in a new issue