Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@lion/form-core

Package Overview
Dependencies
Maintainers
1
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lion/form-core - npm Package Compare versions

Comparing version 0.16.0 to 0.17.0

test-helpers/mimicUserInput.d.ts

83

docs/overview.md
# Systems >> Form >> Overview ||10
The Form System allows you to create complex forms with various validations in an easy way.
This page should be used as a starting point when first using the Form System.
It provides an overview of its essential building blocks and provides links to detailed explanations of most of its core concepts.
## Features
## Building Blocks
- Built in [validate](https://github.com/ing-bank/lion/blob/7c52c12bfb025c98f93b5f0be984cedd3028a20a/docs/docs/systems/form/validate.md) for error/warning/info/success
- Formatting of values
- Accessible
Our Form System is built from a set of very fundamental building blocks: `form control`s, `field`s and `fieldset`s.
### Form Controls
`Form control`s are the most fundamental building blocks of our Form System.
They are the fundament of both `field`s, and `fieldset`s and provide a normalized, predictable API throughout the whole form. Every form element inherits from `FormControlMixin`.
`FormControlMixin` creates the default html structure and accessibility is designed to be used in conjunction with the ValidateMixin and the FormatMixin.
## Fields
Fields (think of an input, textarea, select) are the actual form controls the end user interacts with. They extend `LionField`, which in turn uses the `FormControlMixin`. Fields provide a normalized API for both platform components and custom made form controls.
On top of this, they feature:
- [formatting/parsing/serializing](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/formatting-and-parsing.md) of view values.
- Advanced [validation](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/validate.md) possibilities.
- Creation of advanced user interaction scenarios via [interaction states](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/interaction-states.md).
- Provision of labels and help texts in an easy, declarative manner.
- Accessibility out of the box.
- Advanced styling possibilities: map your own Design System to the internal HTML structure.
`Form control`s are the most fundamental building block of the Forms. They are the basis of
both `field`s and `fieldset`s, and the `form` itself.
### Platform fields (wrappers)
- [LionInput](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input/overview.md), a wrapper for `<input>`.
- [LionTextarea](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/textarea/overview.md), a wrapper for `<textarea>`.
- [LionSelect](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/select/overview.md), a wrapper for `<select>`.
### Custom fields (wrappers)
Whenever a native form control doesn't exist or is not sufficient, a [custom form field](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/guides/how-to/create-a-custom-field.md) should be created. One could think of components like:
- [LionCombobox](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/combobox/overview.md), a custom implementation of a combobox.
- [LionDate](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-date/overview.md), an alternative for `<input type="date">`.
- [LionDatepicker](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-datepicker/overview.md), an alternative for `<input type="date">` including a calendar dropdown.
- [LionListbox](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/listbox/overview.md), a custom implementation of a listbox.
- [LionInputAmount](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-amount/overview.md), an alternative for `<input type="number">` special for amounts.
- [LionInputEmail](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-email/overview.md), an alternative for `<input type="email">`.
- [LionInputIban](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-iban/overview.md), an ING specific for an input with IBAN numbers.
- [LionInputRange](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-range/overview.md), an alternative for `<input type="range">`.
- [LionInputStepper](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-stepper/overview.md), an alternative for `<input type="number">`.
- [LionSelectRich](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/select-rich/overview.md), an alternative for `<select>` with multiline options.
### Choice Input Fields
For form controls which return a `checked-state` you can use the `lion-choice-input` mixin. It is used in:
- [LionCheckbox](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/checkbox-group/overview.md), a wrapper for `<input type="checkbox">`.
- LionOption, an alternative for `<option>`.
- [LionRadio](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/radio-group/overview.md), a wrapper for `<input type="radio">`.
- [LionSwitch](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/switch/overview.md), a custom implementation of a switch.
Which contains the following features:
- Get or set the value of the choice - `choiceValue()`.
- Get or set the modelValue (value and checked-state) of the choice - `.modelValue`.
- Pre-select an option by setting the `checked` boolean attribute.
## Fieldsets
Fieldsets are groups of fields. They can be considered fields on their own as well, since they partly share the normalized API via `FormControlMixin`. Fieldsets are the basis for:
- [LionFieldset](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/fieldset/overview.md), a wrapper around multiple input fields or other fieldsets.
- [LionForm](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/form/overview.md), enhances the functionality of the native `<form>` component.
- [LionCheckboxGroup](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/checkbox-group/overview.md), a wrapper component for multiple checkboxes.
- [LionRadioGroup](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/radio-group/overview.md), a wrapper component for multiple radio inputs.
## Other Resources
- [Model Value](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/model-value.md)
- [Formatting and parsing](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/formatting-and-parsing.md)
- [Interaction states](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/interaction-states.md)
- [Validation System](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/validate.md)

6

package.json
{
"name": "@lion/form-core",
"version": "0.16.0",
"version": "0.17.0",
"description": "Form-core contains all essential building blocks for creating form fields and fieldsets",

@@ -41,4 +41,4 @@ "license": "MIT",

"dependencies": {
"@lion/core": "^0.21.0",
"@lion/localize": "^0.23.0"
"@lion/core": "^0.22.0",
"@lion/localize": "^0.24.0"
},

@@ -45,0 +45,0 @@ "keywords": [

# Systems >> Form >> Overview ||10
The Form System allows you to create complex forms with various validations in an easy way.
This page should be used as a starting point when first using the Form System.
It provides an overview of its essential building blocks and provides links to detailed explanations of most of its core concepts.
## Features
## Building Blocks
- Built in [validate](https://github.com/ing-bank/lion/blob/7c52c12bfb025c98f93b5f0be984cedd3028a20a/docs/docs/systems/form/validate.md) for error/warning/info/success
- Formatting of values
- Accessible
Our Form System is built from a set of very fundamental building blocks: `form control`s, `field`s and `fieldset`s.
### Form Controls
`Form control`s are the most fundamental building blocks of our Form System.
They are the fundament of both `field`s, and `fieldset`s and provide a normalized, predictable API throughout the whole form. Every form element inherits from `FormControlMixin`.
`FormControlMixin` creates the default html structure and accessibility is designed to be used in conjunction with the ValidateMixin and the FormatMixin.
## Fields
Fields (think of an input, textarea, select) are the actual form controls the end user interacts with. They extend `LionField`, which in turn uses the `FormControlMixin`. Fields provide a normalized API for both platform components and custom made form controls.
On top of this, they feature:
- [formatting/parsing/serializing](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/formatting-and-parsing.md) of view values.
- Advanced [validation](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/validate.md) possibilities.
- Creation of advanced user interaction scenarios via [interaction states](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/interaction-states.md).
- Provision of labels and help texts in an easy, declarative manner.
- Accessibility out of the box.
- Advanced styling possibilities: map your own Design System to the internal HTML structure.
`Form control`s are the most fundamental building block of the Forms. They are the basis of
both `field`s and `fieldset`s, and the `form` itself.
### Platform fields (wrappers)
- [LionInput](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input/overview.md), a wrapper for `<input>`.
- [LionTextarea](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/textarea/overview.md), a wrapper for `<textarea>`.
- [LionSelect](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/select/overview.md), a wrapper for `<select>`.
### Custom fields (wrappers)
Whenever a native form control doesn't exist or is not sufficient, a [custom form field](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/guides/how-to/create-a-custom-field.md) should be created. One could think of components like:
- [LionCombobox](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/combobox/overview.md), a custom implementation of a combobox.
- [LionDate](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-date/overview.md), an alternative for `<input type="date">`.
- [LionDatepicker](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-datepicker/overview.md), an alternative for `<input type="date">` including a calendar dropdown.
- [LionListbox](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/listbox/overview.md), a custom implementation of a listbox.
- [LionInputAmount](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-amount/overview.md), an alternative for `<input type="number">` special for amounts.
- [LionInputEmail](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-email/overview.md), an alternative for `<input type="email">`.
- [LionInputIban](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-iban/overview.md), an ING specific for an input with IBAN numbers.
- [LionInputRange](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-range/overview.md), an alternative for `<input type="range">`.
- [LionInputStepper](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/input-stepper/overview.md), an alternative for `<input type="number">`.
- [LionSelectRich](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/select-rich/overview.md), an alternative for `<select>` with multiline options.
### Choice Input Fields
For form controls which return a `checked-state` you can use the `lion-choice-input` mixin. It is used in:
- [LionCheckbox](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/checkbox-group/overview.md), a wrapper for `<input type="checkbox">`.
- LionOption, an alternative for `<option>`.
- [LionRadio](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/radio-group/overview.md), a wrapper for `<input type="radio">`.
- [LionSwitch](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/switch/overview.md), a custom implementation of a switch.
Which contains the following features:
- Get or set the value of the choice - `choiceValue()`.
- Get or set the modelValue (value and checked-state) of the choice - `.modelValue`.
- Pre-select an option by setting the `checked` boolean attribute.
## Fieldsets
Fieldsets are groups of fields. They can be considered fields on their own as well, since they partly share the normalized API via `FormControlMixin`. Fieldsets are the basis for:
- [LionFieldset](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/fieldset/overview.md), a wrapper around multiple input fields or other fieldsets.
- [LionForm](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/form/overview.md), enhances the functionality of the native `<form>` component.
- [LionCheckboxGroup](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/checkbox-group/overview.md), a wrapper component for multiple checkboxes.
- [LionRadioGroup](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/components/radio-group/overview.md), a wrapper component for multiple radio inputs.
## Other Resources
- [Model Value](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/model-value.md)
- [Formatting and parsing](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/formatting-and-parsing.md)
- [Interaction states](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/interaction-states.md)
- [Validation System](https://github.com/ing-bank/lion/blob/24b205837eb6a0111d8c80077dcab0b5040533e0/docs/fundamentals/systems/form/validate.md)
export class FormElementsHaveNoError extends Validator {
static get validatorName(): string;
static getMessage(): Promise<string>;
}
import { Validator } from "../validate/Validator.js";
export type FormatMixin = import('../types/FormatMixinTypes').FormatMixin;
export const FormatMixin: typeof import("../types/FormatMixinTypes").FormatImplementation;
export type FormatOptions = import('@lion/localize/types/LocalizeMixinTypes').FormatNumberOptions;
export type FormatOptions = import('../types/FormatMixinTypes').FormatOptions;
export type ModelValueEventDetails = import('../types/FormControlMixinTypes.js').ModelValueEventDetails;

@@ -10,3 +10,3 @@ /* eslint-disable class-methods-use-this */

* @typedef {import('../types/FormatMixinTypes').FormatMixin} FormatMixin
* @typedef {import('@lion/localize/types/LocalizeMixinTypes').FormatNumberOptions} FormatOptions
* @typedef {import('../types/FormatMixinTypes').FormatOptions} FormatOptions
* @typedef {import('../types/FormControlMixinTypes.js').ModelValueEventDetails} ModelValueEventDetails

@@ -111,4 +111,9 @@ */

/**
* Preprocesses the viewValue before it's parsed to a modelValue. Can be used to filter
* invalid input amongst others.
* Preprocessors could be considered 'live formatters'. Their result is shown to the user
* on keyup instead of after blurring the field. The biggest difference between preprocessors
* and formatters is their moment of execution: preprocessors are run before modelValue is
* computed (and work based on view value), whereas formatters are run after the parser (and
* are based on modelValue)
* Automatically formats code while typing. It depends on a preprocessro that smartly
* updates the viewValue and caret position for best UX.
* @example

@@ -120,8 +125,9 @@ * ```js

* }
* ```
* @param {string} v - the raw value from the <input> after keyUp/Down event
* @returns {string} preprocessedValue: the result of preprocessing for invalid input
* @param {FormatOptions & { prevViewValue: string; currentCaretIndex: number }} opts - the raw value from the <input> after keyUp/Down event
* @returns {{ viewValue:string; caretIndex:number; }|string|undefined} preprocessedValue: the result of preprocessing for invalid input
*/
preprocessor(v) {
return v;
// eslint-disable-next-line no-unused-vars
preprocessor(v, opts) {
return undefined;
}

@@ -210,2 +216,3 @@

this.__preventRecursiveTrigger = false;
this.__prevViewValue = this.value;
}

@@ -225,3 +232,2 @@

// For backwards compatibility we return an empty string:
// - it triggers validation for required validators (see ValidateMixin.validate())
// - it can be expected by 3rd parties (for instance unit tests)

@@ -232,3 +238,3 @@ // TODO(@tlouisse): In a breaking refactor of the Validation System, this behavior can be corrected.

// A.2) Handle edge cases We might have no view value yet, for instance because
// A.2) Handle edge cases. We might have no view value yet, for instance because
// _inputNode.value was not available yet

@@ -272,4 +278,3 @@ if (typeof value !== 'string') {

this._isHandlingUserInput &&
this.hasFeedbackFor &&
this.hasFeedbackFor.length &&
this.hasFeedbackFor?.length &&
this.hasFeedbackFor.includes('error') &&

@@ -292,2 +297,4 @@ this._inputNode

/**
* Responds to modelValue changes in the synchronous cycle (most subclassers should listen to
* the asynchronous cycle ('modelValue' in the .updated lifecycle))
* @param {{ modelValue: unknown; }[]} args

@@ -331,3 +338,3 @@ * @protected

if (!this.__isHandlingComposition) {
this.value = this.preprocessor(this.value);
this.__handlePreprocessor();
}

@@ -342,7 +349,44 @@ const prevFormatted = this.formattedValue;

}
/** @type {string} */
this.__prevViewValue = this.value;
}
/**
* Handle view value and caretIndex, depending on return type of .preprocessor.
* @private
*/
__handlePreprocessor() {
const unprocessedValue = this.value;
let currentCaretIndex = this.value.length;
// Be gentle with Safari
if (
this._inputNode &&
'selectionStart' in this._inputNode &&
/** @type {HTMLInputElement} */ (this._inputNode)?.type !== 'range'
) {
currentCaretIndex = /** @type {number} */ (this._inputNode.selectionStart);
}
const preprocessedValue = this.preprocessor(this.value, {
...this.formatOptions,
currentCaretIndex,
prevViewValue: this.__prevViewValue,
});
this.__prevViewValue = unprocessedValue;
if (preprocessedValue === undefined) {
// Make sure we do no set back original value, so we preserve
// caret index (== selectionStart/selectionEnd)
return;
}
if (typeof preprocessedValue === 'string') {
this.value = preprocessedValue;
} else if (typeof preprocessedValue === 'object') {
const { viewValue, caretIndex } = preprocessedValue;
this.value = viewValue;
if (caretIndex && this._inputNode && 'selectionStart' in this._inputNode) {
this._inputNode.selectionStart = caretIndex;
this._inputNode.selectionEnd = caretIndex;
}
}
}
/**
* Synchronization from `LionField.value` to `._inputNode.value`

@@ -364,3 +408,3 @@ * - flow [1] will always be reflected back

* modelValue change), this condition is checked. When enhancing it, it's recommended to
* call `super._reflectBackOn()`
* call via `return this._myExtraCondition && super._reflectBackOn()`
* @overridable

@@ -504,2 +548,5 @@ * @return {boolean}

/**
* @private
*/
__onPaste() {

@@ -525,2 +572,5 @@ this._isPasting = true;

}
/** @type {string} */
this.__prevViewValue = this.value;
this._reflectBackFormattedValueToUser();

@@ -527,0 +577,0 @@

@@ -46,2 +46,3 @@ import { css, dedupeMixin, html, nothing, SlotMixin, DisabledMixin } from '@lion/core';

label: String, // FIXME: { attribute: false } breaks a bunch of tests, but shouldn't...
labelSrOnly: { type: Boolean, attribute: 'label-sr-only', reflect: true },
helpText: { type: String, attribute: 'help-text' },

@@ -191,2 +192,8 @@ modelValue: { attribute: false },

/**
* The label will only be visible for srceen readers when true
* @type {boolean}
*/
this.labelSrOnly = false;
/**
* The helpt text for the input node.

@@ -704,2 +711,16 @@ * When no value is defined, textContent of [slot=help-text] will be used

:host([label-sr-only]) .form-field__label {
position: absolute;
top: 0;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(100%);
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap;
border: 0;
margin: 0;
padding: 0;
}
/***********************

@@ -706,0 +727,0 @@ {block} .input-group

@@ -1,2 +0,2 @@

declare const LionField_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/SlotMixinTypes").SlotHost> & Pick<typeof import("@lion/core/types/SlotMixinTypes").SlotHost, "prototype"> & Pick<typeof LitElement, "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "shadowRootOptions"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/validate/ValidateMixinTypes.js").ValidateHost> & Pick<typeof import("../types/validate/ValidateMixinTypes.js").ValidateHost, "prototype" | "validationTypes"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormControlMixinTypes.js").FormControlHost> & Pick<typeof import("../types/FormControlMixinTypes.js").FormControlHost, "prototype" | "properties" | "styles"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/utils/SyncUpdatableMixinTypes.js").SyncUpdatableHost> & Pick<typeof import("../types/utils/SyncUpdatableMixinTypes.js").SyncUpdatableHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/DisabledMixinTypes").DisabledHost> & Pick<typeof import("@lion/core/types/DisabledMixinTypes").DisabledHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/src/types").ScopedElementsHost> & Pick<typeof import("@open-wc/scoped-elements/src/types").ScopedElementsHost, "prototype" | "shadowRootOptions" | "scopedElements"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormatMixinTypes.js").FormatHost> & Pick<typeof import("../types/FormatMixinTypes.js").FormatHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/FocusMixinTypes.js").FocusHost> & Pick<typeof import("../types/FocusMixinTypes.js").FocusHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/InteractionStateMixinTypes.js").InteractionStateHost> & Pick<typeof import("../types/InteractionStateMixinTypes.js").InteractionStateHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/registration/FormRegisteringMixinTypes.js").FormRegisteringHost> & Pick<typeof import("../types/registration/FormRegisteringMixinTypes.js").FormRegisteringHost, "prototype">;
declare const LionField_base: typeof LitElement & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/SlotMixinTypes").SlotHost> & Pick<typeof import("@lion/core/types/SlotMixinTypes").SlotHost, "prototype"> & Pick<typeof LitElement, "prototype" | "_$litElement$" | "enabledWarnings" | "enableWarning" | "disableWarning" | "addInitializer" | "_initializers" | "elementProperties" | "properties" | "elementStyles" | "styles" | "observedAttributes" | "createProperty" | "shadowRootOptions"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/index.js").ValidateHost> & Pick<typeof import("../types/index.js").ValidateHost, "prototype" | "validationTypes"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormControlMixinTypes.js").FormControlHost> & Pick<typeof import("../types/FormControlMixinTypes.js").FormControlHost, "prototype" | "properties" | "styles"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/index.js").SyncUpdatableHost> & Pick<typeof import("../types/index.js").SyncUpdatableHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("@lion/core/types/DisabledMixinTypes").DisabledHost> & Pick<typeof import("@lion/core/types/DisabledMixinTypes").DisabledHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/src/types").ScopedElementsHost> & Pick<typeof import("@open-wc/scoped-elements/src/types").ScopedElementsHost, "prototype" | "shadowRootOptions" | "scopedElements"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/FormatMixinTypes.js").FormatHost> & Pick<typeof import("../types/FormatMixinTypes.js").FormatHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/FocusMixinTypes.js").FocusHost> & Pick<typeof import("../types/FocusMixinTypes.js").FocusHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/InteractionStateMixinTypes.js").InteractionStateHost> & Pick<typeof import("../types/InteractionStateMixinTypes.js").InteractionStateHost, "prototype"> & import("@open-wc/dedupe-mixin").Constructor<import("../types/index.js").FormRegisteringHost> & Pick<typeof import("../types/index.js").FormRegisteringHost, "prototype">;
/**

@@ -3,0 +3,0 @@ * `LionField`: wraps <input>, <textarea>, <select> and other interactable elements.

@@ -9,3 +9,3 @@ /**

* The model(value) concept as implemented in lion-web is conceptually comparable to those found in
* popular frameworks like Angular and Vue.
* popular systems like Angular and Vue.

@@ -12,0 +12,0 @@ * The Unparseable type is an addition on top of this that mainly is added for the following two

@@ -9,3 +9,3 @@ /**

* The model(value) concept as implemented in lion-web is conceptually comparable to those found in
* popular frameworks like Angular and Vue.
* popular systems like Angular and Vue.

@@ -12,0 +12,0 @@ * The Unparseable type is an addition on top of this that mainly is added for the following two

@@ -12,1 +12,13 @@ /**

export type ValidationType = import('../../types/validate/ValidateMixinTypes').ValidationType;
export type ValidateHost = import('../../types/validate/ValidateMixinTypes').ValidateHost;
export type ValidateHostConstructor = typeof import('../../types/validate/ValidateMixinTypes').ValidateHost;
export type ValidationResultEntry = {
validator: Validator;
outcome: boolean | string;
};
export type ValidationStates = {
[type: string]: {
[validatorName: string]: string | boolean;
};
};
import { Validator } from "./Validator.js";

@@ -20,2 +20,6 @@ /* eslint-disable class-methods-use-this, camelcase, no-param-reassign, max-classes-per-file */

* @typedef {import('../../types/validate/ValidateMixinTypes').ValidationType} ValidationType
* @typedef {import('../../types/validate/ValidateMixinTypes').ValidateHost} ValidateHost
* @typedef {typeof import('../../types/validate/ValidateMixinTypes').ValidateHost} ValidateHostConstructor
* @typedef {{validator:Validator; outcome:boolean|string}} ValidationResultEntry
* @typedef {{[type:string]: {[validatorName:string]:boolean|string}}} ValidationStates
*/

@@ -107,4 +111,3 @@

feedback: () => {
// @ts-ignore we load a polyfill to support createElement on shadowRoot
const feedbackEl = this.shadowRoot.createElement('lion-validation-feedback');
const feedbackEl = this.createScopedElement('lion-validation-feedback');
feedbackEl.setAttribute('data-tag-name', 'lion-validation-feedback');

@@ -164,3 +167,3 @@ return feedbackEl;

// TODO: [v1] make this fully private (preifix __)?
// TODO: [v1] make this fully private (prefix __)?
/**

@@ -177,3 +180,3 @@ * A temporary storage to transition from hasFeedbackFor to showsFeedbackFor

* @readOnly
* @type {Object.<string, Object.<string, boolean>>}
* @type {ValidationStates}
*/

@@ -213,2 +216,3 @@ this.validationStates = {};

* The amount of feedback messages that will visible in LionValidationFeedback
* @configurable
* @protected

@@ -219,3 +223,3 @@ */

/**
* @type {Validator[]}
* @type {ValidationResultEntry[]}
* @private

@@ -226,3 +230,3 @@ */

/**
* @type {Validator[]}
* @type {ValidationResultEntry[]}
* @private

@@ -234,3 +238,3 @@ */

* Aggregated result from sync Validators, async Validators and ResultValidators
* @type {Validator[]}
* @type {ValidationResultEntry[]}
* @private

@@ -241,3 +245,3 @@ */

/**
* @type {Validator[]}
* @type {ValidationResultEntry[]}
* @private

@@ -248,3 +252,3 @@ */

/**
* @type {Validator[]}
* @type {ValidationResultEntry[]}
* @private

@@ -256,3 +260,3 @@ */

* The updated children validity affects the validity of the parent. Helper to recompute
* validatity of parent FormGroup
* validity of parent FormGroup
* @private

@@ -262,5 +266,5 @@ */

/** @private */
this.__onValidatorUpdated = this.__onValidatorUpdated.bind(this);
/** @protected */
this._onValidatorUpdated = this._onValidatorUpdated.bind(this);
/** @protected */
this._updateFeedbackComponent = this._updateFeedbackComponent.bind(this);

@@ -352,3 +356,3 @@ }

* - change in the 'validators' array
* - change in the config of an individual Validator
* - change in the config of an individual Validator
*

@@ -400,2 +404,10 @@ * Three situations are handled:

async __executeValidators() {
/**
* Allows Application Developer to wait for (async) validation
* @example
* ```js
* await el.validateComplete;
* ```
* @type {Promise<boolean>}
*/
this.validateComplete = new Promise(resolve => {

@@ -427,3 +439,3 @@ this.__validateCompleteResolve = resolve;

if (requiredValidator) {
this.__syncValidationResult = [requiredValidator];
this.__syncValidationResult = [{ validator: requiredValidator, outcome: true }];
}

@@ -469,5 +481,8 @@ this.__finishValidation({ source: 'sync' });

if (syncValidators.length) {
this.__syncValidationResult = syncValidators.filter(v =>
v.execute(value, v.param, { node: this }),
);
this.__syncValidationResult = syncValidators
.map(v => ({
validator: v,
outcome: /** @type {boolean|string} */ (v.execute(value, v.param, { node: this })),
}))
.filter(v => Boolean(v.outcome));
}

@@ -487,6 +502,11 @@ this.__finishValidation({ source: 'sync', hasAsync });

const resultPromises = asyncValidators.map(v => v.execute(value, v.param, { node: this }));
const booleanResults = await Promise.all(resultPromises);
this.__asyncValidationResult = booleanResults
.map((r, i) => asyncValidators[i]) // Create an array of Validators
.filter((v, i) => booleanResults[i]); // Only leave the ones returning true
const asyncExecutionResults = await Promise.all(resultPromises);
this.__asyncValidationResult = asyncExecutionResults
.map((r, i) => ({
validator: asyncValidators[i],
outcome: /** @type {boolean|string} */ (asyncExecutionResults[i]),
}))
.filter(v => Boolean(v.outcome));
this.__finishValidation({ source: 'async' });

@@ -499,3 +519,3 @@ this.isPending = false;

* step b (as explained in `validate()`), called by __finishValidation
* @param {Validator[]} regularValidationResult result of steps 1-3
* @param {{validator:Validator; outcome: boolean|string;}[]} regularValidationResult result of steps 1-3
* @private

@@ -511,9 +531,17 @@ */

return resultValidators.filter(v =>
v.executeOnResults({
regularValidationResult,
prevValidationResult: this.__prevValidationResult,
prevShownValidationResult: this.__prevShownValidationResult,
}),
);
// Map everything to Validator[] for backwards compatibility
return resultValidators
.map(v => ({
validator: v,
outcome: /** @type {boolean|string} */ (
v.executeOnResults({
regularValidationResult: regularValidationResult.map(entry => entry.validator),
prevValidationResult: this.__prevValidationResult.map(entry => entry.validator),
prevShownValidationResult: this.__prevShownValidationResult.map(
entry => entry.validator,
),
})
),
}))
.filter(v => Boolean(v.outcome));
}

@@ -534,10 +562,6 @@

this.__validationResult = [...resultOutCome, ...syncAndAsyncOutcome];
// this._storeResultsOnInstance(this.__validationResult);
const ctor =
/** @type {typeof import('../../types/validate/ValidateMixinTypes').ValidateHost} */ (
this.constructor
);
const ctor = /** @type {ValidateHostConstructor} */ (this.constructor);
/** @type {Object.<string, Object.<string, boolean>>} */
/** @type {ValidationStates} */
const validationStates = ctor.validationTypes.reduce(

@@ -547,12 +571,14 @@ (acc, type) => ({ ...acc, [type]: {} }),

);
this.__validationResult.forEach(v => {
if (!validationStates[v.type]) {
validationStates[v.type] = {};
this.__validationResult.forEach(({ validator, outcome }) => {
if (!validationStates[validator.type]) {
validationStates[validator.type] = {};
}
const vCtor = /** @type {typeof Validator} */ (v.constructor);
validationStates[v.type][vCtor.validatorName] = true;
const vCtor = /** @type {typeof Validator} */ (validator.constructor);
validationStates[validator.type][vCtor.validatorName] = outcome;
});
this.validationStates = validationStates;
this.hasFeedbackFor = [...new Set(this.__validationResult.map(v => v.type))];
this.hasFeedbackFor = [
...new Set(this.__validationResult.map(({ validator }) => validator.type)),
];

@@ -563,4 +589,3 @@ /** private event that should be listened to by LionFieldSet */

if (this.__validateCompleteResolve) {
// @ts-ignore [allow-private]
this.__validateCompleteResolve();
this.__validateCompleteResolve(true);
}

@@ -580,5 +605,5 @@ }

* @param {Event|CustomEvent} e
* @private
* @protected
*/
__onValidatorUpdated(e) {
_onValidatorUpdated(e) {
if (e.type === 'param-changed' || e.type === 'config-changed') {

@@ -598,3 +623,3 @@ this.validate();

if (v.removeEventListener) {
v.removeEventListener(e, this.__onValidatorUpdated);
v.removeEventListener(e, this._onValidatorUpdated);
}

@@ -614,6 +639,3 @@ });

}
const ctor =
/** @type {typeof import('../../types/validate/ValidateMixinTypes').ValidateHost} */ (
this.constructor
);
const ctor = /** @type {ValidateHostConstructor} */ (this.constructor);
if (ctor.validationTypes.indexOf(v.type) === -1) {

@@ -627,5 +649,11 @@ const vCtor = /** @type {typeof Validator} */ (v.constructor);

}
events.forEach(e => {
/** Updated the code to fix issue #1607 to sync the calendar date with validators params
* Here _onValidatorUpdated is responsible for responding to the event
*/
events.forEach(eventName => {
if (v.addEventListener) {
v.addEventListener(e, this.__onValidatorUpdated);
v.addEventListener(eventName, e => {
// @ts-ignore for making validator param dynamic
this._onValidatorUpdated(e, { validator: v });
});
}

@@ -669,10 +697,10 @@ });

/**
* @param {Validator[]} validators list of objects having a .getMessage method
* @param {ValidationResultEntry[]} validationResults list of objects having a .getMessage method
* @return {Promise.<FeedbackMessage[]>}
* @private
*/
async __getFeedbackMessages(validators) {
async __getFeedbackMessages(validationResults) {
let fieldName = await this.fieldName;
return Promise.all(
validators.map(async validator => {
validationResults.map(async ({ validator, outcome }) => {
if (validator.config.fieldName) {

@@ -686,2 +714,3 @@ fieldName = await validator.config.fieldName;

fieldName,
outcome,
});

@@ -721,6 +750,15 @@ return { message, type: validator.type, validator };

/** @type {Validator[]} */
this.__prioritizedResult = this._prioritizeAndFilterFeedback({
validationResult: this.__validationResult,
const prioritizedValidators = this._prioritizeAndFilterFeedback({
validationResult: this.__validationResult.map(entry => entry.validator),
});
this.__prioritizedResult = prioritizedValidators
.map(v => {
const found = /** @type {ValidationResultEntry} */ (
this.__validationResult.find(r => v === r.validator)
);
return found;
})
.filter(Boolean);
if (this.__prioritizedResult.length > 0) {

@@ -764,3 +802,3 @@ this.__prevShownValidationResult = this.__prioritizedResult;

/**
* Allows the end user to specify when a feedback message should be shown
* Allows the Application Developer to specify when a feedback message should be shown
* @example

@@ -770,3 +808,3 @@ * ```js

* if (type === 'info') {
* return return;
* return true;
* } else if (type === 'prefilledOnly') {

@@ -809,3 +847,5 @@ * return meta.prefilled;

/** @param {import('@lion/core').PropertyValues} changedProperties */
/**
* @param {import('@lion/core').PropertyValues} changedProperties
*/
updated(changedProperties) {

@@ -818,6 +858,3 @@ super.updated(changedProperties);

) {
const ctor =
/** @type {typeof import('../../types/validate/ValidateMixinTypes').ValidateHost} */ (
this.constructor
);
const ctor = /** @type {ValidateHostConstructor} */ (this.constructor);
// Necessary typecast because types aren't smart enough to understand that we filter out undefined

@@ -858,6 +895,3 @@ this.showsFeedbackFor = /** @type {string[]} */ (

_updateShouldShowFeedbackFor() {
const ctor =
/** @type {typeof import('../../types/validate/ValidateMixinTypes').ValidateHost} */ (
this.constructor
);
const ctor = /** @type {ValidateHostConstructor} */ (this.constructor);

@@ -885,4 +919,4 @@ // Necessary typecast because types aren't smart enough to understand that we filter out undefined

/**
* Orders all active validators in this.__validationResult. Can
* also filter out occurrences (based on interaction states)
* Orders all active validators in this.__validationResult.
* Can also filter out occurrences (based on interaction states)
* @overridable

@@ -894,6 +928,3 @@ * @param {{ validationResult: Validator[] }} opts

_prioritizeAndFilterFeedback({ validationResult }) {
const ctor =
/** @type {typeof import('../../types/validate/ValidateMixinTypes').ValidateHost} */ (
this.constructor
);
const ctor = /** @type {ValidateHostConstructor} */ (this.constructor);
const types = ctor.validationTypes;

@@ -900,0 +931,0 @@ // Sort all validators based on the type provided.

/**
* @typedef {object} MessageData
* @property {*} [MessageData.modelValue]
* @property {string} [MessageData.fieldName]
* @property {HTMLElement} [MessageData.formControl]
* @property {string} [MessageData.type]
* @property {Object.<string,?>} [MessageData.config]
* @property {string} [MessageData.name]
* @typedef {import('../../types/validate/validate').FeedbackMessageData} FeedbackMessageData
* @typedef {import('../../types/validate/validate').ValidatorParam} ValidatorParam
* @typedef {import('../../types/validate/validate').ValidatorConfig} ValidatorConfig
* @typedef {import('../../types/validate/validate').ValidatorOutcome} ValidatorOutcome
* @typedef {import('../../types/validate/validate').ValidatorName} ValidatorName
* @typedef {import('../../types/validate/validate').ValidationType} ValidationType
* @typedef {import('../FormControlMixin').FormControlHost} FormControlHost
*/
export class Validator {
static get validatorName(): string;
static get async(): boolean;
export class Validator extends EventTarget {
/**
* The name under which validation results get registered. For convience and predictability, this
* should always be the same as the constructor name (since it will be obfuscated in js builds,
* we need to provide it separately).
* @type {ValidatorName}
*/
static validatorName: ValidatorName;
/**
* Whether the validator is asynchronous or not. When true., this means execute function returns
* a Promise. This can be handy for:
* - server side calls
* - validations that are dependent on lazy loaded resources (they can be async until the dependency
* is loaded)
* @type {boolean}
*/
static async: boolean;
/**
* Called inside Validator.prototype._getMessage (see explanation).
* @example
* ```js
* class MyValidator extends Validator {
* static async getMessage() {
* return 'lowest prio, defined globally by Validator author'
* }
* }
* // globally overridden
* MyValidator.getMessage = async() => 'overrides already configured message';
* ```
* @overridable
* @param {MessageData} [data]
* @returns {Promise<string|Node>}
* @param {Partial<FeedbackMessageData>} [data]
* @returns {Promise<string|Element>}
*/
static getMessage(data?: MessageData | undefined): Promise<string | Node>;
static getMessage(data?: Partial<import("../../types/validate/validate").FeedbackMessageData> | undefined): Promise<string | Element>;
/**
*
* @param {?} [param]
* @param {Object.<string,?>} [config]
* @param {ValidatorParam} [param]
* @param {ValidatorConfig} [config]
*/
constructor(param?: unknown, config?: {
[x: string]: any;
} | undefined);
/** @type {?} */
__param: unknown;
/** @type {Object.<string,?>} */
__config: {
[x: string]: unknown;
};
constructor(param?: ValidatorParam, config?: import("../../types/validate/validate").ValidatorConfig | undefined);
/** @type {ValidatorParam} */
__param: ValidatorParam;
/** @type {ValidatorConfig} */
__config: ValidatorConfig;
/** @type {ValidationType} */
type: any;
/**
* @desc The function that returns a Boolean
* @param {?} [modelValue]
* @param {?} [param]
* @param {{}} [config]
* @returns {Boolean|Promise<Boolean>}
* The function that returns a validity outcome. When we need to show feedback,
* it should return true, otherwise false. So when an error\info|warning|success message
* needs to be shown, return true. For async Validators, the function can return a Promise.
* It's also possible to return an enum. Let's say that a phone number can have multiple
* states: 'invalid-country-code' | 'too-long' | 'too-short'
* Those states can be retrieved in the getMessage
* @param {any} modelValue
* @param {ValidatorParam} [param]
* @param {ValidatorConfig} [config]
* @returns {ValidatorOutcome|Promise<ValidatorOutcome>}
*/
execute(modelValue?: unknown, param?: unknown, config?: {} | undefined): boolean | Promise<boolean>;
execute(modelValue: any, param?: ValidatorParam, config?: import("../../types/validate/validate").ValidatorConfig | undefined): ValidatorOutcome | Promise<ValidatorOutcome>;
/**
* The first argument of the constructor, for instance 3 in `new MinLength(3)`. Will
* be stored on Validator instance and passed to `execute` function
* @example
* ```js
* // Store reference to Validator instance
* const myValidatorInstance = new MyValidator(1);
* // Use this instance initially on a FormControl (that uses ValidateMixin)
* render(html`<validatable-element .validators="${[myValidatorInstance]}"></validatable-element>`, document.body);
* // Based on some event, we need to change the param
* myValidatorInstance.param = 2;
* ```
* @property {ValidatorParam}
*/
set param(arg: any);
get param(): any;
set config(arg: {
[x: string]: any;
});
get config(): {
[x: string]: any;
};
/**
* @overridable
* @param {MessageData} [data]
* @returns {Promise<string|Node>}
* The second argument of the constructor, for instance
* `new MinLength(3, {getFeedMessage: async () => 'too long'})`.
* Will be stored on Validator instance and passed to `execute` function.
* @example
* ```js
* // Store reference to Validator instance
* const myValidatorInstance = new MyValidator(1, {getMessage() => 'x'});
* // Use this instance initially on a FormControl (that uses ValidateMixin)
* render(html`<validatable-element .validators="${[myValidatorInstance]}"></validatable-element>`, document.body);
* // Based on some event, we need to change the param
* myValidatorInstance.config = {getMessage() => 'y'};
* ```
* @property {ValidatorConfig}
*/
set config(arg: import("../../types/validate/validate").ValidatorConfig);
get config(): import("../../types/validate/validate").ValidatorConfig;
/**
* This is a protected method that usually should not be overridden. It is called by ValidateMixin
* and it gathers data to be passed to getMessage functions found:
* - `this.config.getMessage`, locally provided by consumers of the Validator (overrides global getMessage)
* - `MyValidator.getMessage`, globally provided by creators or consumers of the Validator
*
* Confusion can arise because of similarities with former mentioned methods. In that regard, a
* better name for this function would have been _pepareDataAndCallHighestPrioGetMessage.
* @example
* ```js
* class MyValidator extends Validator {
* // ...
* // 1. globally defined
* static async getMessage() {
* return 'lowest prio, defined globally by Validator author'
* }
* }
* // 2. globally overridden
* MyValidator.getMessage = async() => 'overrides already configured message';
* // 3. locally overridden
* new MyValidator(myParam, { getMessage: async() => 'locally defined, always wins' });
* ```
* @param {Partial<FeedbackMessageData>} [data]
* @returns {Promise<string|Element>}
* @protected
*/
protected _getMessage(data?: MessageData | undefined): Promise<string | Node>;
protected _getMessage(data?: Partial<import("../../types/validate/validate").FeedbackMessageData> | undefined): Promise<string | Element>;
/**
* @param {HTMLElement} formControl
* Validators are allowed to have knowledge about FormControls.
* In some cases (in case of the Required Validator) we wanted to enhance accessibility by
* adding [aria-required]. Also, it would be possible to write an advanced MinLength
* Validator that adds a .preprocessor that restricts from typing too many characters
* (like the native [minlength] validator).
* Will be called when Validator is added to FormControl.validators.
* @example
* ```js
* onFormControlConnect(formControl) {
* if(formControl.inputNode) {
* inputNode.setAttribute('aria-required', 'true');
* }
* }
*
* ```
* @configurable
* @param {FormControlHost} formControl
*/
onFormControlConnect(formControl: HTMLElement): void;
onFormControlConnect(formControl: FormControlHost): void;
/**
* @param {HTMLElement} formControl
* Also see `onFormControlConnect`.
* Will be called when Validator is removed from FormControl.validators.
* @example
* ```js
* onFormControlDisconnect(formControl) {
* if(formControl.inputNode) {
* inputNode.removeAttribute('aria-required');
* }
* }
* @configurable
* @param {FormControlHost} formControl
*/
onFormControlDisconnect(formControl: HTMLElement): void;
onFormControlDisconnect(formControl: FormControlHost): void;
/**

@@ -75,19 +172,9 @@ * @desc Used on async Validators, makes it able to do perf optimizations when there are

abortExecution(): void;
/**
* @private
*/
private __fakeExtendsEventTarget;
addEventListener: ((type: string, listener: EventListener, opts?: Object | undefined) => void) | undefined;
removeEventListener: ((type: string, listener: EventListener, opts?: Object | undefined) => void) | undefined;
dispatchEvent: ((event: Event | CustomEvent) => boolean) | undefined;
}
export type MessageData = {
modelValue?: any;
fieldName?: string | undefined;
formControl?: HTMLElement | undefined;
type?: string | undefined;
config?: {
[x: string]: any;
} | undefined;
name?: string | undefined;
};
export type FeedbackMessageData = import('../../types/validate/validate').FeedbackMessageData;
export type ValidatorParam = import('../../types/validate/validate').ValidatorParam;
export type ValidatorConfig = import('../../types/validate/validate').ValidatorConfig;
export type ValidatorOutcome = import('../../types/validate/validate').ValidatorOutcome;
export type ValidatorName = import('../../types/validate/validate').ValidatorName;
export type ValidationType = any;
export type FormControlHost = import('../FormControlMixin').FormControlHost;
/**
* @typedef {object} MessageData
* @property {*} [MessageData.modelValue]
* @property {string} [MessageData.fieldName]
* @property {HTMLElement} [MessageData.formControl]
* @property {string} [MessageData.type]
* @property {Object.<string,?>} [MessageData.config]
* @property {string} [MessageData.name]
* @typedef {import('../../types/validate/validate').FeedbackMessageData} FeedbackMessageData
* @typedef {import('../../types/validate/validate').ValidatorParam} ValidatorParam
* @typedef {import('../../types/validate/validate').ValidatorConfig} ValidatorConfig
* @typedef {import('../../types/validate/validate').ValidatorOutcome} ValidatorOutcome
* @typedef {import('../../types/validate/validate').ValidatorName} ValidatorName
* @typedef {import('../../types/validate/validate').ValidationType} ValidationType
* @typedef {import('../FormControlMixin').FormControlHost} FormControlHost
*/
export class Validator {
// TODO: support attribute validators like <my-el my-validator=${dynamicParam}></my-el> =>
// register in a ValidateService that is read by Validator and adds these attrs in properties
// object.
// They would become like configurable
// [global attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes)
// for FormControls.
export class Validator extends EventTarget {
/**
*
* @param {?} [param]
* @param {Object.<string,?>} [config]
* @param {ValidatorParam} [param]
* @param {ValidatorConfig} [config]
*/
constructor(param, config) {
this.__fakeExtendsEventTarget();
super();
/** @type {?} */
/** @type {ValidatorParam} */
this.__param = param;
/** @type {Object.<string,?>} */
/** @type {ValidatorConfig} */
this.__config = config || {};
this.type = (config && config.type) || 'error'; // Default type supported by ValidateMixin
/** @type {ValidationType} */
this.type = config?.type || 'error'; // Default type supported by ValidateMixin
}
static get validatorName() {
return '';
}
/**
* The name under which validation results get registered. For convience and predictability, this
* should always be the same as the constructor name (since it will be obfuscated in js builds,
* we need to provide it separately).
* @type {ValidatorName}
*/
static validatorName = '';
static get async() {
return false;
}
/**
* Whether the validator is asynchronous or not. When true., this means execute function returns
* a Promise. This can be handy for:
* - server side calls
* - validations that are dependent on lazy loaded resources (they can be async until the dependency
* is loaded)
* @type {boolean}
*/
static async = false;
/**
* @desc The function that returns a Boolean
* @param {?} [modelValue]
* @param {?} [param]
* @param {{}} [config]
* @returns {Boolean|Promise<Boolean>}
* The function that returns a validity outcome. When we need to show feedback,
* it should return true, otherwise false. So when an error\info|warning|success message
* needs to be shown, return true. For async Validators, the function can return a Promise.
* It's also possible to return an enum. Let's say that a phone number can have multiple
* states: 'invalid-country-code' | 'too-long' | 'too-short'
* Those states can be retrieved in the getMessage
* @param {any} modelValue
* @param {ValidatorParam} [param]
* @param {ValidatorConfig} [config]
* @returns {ValidatorOutcome|Promise<ValidatorOutcome>}
*/

@@ -54,7 +75,23 @@ // eslint-disable-next-line no-unused-vars, class-methods-use-this

/**
* The first argument of the constructor, for instance 3 in `new MinLength(3)`. Will
* be stored on Validator instance and passed to `execute` function
* @example
* ```js
* // Store reference to Validator instance
* const myValidatorInstance = new MyValidator(1);
* // Use this instance initially on a FormControl (that uses ValidateMixin)
* render(html`<validatable-element .validators="${[myValidatorInstance]}"></validatable-element>`, document.body);
* // Based on some event, we need to change the param
* myValidatorInstance.param = 2;
* ```
* @property {ValidatorParam}
*/
set param(p) {
this.__param = p;
if (this.dispatchEvent) {
this.dispatchEvent(new Event('param-changed'));
}
/**
* This event is listened for by ValidateMixin. Whenever the validation parameter has
* changed, the FormControl will revalidate itself
*/
this.dispatchEvent(new Event('param-changed'));
}

@@ -66,7 +103,24 @@

/**
* The second argument of the constructor, for instance
* `new MinLength(3, {getFeedMessage: async () => 'too long'})`.
* Will be stored on Validator instance and passed to `execute` function.
* @example
* ```js
* // Store reference to Validator instance
* const myValidatorInstance = new MyValidator(1, {getMessage() => 'x'});
* // Use this instance initially on a FormControl (that uses ValidateMixin)
* render(html`<validatable-element .validators="${[myValidatorInstance]}"></validatable-element>`, document.body);
* // Based on some event, we need to change the param
* myValidatorInstance.config = {getMessage() => 'y'};
* ```
* @property {ValidatorConfig}
*/
set config(c) {
this.__config = c;
if (this.dispatchEvent) {
this.dispatchEvent(new Event('config-changed'));
}
/**
* This event is listened for by ValidateMixin. Whenever the validation config has
* changed, the FormControl will revalidate itself
*/
this.dispatchEvent(new Event('config-changed'));
}

@@ -79,5 +133,25 @@

/**
* @overridable
* @param {MessageData} [data]
* @returns {Promise<string|Node>}
* This is a protected method that usually should not be overridden. It is called by ValidateMixin
* and it gathers data to be passed to getMessage functions found:
* - `this.config.getMessage`, locally provided by consumers of the Validator (overrides global getMessage)
* - `MyValidator.getMessage`, globally provided by creators or consumers of the Validator
*
* Confusion can arise because of similarities with former mentioned methods. In that regard, a
* better name for this function would have been _pepareDataAndCallHighestPrioGetMessage.
* @example
* ```js
* class MyValidator extends Validator {
* // ...
* // 1. globally defined
* static async getMessage() {
* return 'lowest prio, defined globally by Validator author'
* }
* }
* // 2. globally overridden
* MyValidator.getMessage = async() => 'overrides already configured message';
* // 3. locally overridden
* new MyValidator(myParam, { getMessage: async() => 'locally defined, always wins' });
* ```
* @param {Partial<FeedbackMessageData>} [data]
* @returns {Promise<string|Element>}
* @protected

@@ -107,5 +181,16 @@ */

/**
* Called inside Validator.prototype._getMessage (see explanation).
* @example
* ```js
* class MyValidator extends Validator {
* static async getMessage() {
* return 'lowest prio, defined globally by Validator author'
* }
* }
* // globally overridden
* MyValidator.getMessage = async() => 'overrides already configured message';
* ```
* @overridable
* @param {MessageData} [data]
* @returns {Promise<string|Node>}
* @param {Partial<FeedbackMessageData>} [data]
* @returns {Promise<string|Element>}
*/

@@ -118,3 +203,19 @@ // eslint-disable-next-line no-unused-vars

/**
* @param {HTMLElement} formControl
* Validators are allowed to have knowledge about FormControls.
* In some cases (in case of the Required Validator) we wanted to enhance accessibility by
* adding [aria-required]. Also, it would be possible to write an advanced MinLength
* Validator that adds a .preprocessor that restricts from typing too many characters
* (like the native [minlength] validator).
* Will be called when Validator is added to FormControl.validators.
* @example
* ```js
* onFormControlConnect(formControl) {
* if(formControl.inputNode) {
* inputNode.setAttribute('aria-required', 'true');
* }
* }
*
* ```
* @configurable
* @param {FormControlHost} formControl
*/

@@ -124,3 +225,13 @@ onFormControlConnect(formControl) {} // eslint-disable-line

/**
* @param {HTMLElement} formControl
* Also see `onFormControlConnect`.
* Will be called when Validator is removed from FormControl.validators.
* @example
* ```js
* onFormControlDisconnect(formControl) {
* if(formControl.inputNode) {
* inputNode.removeAttribute('aria-required');
* }
* }
* @configurable
* @param {FormControlHost} formControl
*/

@@ -139,37 +250,2 @@ onFormControlDisconnect(formControl) {} // eslint-disable-line

abortExecution() {} // eslint-disable-line
/**
* @private
*/
__fakeExtendsEventTarget() {
const delegate = document.createDocumentFragment();
/**
*
* @param {string} type
* @param {EventListener} listener
* @param {Object} [opts]
*/
const delegatedAddEventListener = (type, listener, opts) =>
delegate.addEventListener(type, listener, opts);
/**
* @param {string} type
* @param {EventListener} listener
* @param {Object} [opts]
*/
const delegatedRemoveEventListener = (type, listener, opts) =>
delegate.removeEventListener(type, listener, opts);
/**
* @param {Event|CustomEvent} event
*/
const delegatedDispatchEvent = event => delegate.dispatchEvent(event);
this.addEventListener = delegatedAddEventListener;
this.removeEventListener = delegatedRemoveEventListener;
this.dispatchEvent = delegatedDispatchEvent;
}
}

@@ -176,0 +252,0 @@

export class IsDate extends Validator {
static get validatorName(): string;
}
export class MinDate extends Validator {
static get validatorName(): string;
}
export class MaxDate extends Validator {
static get validatorName(): string;
}
export class MinMaxDate extends Validator {
static get validatorName(): string;
}
export class IsDateDisabled extends Validator {
static get validatorName(): string;
}
import { Validator } from "../Validator.js";
export class IsNumber extends Validator {
static get validatorName(): string;
}
export class MinNumber extends Validator {
static get validatorName(): string;
}
export class MaxNumber extends Validator {
static get validatorName(): string;
}
export class MinMaxNumber extends Validator {
static get validatorName(): string;
}
import { Validator } from "../Validator.js";

@@ -5,2 +5,3 @@ /**

export class Required extends Validator {
static get validatorName(): string;
/**

@@ -7,0 +8,0 @@ * In order to prevent accessibility violations, the aria-required attribute will

export class IsString extends Validator {
static get validatorName(): string;
}
export class EqualsLength extends Validator {
static get validatorName(): string;
}
export class MinLength extends Validator {
static get validatorName(): string;
}
export class MaxLength extends Validator {
static get validatorName(): string;
}
export class MinMaxLength extends Validator {
static get validatorName(): string;
}
export class IsEmail extends Validator {
static get validatorName(): string;
}
export class Pattern extends Validator {
static get validatorName(): string;
}
import { Validator } from "../Validator.js";
export class AlwaysInvalid extends Validator {
static get validatorName(): string;
}
export class AlwaysValid extends Validator {
static get validatorName(): string;
}
export class AsyncAlwaysValid extends AlwaysValid {
static get async(): boolean;
}
export class AsyncAlwaysInvalid extends AlwaysValid {
static get async(): boolean;
}
import { Validator } from "../src/validate/Validator.js";
export * from "./ExampleValidators.js";
export * from "./getFormControlMembers.js";
export * from "./mimicUserInput.js";
export * from './ExampleValidators.js';
export * from './getFormControlMembers.js';
export * from './mimicUserInput.js';

@@ -83,2 +83,39 @@ import { LitElement } from '@lion/core';

/**
* N.B. For platform controls, the same would be achieved with <input aria-label="My label">
* However, since FormControl is usually not the activeElement (_inputNode is), this
* will not have the desired effect on for instance lion-input
*/
it('supports "label-sr-only" to make label visually hidden, but accessible for screen reader users', async () => {
const el = /** @type {FormControlMixinClass} */ (
await fixture(html`
<${tag} label-sr-only>
<label slot="label">Email <span>address</span></label>
${inputSlot}
</${tag}>`)
);
const expectedValues = {
position: 'absolute',
top: '0px',
width: '1px',
height: '1px',
overflow: 'hidden',
clipPath: 'inset(100%)',
clip: 'rect(1px, 1px, 1px, 1px)',
whiteSpace: 'nowrap',
borderWidth: '0px',
margin: '0px',
padding: '0px',
};
const labelStyle = window.getComputedStyle(
// @ts-ignore
el.shadowRoot?.querySelector('.form-field__label'),
);
Object.entries(expectedValues).forEach(([key, val]) => {
expect(labelStyle[key]).to.equal(val);
});
});
it('can have a help-text', async () => {

@@ -85,0 +122,0 @@ const elAttr = /** @type {FormControlMixinClass} */ (

@@ -51,3 +51,3 @@ import { LitElement } from '@lion/core';

expect(() => {
new MyValidator().execute();
new MyValidator().execute(undefined);
}).to.throw(

@@ -65,2 +65,3 @@ 'A validator needs to have a name! Please set it via "static get validatorName() { return \'IsCat\'; }"',

// @ts-ignore needed for test
const vali = new MyValidator({}, { getMessage: 'This is the custom error message' });

@@ -81,4 +82,4 @@ const { getMessage } = getProtectedMembers(vali);

it('receives a config object (optionally) as a second argument on instantiation', async () => {
const vali = new Validator('myParam', { my: 'config' });
expect(vali.config).to.eql({ my: 'config' });
const vali = new Validator('myParam', { fieldName: 'X' });
expect(vali.config).to.eql({ fieldName: 'X' });
});

@@ -93,3 +94,3 @@

}
const vali = new MyValidator('myParam', { my: 'config', getMessage: configSpy });
const vali = new MyValidator('myParam', { fieldName: 'X', getMessage: configSpy });
const { getMessage } = getProtectedMembers(vali);

@@ -102,3 +103,3 @@ getMessage();

params: 'myParam',
config: { my: 'config', getMessage: configSpy },
config: { fieldName: 'X', getMessage: configSpy },
});

@@ -122,3 +123,3 @@ });

}
const vali = new MyValidator('myParam', { my: 'config' });
const vali = new MyValidator('myParam', { fieldName: 'X' });
const { getMessage } = getProtectedMembers(vali);

@@ -131,3 +132,3 @@ getMessage();

params: 'myParam',
config: { my: 'config' },
config: { fieldName: 'X' },
});

@@ -147,3 +148,3 @@ });

it('fires "config-changed" event on config change', async () => {
const vali = new Validator('foo', { foo: 'bar' });
const vali = new Validator('foo', { fieldName: 'X' });
const cb = sinon.spy(() => {});

@@ -153,3 +154,3 @@ if (vali.addEventListener) {

}
vali.config = { bar: 'foo' };
vali.config = { fieldName: 'Y' };
expect(cb.callCount).to.equal(1);

@@ -156,0 +157,0 @@ });

@@ -7,2 +7,3 @@ import { Constructor } from '@open-wc/dedupe-mixin';

export type FormatOptions = { mode: 'pasted' | 'auto' } & object;
export declare class FormatHost {

@@ -16,3 +17,3 @@ /**

*/
parser(v: string, opts: FormatNumberOptions): unknown;
parser(v: string, opts: FormatOptions): unknown;

@@ -28,3 +29,3 @@ /**

*/
formatter(v: unknown, opts?: FormatNumberOptions): string;
formatter(v: unknown, opts?: FormatOptions): string;

@@ -50,4 +51,9 @@ /**

/**
* Preprocesses the viewValue before it's parsed to a modelValue. Can be used to filter
* invalid input amongst others.
* Preprocessors could be considered 'live formatters'. Their result is shown to the user
* on keyup instead of after blurring the field. The biggest difference between preprocessors
* and formatters is their moment of execution: preprocessors are run before modelValue is
* computed (and work based on view value), whereas formatters are run after the parser (and
* are based on modelValue)
* Automatically formats code while typing. It depends on a preprocessro that smartly
* updates the viewValue and caret position for best UX.
* @example

@@ -59,7 +65,10 @@ * ```js

* }
* ```
* @param {string} v - the raw value from the <input> after keyUp/Down event
* @returns {string} preprocessedValue: the result of preprocessing for invalid input
* @param {FormatOptions & { prevViewValue: string; currentCaretIndex: number }} opts - the raw value from the <input> after keyUp/Down event
* @returns {{ viewValue:string; caretIndex:number; }|string|undefined} preprocessedValue: the result of preprocessing for invalid input
*/
preprocessor(v: string): string;
preprocessor(
v: string,
options: FormatOptions & { prevViewValue: string; currentCaretIndex: number },
): { viewValue: string; caretIndex: number } | string | undefined;

@@ -107,3 +116,3 @@ /**

*/
formatOptions: FormatNumberOptions;
formatOptions: FormatOptions;

@@ -110,0 +119,0 @@ /**

@@ -39,2 +39,4 @@ import { LitElement, nothing, TemplateResult, CSSResultArray } from '@lion/core';

value: string;
selectionStart?: number;
selectionEnd?: number;
}

@@ -41,0 +43,0 @@

@@ -107,3 +107,3 @@ import { LitElement } from '@lion/core';

* - change in the 'validators' array
* - change in the config of an individual Validator
* - change in the config of an individual Validator
*

@@ -110,0 +110,0 @@ * Three situations are handled:

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc