feat(validate): use static validatorName instead of instance name (#600)

This commit is contained in:
George Serbanescu 2020-02-26 16:24:01 +02:00 committed by Joren Broekema
parent 62978a559a
commit 7c45dd6839
28 changed files with 233 additions and 248 deletions

View file

@ -237,13 +237,12 @@ Below is a more advanced validator on the group that evaluates the children chec
{() => { {() => {
loadDefaultFeedbackMessages(); loadDefaultFeedbackMessages();
class HasMinTwoChecked extends Validator { class HasMinTwoChecked extends Validator {
constructor(...args) {
super(...args);
this.name = 'HasMinTwoChecked';
}
execute(value) { execute(value) {
return value.length < 2; return value.length < 2;
} }
static get validatorName() {
return 'HasMinTwoChecked';
}
static async getMessage() { static async getMessage() {
return 'You need to select at least 2 values.'; return 'You need to select at least 2 values.';
} }
@ -284,15 +283,15 @@ import { Required, Validator, loadDefaultFeedbackMessages } from '@lion/validate
loadDefaultFeedbackMessages(); loadDefaultFeedbackMessages();
class HasMinTwoChecked extends Validator { class HasMinTwoChecked extends Validator {
constructor(...args) {
super(...args);
this.name = 'HasMinTwoChecked';
}
execute(value) { execute(value) {
return value.length < 2; return value.length < 2;
} }
static get validatorName() {
return 'HasMinTwoChecked';
}
static async getMessage() { static async getMessage() {
return 'You need to select at least 2 values.'; return 'You need to select at least 2 values.';
} }

View file

@ -338,9 +338,8 @@ export function runFormatMixinSuite(customConfig) {
// that set hasError back to false when the user input is mimicked. // that set hasError back to false when the user input is mimicked.
const AlwaysInvalid = class extends Validator { const AlwaysInvalid = class extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'AlwaysInvalid';
this.name = 'AlwaysInvalid';
} }
execute() { execute() {

View file

@ -298,9 +298,8 @@ describe('<lion-field>', () => {
it('should conditionally show error', async () => { it('should conditionally show error', async () => {
const HasX = class extends Validator { const HasX = class extends Validator {
constructor() { static get validatorName() {
super(); return 'HasX';
this.name = 'HasX';
} }
execute(value) { execute(value) {
@ -412,9 +411,8 @@ describe('<lion-field>', () => {
it('will only update formattedValue when valid on `user-input-changed`', async () => { it('will only update formattedValue when valid on `user-input-changed`', async () => {
const formatterSpy = sinon.spy(value => `foo: ${value}`); const formatterSpy = sinon.spy(value => `foo: ${value}`);
const Bar = class extends Validator { const Bar = class extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'Bar';
this.name = 'Bar';
} }
execute(value) { execute(value) {

View file

@ -1,9 +1,8 @@
import { Validator } from '@lion/validate'; import { Validator } from '@lion/validate';
export class FormElementsHaveNoError extends Validator { export class FormElementsHaveNoError extends Validator {
constructor() { static get validatorName() {
super(); return 'FormElementsHaveNoError';
this.name = 'FormElementsHaveNoError';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this

View file

@ -343,9 +343,8 @@ describe('<lion-fieldset>', () => {
describe('Validation', () => { describe('Validation', () => {
it('validates on init', async () => { it('validates on init', async () => {
class IsCat extends Validator { class IsCat extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCat';
this.name = 'IsCat';
} }
execute(value) { execute(value) {
@ -376,9 +375,8 @@ describe('<lion-fieldset>', () => {
it('has a special validator for all children - can be checked via this.error.FormElementsHaveNoError', async () => { it('has a special validator for all children - can be checked via this.error.FormElementsHaveNoError', async () => {
class IsCat extends Validator { class IsCat extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCat';
this.name = 'IsCat';
} }
execute(value) { execute(value) {
@ -405,9 +403,8 @@ describe('<lion-fieldset>', () => {
it('validates on children (de)registration', async () => { it('validates on children (de)registration', async () => {
class HasEvenNumberOfChildren extends Validator { class HasEvenNumberOfChildren extends Validator {
constructor() { static get validatorName() {
super(); return 'HasEvenNumberOfChildren';
this.name = 'HasEvenNumberOfChildren';
} }
execute(value) { execute(value) {
@ -561,9 +558,8 @@ describe('<lion-fieldset>', () => {
it('potentially shows fieldset error message on interaction change', async () => { it('potentially shows fieldset error message on interaction change', async () => {
class Input1IsTen extends Validator { class Input1IsTen extends Validator {
constructor() { static get validatorName() {
super(); return 'Input1IsTen';
this.name = 'Input1IsTen';
} }
execute(value) { execute(value) {
@ -593,9 +589,8 @@ describe('<lion-fieldset>', () => {
it('show error if tabbing "out" of last ', async () => { it('show error if tabbing "out" of last ', async () => {
class Input1IsTen extends Validator { class Input1IsTen extends Validator {
constructor() { static get validatorName() {
super(); return 'Input1IsTen';
this.name = 'Input1IsTen';
} }
execute(value) { execute(value) {
@ -916,9 +911,8 @@ describe('<lion-fieldset>', () => {
it('has correct validation afterwards', async () => { it('has correct validation afterwards', async () => {
class IsCat extends Validator { class IsCat extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCat';
this.name = 'IsCat';
} }
execute(value) { execute(value) {
@ -927,9 +921,8 @@ describe('<lion-fieldset>', () => {
} }
} }
class ColorContainsA extends Validator { class ColorContainsA extends Validator {
constructor() { static get validatorName() {
super(); return 'ColorContainsA';
this.name = 'ColorContainsA';
} }
execute(value) { execute(value) {

View file

@ -363,9 +363,8 @@ You can even hard code localization in there if needed or you can use a localiza
<Story name="Custom Validators"> <Story name="Custom Validators">
{() => { {() => {
class MyValidator extends Validator { class MyValidator extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'myValidator';
this.name = 'myValidator';
} }
execute(modelValue, param) { execute(modelValue, param) {
return modelValue !== param; return modelValue !== param;
@ -394,7 +393,10 @@ You can even hard code localization in there if needed or you can use a localiza
class MyValidator extends Validator { class MyValidator extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'myValidator'; }
static get validatorName() {
return 'myValidator';
} }
execute(modelValue, param) { execute(modelValue, param) {
@ -511,8 +513,12 @@ Oftern
class AsyncValidator extends Validator { class AsyncValidator extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'asyncValidator'; }
this.async = true; static get validatorName() {
return 'asyncValidator';
}
static get async() {
return true;
} }
async execute() { async execute() {
console.log('async pending...'); console.log('async pending...');
@ -549,8 +555,12 @@ function pause(ms = 0) {
class AsyncValidator extends Validator { class AsyncValidator extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'asyncValidator'; }
this.async = true; static get validatorName() {
return 'asyncValidator';
}
static get async() {
return true;
} }
async execute() { async execute() {
console.log('async pending...'); console.log('async pending...');

View file

@ -105,9 +105,8 @@ In the following example we will demonstrate this with interaction states, the m
// Define a demo validator that should only be visible on an odd amount of characters // Define a demo validator that should only be visible on an odd amount of characters
// const OddValidator = [modelValue => ({ odd: modelValue.length % 2 !== 0 })]; // const OddValidator = [modelValue => ({ odd: modelValue.length % 2 !== 0 })];
class OddValidator extends Validator { class OddValidator extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'OddValidator';
this.name = 'OddValidator';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
execute(value) { execute(value) {

View file

@ -175,9 +175,8 @@ Try it by typing something in the input, then removing it.
<Story name="Validation"> <Story name="Validation">
{() => { {() => {
const DemoValidator = class extends Validator { const DemoValidator = class extends Validator {
constructor() { static get validatorName() {
super(); return 'DemoValidator';
this.name = 'DemoValidator';
} }
execute(value) { execute(value) {
if (value && value.input1) { if (value && value.input1) {
@ -199,9 +198,8 @@ Try it by typing something in the input, then removing it.
```js ```js
const DemoValidator = class extends Validator { const DemoValidator = class extends Validator {
constructor() { static get validatorName() {
super(); return 'DemoValidator';
this.name = 'DemoValidator';
} }
execute(value) { execute(value) {
@ -230,9 +228,8 @@ You can have your fieldset validator take into consideration multiple fields.
<Story name="Validating 2 fields"> <Story name="Validating 2 fields">
{() => { {() => {
const IsCatsAndDogs = class extends Validator { const IsCatsAndDogs = class extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCatsAndDogs';
this.name = 'IsCatsAndDogs';
} }
execute(value) { execute(value) {
return !(value.input1 === 'cats' && value.input2 === 'dogs'); return !(value.input1 === 'cats' && value.input2 === 'dogs');
@ -262,9 +259,8 @@ You can have your fieldset validator take into consideration multiple fields.
```js ```js
const IsCatsAndDogs = class extends Validator { const IsCatsAndDogs = class extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCatsAndDogs';
this.name = 'IsCatsAndDogs';
} }
execute(value) { execute(value) {
@ -308,9 +304,8 @@ You can have your fieldset validator take into accounts multiple nested fieldset
<Story name="Validating 2 fieldsets"> <Story name="Validating 2 fieldsets">
{() => { {() => {
const IsCatsDogs = class extends Validator { const IsCatsDogs = class extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCatsAndDogs';
this.name = 'IsCatsDogs';
} }
execute(value) { execute(value) {
if ((value.inner1 && value.inner1.input1 === 'cats') && if ((value.inner1 && value.inner1.input1 === 'cats') &&
@ -350,9 +345,8 @@ You can have your fieldset validator take into accounts multiple nested fieldset
```js ```js
const IsCatsDogs = class extends Validator { const IsCatsDogs = class extends Validator {
constructor() { static get validatorName() {
super(); return 'IsCatsDogs';
this.name = 'IsCatsDogs';
} }
execute(value) { execute(value) {

View file

@ -322,14 +322,14 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) {
// On every validator change, synchronize disabled dates: this means // On every validator change, synchronize disabled dates: this means
// we need to extract minDate, maxDate, minMaxDate and disabledDates validators // we need to extract minDate, maxDate, minMaxDate and disabledDates validators
validators.forEach(v => { validators.forEach(v => {
if (v.name === 'MinDate') { if (v.constructor.name === 'MinDate') {
this.__calendarMinDate = v.param; this.__calendarMinDate = v.param;
} else if (v.name === 'MaxDate') { } else if (v.constructor.name === 'MaxDate') {
this.__calendarMaxDate = v.param; this.__calendarMaxDate = v.param;
} else if (v.name === 'MinMaxDate') { } else if (v.constructor.name === 'MinMaxDate') {
this.__calendarMinDate = v.param.min; this.__calendarMinDate = v.param.min;
this.__calendarMaxDate = v.param.max; this.__calendarMaxDate = v.param.max;
} else if (v.name === 'IsDateDisabled') { } else if (v.constructor.name === 'IsDateDisabled') {
this.__calendarDisableDates = v.param; this.__calendarDisableDates = v.param;
} }
}); });

View file

@ -69,9 +69,8 @@ loadDefaultFeedbackMessages();
<Story name="Custom Validator"> <Story name="Custom Validator">
{() => { {() => {
class GmailOnly extends Validator { class GmailOnly extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'GmailOnly';
this.name = 'GmailOnly';
} }
execute(value) { execute(value) {
let hasError = false; let hasError = false;
@ -98,9 +97,8 @@ loadDefaultFeedbackMessages();
import { Validator } from '@lion/validate'; import { Validator } from '@lion/validate';
class GmailOnly extends Validator { class GmailOnly extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'GmailOnly';
this.name = 'GmailOnly';
} }
execute(value) { execute(value) {

View file

@ -1,8 +1,8 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { isValidIBAN } from 'ibantools';
import { Validator } from '@lion/validate';
import { localize } from '@lion/localize'; import { localize } from '@lion/localize';
import { Validator } from '@lion/validate';
import { isValidIBAN } from 'ibantools';
let loaded = false; let loaded = false;
const loadTranslations = async () => { const loadTranslations = async () => {
@ -19,9 +19,8 @@ const loadTranslations = async () => {
}; };
export class IsIBAN extends Validator { export class IsIBAN extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsIBAN';
this.name = 'IsIBAN';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -36,9 +35,8 @@ export class IsIBAN extends Validator {
} }
export class IsCountryIBAN extends IsIBAN { export class IsCountryIBAN extends IsIBAN {
constructor(...args) { static get validatorName() {
super(...args); return 'IsCountryIBAN';
this.name = 'IsCountryIBAN';
} }
execute(value) { execute(value) {

View file

@ -192,9 +192,8 @@ You can also create a validator that validates whether a certain option is check
<Story name="Validate item"> <Story name="Validate item">
{() => { {() => {
class IsBrontosaurus extends Validator { class IsBrontosaurus extends Validator {
constructor() { static get validatorName() {
super(); return 'IsBrontosaurus';
this.name = 'IsBrontosaurus';
} }
execute(value) { execute(value) {
return value === 'brontosaurus'; return value === 'brontosaurus';
@ -227,9 +226,8 @@ You can also create a validator that validates whether a certain option is check
import { loadDefaultFeedbackMessages, Required, Validator } from 'lion/validate'; import { loadDefaultFeedbackMessages, Required, Validator } from 'lion/validate';
class IsBrontosaurus extends Validator { class IsBrontosaurus extends Validator {
constructor() { static get validatorName() {
super(); return 'IsBrontosaurus';
this.name = 'IsBrontosaurus';
} }
execute(value) { execute(value) {
return value === 'brontosaurus'; return value === 'brontosaurus';

View file

@ -65,9 +65,8 @@ Simple example that illustrates where validation feedback will be displayed.
<Story name="Validation"> <Story name="Validation">
{() => { {() => {
const IsTrue = class extends Validator { const IsTrue = class extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsTrue';
this.name = 'IsTrue';
} }
execute(value) { execute(value) {
return !value.checked; return !value.checked;
@ -98,9 +97,8 @@ import { Validator } from '@lion/validate';
import { LionSwitch } from '@lion/switch/src/LionSwitch'; import { LionSwitch } from '@lion/switch/src/LionSwitch';
const IsTrue = class extends Validator { const IsTrue = class extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsTrue';
this.name = 'IsTrue';
} }
execute(value) { execute(value) {
return !value.checked; return !value.checked;

View file

@ -42,9 +42,8 @@ import { Required, IsString, MaxLength, DefaultSuccess, Validator } from '@lion/
const isInitialsRegex = /^([A-Z]\.)+$/; const isInitialsRegex = /^([A-Z]\.)+$/;
class IsInitialsExample extends Validator { class IsInitialsExample extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsExampleInitials';
this.name = 'IsExampleInitials';
} }
execute(value) { execute(value) {

View file

@ -323,8 +323,12 @@ export const ValidateMixin = dedupeMixin(
const /** @type {Validator[]} */ filteredValidators = this._allValidators.filter( const /** @type {Validator[]} */ filteredValidators = this._allValidators.filter(
v => !(v instanceof ResultValidator) && !(v instanceof Required), v => !(v instanceof ResultValidator) && !(v instanceof Required),
); );
const /** @type {Validator[]} */ syncValidators = filteredValidators.filter(v => !v.async); const /** @type {Validator[]} */ syncValidators = filteredValidators.filter(
const /** @type {Validator[]} */ asyncValidators = filteredValidators.filter(v => v.async); v => !v.constructor.async,
);
const /** @type {Validator[]} */ asyncValidators = filteredValidators.filter(
v => v.constructor.async,
);
/** /**
* 2. Synchronous validators * 2. Synchronous validators
@ -378,7 +382,7 @@ export const ValidateMixin = dedupeMixin(
__executeResultValidators(regularValidationResult) { __executeResultValidators(regularValidationResult) {
/** @type {ResultValidator[]} */ /** @type {ResultValidator[]} */
const resultValidators = this._allValidators.filter( const resultValidators = this._allValidators.filter(
v => !v.async && v instanceof ResultValidator, v => !v.constructor.async && v instanceof ResultValidator,
); );
return resultValidators.filter(v => return resultValidators.filter(v =>
@ -415,7 +419,7 @@ export const ValidateMixin = dedupeMixin(
if (!validationStates[v.type]) { if (!validationStates[v.type]) {
validationStates[v.type] = {}; validationStates[v.type] = {};
} }
validationStates[v.type][v.name] = true; validationStates[v.type][v.constructor.name] = true;
}); });
this.validationStates = validationStates; this.validationStates = validationStates;
this.hasFeedbackFor = [...new Set(this.__validationResult.map(v => v.type))]; this.hasFeedbackFor = [...new Set(this.__validationResult.map(v => v.type))];
@ -457,7 +461,7 @@ export const ValidateMixin = dedupeMixin(
} }
if (this.constructor.validationTypes.indexOf(v.type) === -1) { if (this.constructor.validationTypes.indexOf(v.type) === -1) {
// throws in constructor are not visible to end user so we do both // throws in constructor are not visible to end user so we do both
const errorMessage = `This component does not support the validator type "${v.type}" used in "${v.name}". You may change your validators type or add it to the components "static get validationTypes() {}".`; const errorMessage = `This component does not support the validator type "${v.type}" used in "${v.constructor.validatorName}". You may change your validators type or add it to the components "static get validationTypes() {}".`;
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(errorMessage, this); console.error(errorMessage, this);
throw new Error(errorMessage); throw new Error(errorMessage);

View file

@ -4,13 +4,19 @@ export class Validator {
constructor(param, config) { constructor(param, config) {
fakeExtendsEventTarget(this); fakeExtendsEventTarget(this);
this.name = '';
this.async = false;
this.__param = param; this.__param = param;
this.__config = config || {}; this.__config = config || {};
this.type = (config && config.type) || 'error'; // Default type supported by ValidateMixin this.type = (config && config.type) || 'error'; // Default type supported by ValidateMixin
} }
static get validatorName() {
return '';
}
static get async() {
return false;
}
/** /**
* @desc The function that returns a Boolean * @desc The function that returns a Boolean
* @param {string|Date|Number|object} modelValue * @param {string|Date|Number|object} modelValue
@ -18,8 +24,10 @@ export class Validator {
* @returns {Boolean|Promise<Boolean>} * @returns {Boolean|Promise<Boolean>}
*/ */
execute(/* modelValue, param */) { execute(/* modelValue, param */) {
if (!this.name) { if (!this.validatorName) {
throw new Error('You must provide a name like "this.name = \'IsCat\'" for your Validator'); throw new Error(
'A validator needs to have a name! Please set it via "static get validatorName() { return \'IsCat\'; }"',
);
} }
} }
@ -52,7 +60,7 @@ export class Validator {
*/ */
async _getMessage(data) { async _getMessage(data) {
const composedData = { const composedData = {
name: this.name, name: this.constructor.validatorName,
type: this.type, type: this.type,
params: this.param, params: this.param,
config: this.config, config: this.config,

View file

@ -9,9 +9,8 @@ function isDate(value) {
} }
export class IsDate extends Validator { export class IsDate extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsDate';
this.name = 'IsDate';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -25,9 +24,8 @@ export class IsDate extends Validator {
} }
export class MinDate extends Validator { export class MinDate extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MinDate';
this.name = 'MinDate';
} }
execute(value, min = this.param) { execute(value, min = this.param) {
@ -40,9 +38,8 @@ export class MinDate extends Validator {
} }
export class MaxDate extends Validator { export class MaxDate extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MaxDate';
this.name = 'MaxDate';
} }
execute(value, max = this.param) { execute(value, max = this.param) {
@ -55,9 +52,8 @@ export class MaxDate extends Validator {
} }
export class MinMaxDate extends Validator { export class MinMaxDate extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MinMaxDate';
this.name = 'MinMaxDate';
} }
execute(value, { min = 0, max = 0 } = this.param) { execute(value, { min = 0, max = 0 } = this.param) {
@ -70,9 +66,8 @@ export class MinMaxDate extends Validator {
} }
export class IsDateDisabled extends Validator { export class IsDateDisabled extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsDateDisabled';
this.name = 'IsDateDisabled';
} }
execute(value, isDisabledFn = this.param) { execute(value, isDisabledFn = this.param) {

View file

@ -11,9 +11,8 @@ const isNumber = value =>
value === value && typeof value === 'number'; value === value && typeof value === 'number';
export class IsNumber extends Validator { export class IsNumber extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsNumber';
this.name = 'IsNumber';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -27,9 +26,8 @@ export class IsNumber extends Validator {
} }
export class MinNumber extends Validator { export class MinNumber extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MinNumber';
this.name = 'MinNumber';
} }
execute(value, min = this.param) { execute(value, min = this.param) {
@ -42,9 +40,8 @@ export class MinNumber extends Validator {
} }
export class MaxNumber extends Validator { export class MaxNumber extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MaxNumber';
this.name = 'MaxNumber';
} }
execute(value, max = this.param) { execute(value, max = this.param) {
@ -57,9 +54,8 @@ export class MaxNumber extends Validator {
} }
export class MinMaxNumber extends Validator { export class MinMaxNumber extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MinMaxNumber';
this.name = 'MinMaxNumber';
} }
execute(value, { min = 0, max = 0 } = this.param) { execute(value, { min = 0, max = 0 } = this.param) {

View file

@ -1,9 +1,8 @@
import { Validator } from '../Validator.js'; import { Validator } from '../Validator.js';
export class Required extends Validator { export class Required extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'Required';
this.name = 'Required';
} }
/** /**

View file

@ -4,9 +4,8 @@ import { Validator } from '../Validator.js';
const isString = value => typeof value === 'string'; const isString = value => typeof value === 'string';
export class IsString extends Validator { export class IsString extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsString';
this.name = 'IsString';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -20,9 +19,8 @@ export class IsString extends Validator {
} }
export class EqualsLength extends Validator { export class EqualsLength extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'EqualsLength';
this.name = 'EqualsLength';
} }
execute(value, length = this.param) { execute(value, length = this.param) {
@ -35,9 +33,8 @@ export class EqualsLength extends Validator {
} }
export class MinLength extends Validator { export class MinLength extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MinLength';
this.name = 'MinLength';
} }
execute(value, min = this.param) { execute(value, min = this.param) {
@ -50,9 +47,8 @@ export class MinLength extends Validator {
} }
export class MaxLength extends Validator { export class MaxLength extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MaxLength';
this.name = 'MaxLength';
} }
execute(value, max = this.param) { execute(value, max = this.param) {
@ -65,9 +61,8 @@ export class MaxLength extends Validator {
} }
export class MinMaxLength extends Validator { export class MinMaxLength extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MinMaxLength';
this.name = 'MinMaxLength';
} }
execute(value, { min = 0, max = 0 } = this.param) { execute(value, { min = 0, max = 0 } = this.param) {
@ -81,9 +76,8 @@ export class MinMaxLength extends Validator {
const isEmailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const isEmailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export class IsEmail extends Validator { export class IsEmail extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'IsEmail';
this.name = 'IsEmail';
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this

View file

@ -46,11 +46,6 @@ All validators extend from the default `Validator` class. Below example is an ex
```js ```js
class MyValidator extends Validator { class MyValidator extends Validator {
constructor(...args) {
super(...args);
this.name = 'MyValidator';
}
execute(modelValue, param) { execute(modelValue, param) {
const hasFeedback = false; const hasFeedback = false;
if (modelValue === param) { if (modelValue === param) {
@ -59,6 +54,10 @@ class MyValidator extends Validator {
return hasFeedback; return hasFeedback;
} }
static get validatorName() {
return 'Required';
}
static getMessage({ fieldName }) { static getMessage({ fieldName }) {
return `Please fill in ${fieldName}`; return `Please fill in ${fieldName}`;
} }
@ -87,7 +86,7 @@ You can implement your own custom message for a default validator. Pass a getMes
```js ```js
function getCustomMessage(data) { function getCustomMessage(data) {
return `${data.name} is a required field`; return `${data.validatorName} is a required field`;
} }
``` ```
@ -113,12 +112,6 @@ You can make your async validators as follows:
```js ```js
class AsyncValidator extends Validator { class AsyncValidator extends Validator {
constructor(...args) {
super(...args);
this.name = 'AsyncValidator';
this.async = true;
}
async execute() { async execute() {
console.log('async pending...'); console.log('async pending...');
await pause(2000); await pause(2000);
@ -126,6 +119,12 @@ class AsyncValidator extends Validator {
return true; return true;
} }
static get validatorName() {
return 'AsyncValidator';
}
static get async() { return true; }
static getMessage({ modelValue }) { static getMessage({ modelValue }) {
return `Validated for modelValue: ${modelValue}`; return `Validated for modelValue: ${modelValue}`;
} }

View file

@ -2,9 +2,8 @@
import { Validator } from '../src/Validator.js'; import { Validator } from '../src/Validator.js';
export class AlwaysInvalid extends Validator { export class AlwaysInvalid extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'AlwaysInvalid';
this.name = 'AlwaysInvalid';
} }
execute() { execute() {
@ -14,9 +13,8 @@ export class AlwaysInvalid extends Validator {
} }
export class AlwaysValid extends Validator { export class AlwaysValid extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'AlwaysValid';
this.name = 'AlwaysValid';
} }
execute() { execute() {
@ -26,9 +24,8 @@ export class AlwaysValid extends Validator {
} }
export class AsyncAlwaysValid extends AlwaysValid { export class AsyncAlwaysValid extends AlwaysValid {
constructor(...args) { static get async() {
super(...args); return true;
this.async = true;
} }
execute() { execute() {
@ -37,9 +34,8 @@ export class AsyncAlwaysValid extends AlwaysValid {
} }
export class AsyncAlwaysInvalid extends AlwaysValid { export class AsyncAlwaysInvalid extends AlwaysValid {
constructor(...args) { static get async() {
super(...args); return true;
this.async = true;
} }
async execute() { async execute() {

View file

@ -1,22 +1,21 @@
import { expect, fixture, html, unsafeStatic, defineCE, aTimeout } from '@open-wc/testing';
import sinon from 'sinon';
import { LitElement } from '@lion/core'; import { LitElement } from '@lion/core';
import { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
import sinon from 'sinon';
import { import {
AlwaysValid,
AlwaysInvalid,
AsyncAlwaysValid,
AsyncAlwaysInvalid,
} from '../test-helpers.js';
import {
ValidateMixin,
Unparseable,
Validator,
ResultValidator,
Required,
MinLength,
MaxLength, MaxLength,
MinLength,
Required,
ResultValidator,
Unparseable,
ValidateMixin,
Validator,
} from '../index.js'; } from '../index.js';
import {
AlwaysInvalid,
AlwaysValid,
AsyncAlwaysInvalid,
AsyncAlwaysValid,
} from '../test-helpers.js';
export function runValidateMixinSuite(customConfig) { export function runValidateMixinSuite(customConfig) {
const cfg = { const cfg = {
@ -104,9 +103,12 @@ export function runValidateMixinSuite(customConfig) {
class MajorValidator extends Validator { class MajorValidator extends Validator {
constructor() { constructor() {
super(); super();
this.name = 'MajorValidator';
this.type = 'major error'; this.type = 'major error';
} }
static get validatorName() {
return 'MajorValidator';
}
} }
const el = await fixture(html`<${tag}></${tag}>`); const el = await fixture(html`<${tag}></${tag}>`);
expect(() => { expect(() => {
@ -218,9 +220,8 @@ export function runValidateMixinSuite(customConfig) {
it('finally checks for ResultValidators: creates TotalValidationResult', async () => { it('finally checks for ResultValidators: creates TotalValidationResult', async () => {
class MyResult extends ResultValidator { class MyResult extends ResultValidator {
constructor(...args) { static get validatorName() {
super(...args); return 'ResultValidator';
this.name = 'ResultValidator';
} }
} }
@ -285,21 +286,27 @@ export function runValidateMixinSuite(customConfig) {
class IsCat extends Validator { class IsCat extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'isCat';
this.execute = (modelValue, param) => { this.execute = (modelValue, param) => {
const validateString = param && param.number ? `cat${param.number}` : 'cat'; const validateString = param && param.number ? `cat${param.number}` : 'cat';
const showError = modelValue !== validateString; const showError = modelValue !== validateString;
return showError; return showError;
}; };
} }
static get validatorName() {
return 'isCat';
}
} }
class OtherValidator extends Validator { class OtherValidator extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'otherValidator';
this.execute = () => true; this.execute = () => true;
} }
static get validatorName() {
return 'otherValidator';
}
} }
it('Validators will be called with ".modelValue" as first argument', async () => { it('Validators will be called with ".modelValue" as first argument', async () => {
@ -357,11 +364,11 @@ export function runValidateMixinSuite(customConfig) {
`); `);
el.modelValue = 'cat'; el.modelValue = 'cat';
expect(el.validationStates.error.isCat).to.be.undefined; expect(el.validationStates.error.IsCat).to.be.undefined;
el.modelValue = 'dog'; el.modelValue = 'dog';
expect(el.validationStates.error.isCat).to.be.true; expect(el.validationStates.error.IsCat).to.be.true;
el.modelValue = ''; el.modelValue = '';
expect(el.validationStates.error.isCat).to.be.undefined; expect(el.validationStates.error.IsCat).to.be.undefined;
}); });
it('Validators get retriggered on parameter change', async () => { it('Validators get retriggered on parameter change', async () => {
@ -391,10 +398,12 @@ export function runValidateMixinSuite(customConfig) {
}); });
class IsAsyncCat extends Validator { class IsAsyncCat extends Validator {
constructor(param, config) { static get validatorName() {
super(param, config); return 'delayed-cat';
this.name = 'delayed-cat'; }
this.async = true;
static get async() {
return true;
} }
/** /**
@ -769,20 +778,26 @@ export function runValidateMixinSuite(customConfig) {
class ContainsLowercaseA extends Validator { class ContainsLowercaseA extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'ContainsLowercaseA';
this.execute = modelValue => !modelValue.includes('a'); this.execute = modelValue => !modelValue.includes('a');
} }
static get validatorName() {
return 'ContainsLowercaseA';
}
} }
class ContainsLowercaseB extends Validator { class ContainsLowercaseB extends Validator {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
this.name = 'containsLowercaseB';
this.execute = modelValue => !modelValue.includes('b'); this.execute = modelValue => !modelValue.includes('b');
} }
static get validatorName() {
return 'containsLowercaseB';
}
} }
it('stores validity of individual Validators in ".validationStates.error[validator.name]"', async () => { it('stores validity of individual Validators in ".validationStates.error[validator.validatorName]"', async () => {
const el = await fixture(html` const el = await fixture(html`
<${tag} <${tag}
.modelValue=${'a'} .modelValue=${'a'}

View file

@ -41,9 +41,8 @@ export function runValidateMixinFeedbackPart() {
tag = unsafeStatic(tagString); tag = unsafeStatic(tagString);
ContainsLowercaseA = class extends Validator { ContainsLowercaseA = class extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'ContainsLowercaseA';
this.name = 'ContainsLowercaseA';
} }
execute(modelValue) { execute(modelValue) {
@ -53,9 +52,8 @@ export function runValidateMixinFeedbackPart() {
}; };
class ContainsCat extends Validator { class ContainsCat extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'ContainsCat';
this.name = 'ContainsCat';
} }
execute(modelValue) { execute(modelValue) {
@ -244,7 +242,7 @@ export function runValidateMixinFeedbackPart() {
render() { render() {
return html` return html`
Custom for ${this.feedbackData[0].validator.name} Custom for ${this.feedbackData[0].validator.constructor.name}
`; `;
} }
}, },

View file

@ -13,7 +13,7 @@ describe('Date Validation', () => {
it('provides new isDate() to allow only dates', () => { it('provides new isDate() to allow only dates', () => {
let isEnabled; let isEnabled;
const validator = new IsDate(); const validator = new IsDate();
expect(validator.name).to.equal('IsDate'); expect(validator.constructor.name).to.equal('IsDate');
isEnabled = validator.execute(new Date()); isEnabled = validator.execute(new Date());
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -28,7 +28,7 @@ describe('Date Validation', () => {
it('provides new minDate(x) to allow only dates after min', () => { it('provides new minDate(x) to allow only dates after min', () => {
let isEnabled; let isEnabled;
const validator = new MinDate(new Date('2018/02/02')); const validator = new MinDate(new Date('2018/02/02'));
expect(validator.name).to.equal('MinDate'); expect(validator.constructor.name).to.equal('MinDate');
isEnabled = validator.execute(new Date('2018-02-03')); isEnabled = validator.execute(new Date('2018-02-03'));
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -46,7 +46,7 @@ describe('Date Validation', () => {
it('provides maxDate() to allow only dates before max', () => { it('provides maxDate() to allow only dates before max', () => {
let isEnabled; let isEnabled;
const validator = new MaxDate(new Date('2018/02/02')); const validator = new MaxDate(new Date('2018/02/02'));
expect(validator.name).to.equal('MaxDate'); expect(validator.constructor.name).to.equal('MaxDate');
isEnabled = validator.execute(new Date('2018-02-01')); isEnabled = validator.execute(new Date('2018-02-01'));
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -67,7 +67,7 @@ describe('Date Validation', () => {
min: new Date('2018/02/02'), min: new Date('2018/02/02'),
max: new Date('2018/02/04'), max: new Date('2018/02/04'),
}); });
expect(validator.name).to.equal('MinMaxDate'); expect(validator.constructor.name).to.equal('MinMaxDate');
isEnabled = validator.execute(new Date('2018/02/03')); isEnabled = validator.execute(new Date('2018/02/03'));
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -88,7 +88,7 @@ describe('Date Validation', () => {
it('provides new IsDateDisabled() to disable dates matching specified condition', () => { it('provides new IsDateDisabled() to disable dates matching specified condition', () => {
let isDisabled; let isDisabled;
const validator = new IsDateDisabled(d => d.getDate() === 3); const validator = new IsDateDisabled(d => d.getDate() === 3);
expect(validator.name).to.equal('IsDateDisabled'); expect(validator.constructor.name).to.equal('IsDateDisabled');
isDisabled = validator.execute(new Date('2018/02/04')); isDisabled = validator.execute(new Date('2018/02/04'));
expect(isDisabled).to.be.false; expect(isDisabled).to.be.false;

View file

@ -11,7 +11,7 @@ describe('Number Validation', () => {
it('provides new IsNumber() to allow only numbers', () => { it('provides new IsNumber() to allow only numbers', () => {
let isEnabled; let isEnabled;
const validator = new IsNumber(); const validator = new IsNumber();
expect(validator.name).to.equal('IsNumber'); expect(validator.constructor.name).to.equal('IsNumber');
isEnabled = validator.execute(4); isEnabled = validator.execute(4);
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -26,7 +26,7 @@ describe('Number Validation', () => {
it('provides new MinNumber(x) to allow only numbers longer then min', () => { it('provides new MinNumber(x) to allow only numbers longer then min', () => {
let isEnabled; let isEnabled;
const validator = new MinNumber(3); const validator = new MinNumber(3);
expect(validator.name).to.equal('MinNumber'); expect(validator.constructor.name).to.equal('MinNumber');
isEnabled = validator.execute(3); isEnabled = validator.execute(3);
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -38,7 +38,7 @@ describe('Number Validation', () => {
it('provides new MaxNumber(x) to allow only number shorter then max', () => { it('provides new MaxNumber(x) to allow only number shorter then max', () => {
let isEnabled; let isEnabled;
const validator = new MaxNumber(3); const validator = new MaxNumber(3);
expect(validator.name).to.equal('MaxNumber'); expect(validator.constructor.name).to.equal('MaxNumber');
isEnabled = validator.execute(3); isEnabled = validator.execute(3);
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -50,7 +50,7 @@ describe('Number Validation', () => {
it('provides new MinMaxNumber({ min: x, max: y}) to allow only numbers between min and max', () => { it('provides new MinMaxNumber({ min: x, max: y}) to allow only numbers between min and max', () => {
let isEnabled; let isEnabled;
const validator = new MinMaxNumber({ min: 2, max: 4 }); const validator = new MinMaxNumber({ min: 2, max: 4 });
expect(validator.name).to.equal('MinMaxNumber'); expect(validator.constructor.name).to.equal('MinMaxNumber');
isEnabled = validator.execute(2); isEnabled = validator.execute(2);
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;

View file

@ -13,7 +13,7 @@ describe('String Validation', () => {
it('provides new IsString() to allow only strings', () => { it('provides new IsString() to allow only strings', () => {
let isEnabled; let isEnabled;
const validator = new IsString(); const validator = new IsString();
expect(validator.name).to.equal('IsString'); expect(validator.constructor.name).to.equal('IsString');
isEnabled = validator.execute('foo'); isEnabled = validator.execute('foo');
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -28,7 +28,7 @@ describe('String Validation', () => {
it('provides new EqualsLength(x) to allow only a specific string length', () => { it('provides new EqualsLength(x) to allow only a specific string length', () => {
let isEnabled; let isEnabled;
const validator = new EqualsLength(3); const validator = new EqualsLength(3);
expect(validator.name).to.equal('EqualsLength'); expect(validator.constructor.name).to.equal('EqualsLength');
isEnabled = validator.execute('foo'); isEnabled = validator.execute('foo');
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -43,7 +43,7 @@ describe('String Validation', () => {
it('provides new MinLength(x) to allow only strings longer then min', () => { it('provides new MinLength(x) to allow only strings longer then min', () => {
let isEnabled; let isEnabled;
const validator = new MinLength(3); const validator = new MinLength(3);
expect(validator.name).to.equal('MinLength'); expect(validator.constructor.name).to.equal('MinLength');
isEnabled = validator.execute('foo'); isEnabled = validator.execute('foo');
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -55,7 +55,7 @@ describe('String Validation', () => {
it('provides new MaxLength(x) to allow only strings shorter then max', () => { it('provides new MaxLength(x) to allow only strings shorter then max', () => {
let isEnabled; let isEnabled;
const validator = new MaxLength(3); const validator = new MaxLength(3);
expect(validator.name).to.equal('MaxLength'); expect(validator.constructor.name).to.equal('MaxLength');
isEnabled = validator.execute('foo'); isEnabled = validator.execute('foo');
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -67,7 +67,7 @@ describe('String Validation', () => {
it('provides new MinMaxValidator({ min: x, max: y}) to allow only strings between min and max', () => { it('provides new MinMaxValidator({ min: x, max: y}) to allow only strings between min and max', () => {
let isEnabled; let isEnabled;
const validator = new MinMaxLength({ min: 2, max: 4 }); const validator = new MinMaxLength({ min: 2, max: 4 });
expect(validator.name).to.equal('MinMaxLength'); expect(validator.constructor.name).to.equal('MinMaxLength');
isEnabled = validator.execute('foo'); isEnabled = validator.execute('foo');
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;
@ -82,7 +82,7 @@ describe('String Validation', () => {
it('provides new IsEmail() to allow only valid email formats', () => { it('provides new IsEmail() to allow only valid email formats', () => {
let isEnabled; let isEnabled;
const validator = new IsEmail(); const validator = new IsEmail();
expect(validator.name).to.equal('IsEmail'); expect(validator.constructor.name).to.equal('IsEmail');
isEnabled = validator.execute('foo@bar.com'); isEnabled = validator.execute('foo@bar.com');
expect(isEnabled).to.be.false; expect(isEnabled).to.be.false;

View file

@ -32,14 +32,15 @@ describe('Validator', () => {
class MyValidator extends Validator {} class MyValidator extends Validator {}
expect(() => { expect(() => {
new MyValidator().execute(); new MyValidator().execute();
}).to.throw('You must provide a name like "this.name = \'IsCat\'" for your Validator'); }).to.throw(
'A validator needs to have a name! Please set it via "static get validatorName() { return \'IsCat\'; }"',
);
}); });
it('throws when executing a Validator that has a getMessage config property with a value not of type function', async () => { it('throws when executing a Validator that has a getMessage config property with a value not of type function', async () => {
class MyValidator extends Validator { class MyValidator extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MyValidator';
this.name = 'MyValidator';
} }
} }
@ -62,9 +63,8 @@ describe('Validator', () => {
it('has access to name, type, params, config in getMessage provided by config', () => { it('has access to name, type, params, config in getMessage provided by config', () => {
const configSpy = sinon.spy(); const configSpy = sinon.spy();
class MyValidator extends Validator { class MyValidator extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MyValidator';
this.name = 'MyValidator';
} }
} }
const vali = new MyValidator('myParam', { my: 'config', getMessage: configSpy }); const vali = new MyValidator('myParam', { my: 'config', getMessage: configSpy });
@ -81,9 +81,8 @@ describe('Validator', () => {
it('has access to name, type, params, config in static get getMessage', () => { it('has access to name, type, params, config in static get getMessage', () => {
let staticArgs; let staticArgs;
class MyValidator extends Validator { class MyValidator extends Validator {
constructor(...args) { static get validatorName() {
super(...args); return 'MyValidator';
this.name = 'MyValidator';
} }
static getMessage(...args) { static getMessage(...args) {