From 7c45dd683984e88e3216fba9fcae1b6dc73835b2 Mon Sep 17 00:00:00 2001 From: George Serbanescu Date: Wed, 26 Feb 2020 16:24:01 +0200 Subject: [PATCH] feat(validate): use static validatorName instead of instance name (#600) --- .../checkbox-group/stories/index.stories.mdx | 15 ++-- .../field/test-suites/FormatMixin.suite.js | 5 +- packages/field/test/lion-field.test.js | 10 +-- .../fieldset/src/FormElementsHaveNoError.js | 5 +- packages/fieldset/test/lion-fieldset.test.js | 35 ++++----- .../17-Validation-Examples.stories.mdx | 26 +++++-- .../35-System-InteractionStates.stories.mdx | 5 +- .../stories/fieldset-examples.stories.mdx | 30 +++----- .../src/LionInputDatepicker.js | 8 +- .../input-email/stories/index.stories.mdx | 10 +-- packages/input-iban/src/validators.js | 14 ++-- .../radio-group/stories/index.stories.mdx | 10 +-- packages/switch/stories/index.stories.mdx | 10 +-- packages/validate/README.md | 5 +- packages/validate/src/ValidateMixin.js | 14 ++-- packages/validate/src/Validator.js | 18 +++-- .../validate/src/validators/DateValidators.js | 25 +++--- .../src/validators/NumberValidators.js | 20 ++--- packages/validate/src/validators/Required.js | 5 +- .../src/validators/StringValidators.js | 30 +++----- .../validate/stories/Overview.stories.mdx | 23 +++--- .../test-helpers/ExampleValidators.js | 20 ++--- .../test-suites/ValidateMixin.suite.js | 77 +++++++++++-------- .../ValidateMixinFeedbackPart.suite.js | 12 ++- packages/validate/test/DateValidators.test.js | 10 +-- .../validate/test/NumberValidators.test.js | 8 +- .../validate/test/StringValidators.test.js | 12 +-- packages/validate/test/Validator.test.js | 19 +++-- 28 files changed, 233 insertions(+), 248 deletions(-) diff --git a/packages/checkbox-group/stories/index.stories.mdx b/packages/checkbox-group/stories/index.stories.mdx index f5397e413..2e847fe6a 100644 --- a/packages/checkbox-group/stories/index.stories.mdx +++ b/packages/checkbox-group/stories/index.stories.mdx @@ -237,13 +237,12 @@ Below is a more advanced validator on the group that evaluates the children chec {() => { loadDefaultFeedbackMessages(); class HasMinTwoChecked extends Validator { - constructor(...args) { - super(...args); - this.name = 'HasMinTwoChecked'; - } execute(value) { return value.length < 2; } + static get validatorName() { + return 'HasMinTwoChecked'; + } static async getMessage() { return 'You need to select at least 2 values.'; } @@ -284,15 +283,15 @@ import { Required, Validator, loadDefaultFeedbackMessages } from '@lion/validate loadDefaultFeedbackMessages(); class HasMinTwoChecked extends Validator { - constructor(...args) { - super(...args); - this.name = 'HasMinTwoChecked'; - } execute(value) { return value.length < 2; } + static get validatorName() { + return 'HasMinTwoChecked'; + } + static async getMessage() { return 'You need to select at least 2 values.'; } diff --git a/packages/field/test-suites/FormatMixin.suite.js b/packages/field/test-suites/FormatMixin.suite.js index 6514529d9..1f19b6b7f 100644 --- a/packages/field/test-suites/FormatMixin.suite.js +++ b/packages/field/test-suites/FormatMixin.suite.js @@ -338,9 +338,8 @@ export function runFormatMixinSuite(customConfig) { // that set hasError back to false when the user input is mimicked. const AlwaysInvalid = class extends Validator { - constructor(...args) { - super(...args); - this.name = 'AlwaysInvalid'; + static get validatorName() { + return 'AlwaysInvalid'; } execute() { diff --git a/packages/field/test/lion-field.test.js b/packages/field/test/lion-field.test.js index 99c8c8bc5..b45fe156f 100644 --- a/packages/field/test/lion-field.test.js +++ b/packages/field/test/lion-field.test.js @@ -298,9 +298,8 @@ describe('', () => { it('should conditionally show error', async () => { const HasX = class extends Validator { - constructor() { - super(); - this.name = 'HasX'; + static get validatorName() { + return 'HasX'; } execute(value) { @@ -412,9 +411,8 @@ describe('', () => { it('will only update formattedValue when valid on `user-input-changed`', async () => { const formatterSpy = sinon.spy(value => `foo: ${value}`); const Bar = class extends Validator { - constructor(...args) { - super(...args); - this.name = 'Bar'; + static get validatorName() { + return 'Bar'; } execute(value) { diff --git a/packages/fieldset/src/FormElementsHaveNoError.js b/packages/fieldset/src/FormElementsHaveNoError.js index 70d133de0..d9cc17646 100644 --- a/packages/fieldset/src/FormElementsHaveNoError.js +++ b/packages/fieldset/src/FormElementsHaveNoError.js @@ -1,9 +1,8 @@ import { Validator } from '@lion/validate'; export class FormElementsHaveNoError extends Validator { - constructor() { - super(); - this.name = 'FormElementsHaveNoError'; + static get validatorName() { + return 'FormElementsHaveNoError'; } // eslint-disable-next-line class-methods-use-this diff --git a/packages/fieldset/test/lion-fieldset.test.js b/packages/fieldset/test/lion-fieldset.test.js index 3690bbeb6..13207bf9e 100644 --- a/packages/fieldset/test/lion-fieldset.test.js +++ b/packages/fieldset/test/lion-fieldset.test.js @@ -343,9 +343,8 @@ describe('', () => { describe('Validation', () => { it('validates on init', async () => { class IsCat extends Validator { - constructor() { - super(); - this.name = 'IsCat'; + static get validatorName() { + return 'IsCat'; } execute(value) { @@ -376,9 +375,8 @@ describe('', () => { it('has a special validator for all children - can be checked via this.error.FormElementsHaveNoError', async () => { class IsCat extends Validator { - constructor() { - super(); - this.name = 'IsCat'; + static get validatorName() { + return 'IsCat'; } execute(value) { @@ -405,9 +403,8 @@ describe('', () => { it('validates on children (de)registration', async () => { class HasEvenNumberOfChildren extends Validator { - constructor() { - super(); - this.name = 'HasEvenNumberOfChildren'; + static get validatorName() { + return 'HasEvenNumberOfChildren'; } execute(value) { @@ -561,9 +558,8 @@ describe('', () => { it('potentially shows fieldset error message on interaction change', async () => { class Input1IsTen extends Validator { - constructor() { - super(); - this.name = 'Input1IsTen'; + static get validatorName() { + return 'Input1IsTen'; } execute(value) { @@ -593,9 +589,8 @@ describe('', () => { it('show error if tabbing "out" of last ', async () => { class Input1IsTen extends Validator { - constructor() { - super(); - this.name = 'Input1IsTen'; + static get validatorName() { + return 'Input1IsTen'; } execute(value) { @@ -916,9 +911,8 @@ describe('', () => { it('has correct validation afterwards', async () => { class IsCat extends Validator { - constructor() { - super(); - this.name = 'IsCat'; + static get validatorName() { + return 'IsCat'; } execute(value) { @@ -927,9 +921,8 @@ describe('', () => { } } class ColorContainsA extends Validator { - constructor() { - super(); - this.name = 'ColorContainsA'; + static get validatorName() { + return 'ColorContainsA'; } execute(value) { diff --git a/packages/form-system/stories/17-Validation-Examples.stories.mdx b/packages/form-system/stories/17-Validation-Examples.stories.mdx index 93e24387a..14babb3a3 100644 --- a/packages/form-system/stories/17-Validation-Examples.stories.mdx +++ b/packages/form-system/stories/17-Validation-Examples.stories.mdx @@ -363,9 +363,8 @@ You can even hard code localization in there if needed or you can use a localiza {() => { class MyValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'myValidator'; + static get validatorName() { + return 'myValidator'; } execute(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 { constructor(...args) { super(...args); - this.name = 'myValidator'; + } + + static get validatorName() { + return 'myValidator'; } execute(modelValue, param) { @@ -511,8 +513,12 @@ Oftern class AsyncValidator extends Validator { constructor(...args) { super(...args); - this.name = 'asyncValidator'; - this.async = true; + } + static get validatorName() { + return 'asyncValidator'; + } + static get async() { + return true; } async execute() { console.log('async pending...'); @@ -549,8 +555,12 @@ function pause(ms = 0) { class AsyncValidator extends Validator { constructor(...args) { super(...args); - this.name = 'asyncValidator'; - this.async = true; + } + static get validatorName() { + return 'asyncValidator'; + } + static get async() { + return true; } async execute() { console.log('async pending...'); diff --git a/packages/form-system/stories/35-System-InteractionStates.stories.mdx b/packages/form-system/stories/35-System-InteractionStates.stories.mdx index 54793932e..9ceef3c92 100644 --- a/packages/form-system/stories/35-System-InteractionStates.stories.mdx +++ b/packages/form-system/stories/35-System-InteractionStates.stories.mdx @@ -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 // const OddValidator = [modelValue => ({ odd: modelValue.length % 2 !== 0 })]; class OddValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'OddValidator'; + static get validatorName() { + return 'OddValidator'; } // eslint-disable-next-line class-methods-use-this execute(value) { diff --git a/packages/form-system/stories/fieldset-examples.stories.mdx b/packages/form-system/stories/fieldset-examples.stories.mdx index ca7ed46f9..08a62cd16 100644 --- a/packages/form-system/stories/fieldset-examples.stories.mdx +++ b/packages/form-system/stories/fieldset-examples.stories.mdx @@ -175,9 +175,8 @@ Try it by typing something in the input, then removing it. {() => { const DemoValidator = class extends Validator { - constructor() { - super(); - this.name = 'DemoValidator'; + static get validatorName() { + return 'DemoValidator'; } execute(value) { if (value && value.input1) { @@ -199,9 +198,8 @@ Try it by typing something in the input, then removing it. ```js const DemoValidator = class extends Validator { - constructor() { - super(); - this.name = 'DemoValidator'; + static get validatorName() { + return 'DemoValidator'; } execute(value) { @@ -230,9 +228,8 @@ You can have your fieldset validator take into consideration multiple fields. {() => { const IsCatsAndDogs = class extends Validator { - constructor() { - super(); - this.name = 'IsCatsAndDogs'; + static get validatorName() { + return 'IsCatsAndDogs'; } execute(value) { return !(value.input1 === 'cats' && value.input2 === 'dogs'); @@ -262,9 +259,8 @@ You can have your fieldset validator take into consideration multiple fields. ```js const IsCatsAndDogs = class extends Validator { - constructor() { - super(); - this.name = 'IsCatsAndDogs'; + static get validatorName() { + return 'IsCatsAndDogs'; } execute(value) { @@ -308,9 +304,8 @@ You can have your fieldset validator take into accounts multiple nested fieldset {() => { const IsCatsDogs = class extends Validator { - constructor() { - super(); - this.name = 'IsCatsDogs'; + static get validatorName() { + return 'IsCatsAndDogs'; } execute(value) { if ((value.inner1 && value.inner1.input1 === 'cats') && @@ -350,9 +345,8 @@ You can have your fieldset validator take into accounts multiple nested fieldset ```js const IsCatsDogs = class extends Validator { - constructor() { - super(); - this.name = 'IsCatsDogs'; + static get validatorName() { + return 'IsCatsDogs'; } execute(value) { diff --git a/packages/input-datepicker/src/LionInputDatepicker.js b/packages/input-datepicker/src/LionInputDatepicker.js index c2f2e965d..e5fedca78 100644 --- a/packages/input-datepicker/src/LionInputDatepicker.js +++ b/packages/input-datepicker/src/LionInputDatepicker.js @@ -322,14 +322,14 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) { // On every validator change, synchronize disabled dates: this means // we need to extract minDate, maxDate, minMaxDate and disabledDates validators validators.forEach(v => { - if (v.name === 'MinDate') { + if (v.constructor.name === 'MinDate') { this.__calendarMinDate = v.param; - } else if (v.name === 'MaxDate') { + } else if (v.constructor.name === 'MaxDate') { this.__calendarMaxDate = v.param; - } else if (v.name === 'MinMaxDate') { + } else if (v.constructor.name === 'MinMaxDate') { this.__calendarMinDate = v.param.min; this.__calendarMaxDate = v.param.max; - } else if (v.name === 'IsDateDisabled') { + } else if (v.constructor.name === 'IsDateDisabled') { this.__calendarDisableDates = v.param; } }); diff --git a/packages/input-email/stories/index.stories.mdx b/packages/input-email/stories/index.stories.mdx index c135649e6..68386148e 100644 --- a/packages/input-email/stories/index.stories.mdx +++ b/packages/input-email/stories/index.stories.mdx @@ -69,9 +69,8 @@ loadDefaultFeedbackMessages(); {() => { class GmailOnly extends Validator { - constructor(...args) { - super(...args); - this.name = 'GmailOnly'; + static get validatorName() { + return 'GmailOnly'; } execute(value) { let hasError = false; @@ -98,9 +97,8 @@ loadDefaultFeedbackMessages(); import { Validator } from '@lion/validate'; class GmailOnly extends Validator { - constructor(...args) { - super(...args); - this.name = 'GmailOnly'; + static get validatorName() { + return 'GmailOnly'; } execute(value) { diff --git a/packages/input-iban/src/validators.js b/packages/input-iban/src/validators.js index b9af119c4..aaf94149c 100644 --- a/packages/input-iban/src/validators.js +++ b/packages/input-iban/src/validators.js @@ -1,8 +1,8 @@ /* eslint-disable max-classes-per-file */ -import { isValidIBAN } from 'ibantools'; -import { Validator } from '@lion/validate'; import { localize } from '@lion/localize'; +import { Validator } from '@lion/validate'; +import { isValidIBAN } from 'ibantools'; let loaded = false; const loadTranslations = async () => { @@ -19,9 +19,8 @@ const loadTranslations = async () => { }; export class IsIBAN extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsIBAN'; + static get validatorName() { + return 'IsIBAN'; } // eslint-disable-next-line class-methods-use-this @@ -36,9 +35,8 @@ export class IsIBAN extends Validator { } export class IsCountryIBAN extends IsIBAN { - constructor(...args) { - super(...args); - this.name = 'IsCountryIBAN'; + static get validatorName() { + return 'IsCountryIBAN'; } execute(value) { diff --git a/packages/radio-group/stories/index.stories.mdx b/packages/radio-group/stories/index.stories.mdx index 5bc947955..332f87740 100644 --- a/packages/radio-group/stories/index.stories.mdx +++ b/packages/radio-group/stories/index.stories.mdx @@ -192,9 +192,8 @@ You can also create a validator that validates whether a certain option is check {() => { class IsBrontosaurus extends Validator { - constructor() { - super(); - this.name = 'IsBrontosaurus'; + static get validatorName() { + return 'IsBrontosaurus'; } execute(value) { 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'; class IsBrontosaurus extends Validator { - constructor() { - super(); - this.name = 'IsBrontosaurus'; + static get validatorName() { + return 'IsBrontosaurus'; } execute(value) { return value === 'brontosaurus'; diff --git a/packages/switch/stories/index.stories.mdx b/packages/switch/stories/index.stories.mdx index 4fe55d2dd..377549155 100644 --- a/packages/switch/stories/index.stories.mdx +++ b/packages/switch/stories/index.stories.mdx @@ -65,9 +65,8 @@ Simple example that illustrates where validation feedback will be displayed. {() => { const IsTrue = class extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsTrue'; + static get validatorName() { + return 'IsTrue'; } execute(value) { return !value.checked; @@ -98,9 +97,8 @@ import { Validator } from '@lion/validate'; import { LionSwitch } from '@lion/switch/src/LionSwitch'; const IsTrue = class extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsTrue'; + static get validatorName() { + return 'IsTrue'; } execute(value) { return !value.checked; diff --git a/packages/validate/README.md b/packages/validate/README.md index 09c14c464..d0e7523c8 100644 --- a/packages/validate/README.md +++ b/packages/validate/README.md @@ -42,9 +42,8 @@ import { Required, IsString, MaxLength, DefaultSuccess, Validator } from '@lion/ const isInitialsRegex = /^([A-Z]\.)+$/; class IsInitialsExample extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsExampleInitials'; + static get validatorName() { + return 'IsExampleInitials'; } execute(value) { diff --git a/packages/validate/src/ValidateMixin.js b/packages/validate/src/ValidateMixin.js index e13fa7f4d..e1f8f2425 100644 --- a/packages/validate/src/ValidateMixin.js +++ b/packages/validate/src/ValidateMixin.js @@ -323,8 +323,12 @@ export const ValidateMixin = dedupeMixin( const /** @type {Validator[]} */ filteredValidators = this._allValidators.filter( v => !(v instanceof ResultValidator) && !(v instanceof Required), ); - const /** @type {Validator[]} */ syncValidators = filteredValidators.filter(v => !v.async); - const /** @type {Validator[]} */ asyncValidators = filteredValidators.filter(v => v.async); + const /** @type {Validator[]} */ syncValidators = filteredValidators.filter( + v => !v.constructor.async, + ); + const /** @type {Validator[]} */ asyncValidators = filteredValidators.filter( + v => v.constructor.async, + ); /** * 2. Synchronous validators @@ -378,7 +382,7 @@ export const ValidateMixin = dedupeMixin( __executeResultValidators(regularValidationResult) { /** @type {ResultValidator[]} */ const resultValidators = this._allValidators.filter( - v => !v.async && v instanceof ResultValidator, + v => !v.constructor.async && v instanceof ResultValidator, ); return resultValidators.filter(v => @@ -415,7 +419,7 @@ export const ValidateMixin = dedupeMixin( if (!validationStates[v.type]) { validationStates[v.type] = {}; } - validationStates[v.type][v.name] = true; + validationStates[v.type][v.constructor.name] = true; }); this.validationStates = validationStates; 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) { // 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 console.error(errorMessage, this); throw new Error(errorMessage); diff --git a/packages/validate/src/Validator.js b/packages/validate/src/Validator.js index 82b78118c..904dcacdb 100644 --- a/packages/validate/src/Validator.js +++ b/packages/validate/src/Validator.js @@ -4,13 +4,19 @@ export class Validator { constructor(param, config) { fakeExtendsEventTarget(this); - this.name = ''; - this.async = false; this.__param = param; this.__config = config || {}; 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 * @param {string|Date|Number|object} modelValue @@ -18,8 +24,10 @@ export class Validator { * @returns {Boolean|Promise} */ execute(/* modelValue, param */) { - if (!this.name) { - throw new Error('You must provide a name like "this.name = \'IsCat\'" for your Validator'); + if (!this.validatorName) { + 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) { const composedData = { - name: this.name, + name: this.constructor.validatorName, type: this.type, params: this.param, config: this.config, diff --git a/packages/validate/src/validators/DateValidators.js b/packages/validate/src/validators/DateValidators.js index f083d6582..16e928ab6 100644 --- a/packages/validate/src/validators/DateValidators.js +++ b/packages/validate/src/validators/DateValidators.js @@ -9,9 +9,8 @@ function isDate(value) { } export class IsDate extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsDate'; + static get validatorName() { + return 'IsDate'; } // eslint-disable-next-line class-methods-use-this @@ -25,9 +24,8 @@ export class IsDate extends Validator { } export class MinDate extends Validator { - constructor(...args) { - super(...args); - this.name = 'MinDate'; + static get validatorName() { + return 'MinDate'; } execute(value, min = this.param) { @@ -40,9 +38,8 @@ export class MinDate extends Validator { } export class MaxDate extends Validator { - constructor(...args) { - super(...args); - this.name = 'MaxDate'; + static get validatorName() { + return 'MaxDate'; } execute(value, max = this.param) { @@ -55,9 +52,8 @@ export class MaxDate extends Validator { } export class MinMaxDate extends Validator { - constructor(...args) { - super(...args); - this.name = 'MinMaxDate'; + static get validatorName() { + return 'MinMaxDate'; } execute(value, { min = 0, max = 0 } = this.param) { @@ -70,9 +66,8 @@ export class MinMaxDate extends Validator { } export class IsDateDisabled extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsDateDisabled'; + static get validatorName() { + return 'IsDateDisabled'; } execute(value, isDisabledFn = this.param) { diff --git a/packages/validate/src/validators/NumberValidators.js b/packages/validate/src/validators/NumberValidators.js index a2618ba6b..520ed398f 100644 --- a/packages/validate/src/validators/NumberValidators.js +++ b/packages/validate/src/validators/NumberValidators.js @@ -11,9 +11,8 @@ const isNumber = value => value === value && typeof value === 'number'; export class IsNumber extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsNumber'; + static get validatorName() { + return 'IsNumber'; } // eslint-disable-next-line class-methods-use-this @@ -27,9 +26,8 @@ export class IsNumber extends Validator { } export class MinNumber extends Validator { - constructor(...args) { - super(...args); - this.name = 'MinNumber'; + static get validatorName() { + return 'MinNumber'; } execute(value, min = this.param) { @@ -42,9 +40,8 @@ export class MinNumber extends Validator { } export class MaxNumber extends Validator { - constructor(...args) { - super(...args); - this.name = 'MaxNumber'; + static get validatorName() { + return 'MaxNumber'; } execute(value, max = this.param) { @@ -57,9 +54,8 @@ export class MaxNumber extends Validator { } export class MinMaxNumber extends Validator { - constructor(...args) { - super(...args); - this.name = 'MinMaxNumber'; + static get validatorName() { + return 'MinMaxNumber'; } execute(value, { min = 0, max = 0 } = this.param) { diff --git a/packages/validate/src/validators/Required.js b/packages/validate/src/validators/Required.js index 13ba04ca4..6ae652766 100644 --- a/packages/validate/src/validators/Required.js +++ b/packages/validate/src/validators/Required.js @@ -1,9 +1,8 @@ import { Validator } from '../Validator.js'; export class Required extends Validator { - constructor(...args) { - super(...args); - this.name = 'Required'; + static get validatorName() { + return 'Required'; } /** diff --git a/packages/validate/src/validators/StringValidators.js b/packages/validate/src/validators/StringValidators.js index 11d56a13a..9f0576c16 100644 --- a/packages/validate/src/validators/StringValidators.js +++ b/packages/validate/src/validators/StringValidators.js @@ -4,9 +4,8 @@ import { Validator } from '../Validator.js'; const isString = value => typeof value === 'string'; export class IsString extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsString'; + static get validatorName() { + return 'IsString'; } // eslint-disable-next-line class-methods-use-this @@ -20,9 +19,8 @@ export class IsString extends Validator { } export class EqualsLength extends Validator { - constructor(...args) { - super(...args); - this.name = 'EqualsLength'; + static get validatorName() { + return 'EqualsLength'; } execute(value, length = this.param) { @@ -35,9 +33,8 @@ export class EqualsLength extends Validator { } export class MinLength extends Validator { - constructor(...args) { - super(...args); - this.name = 'MinLength'; + static get validatorName() { + return 'MinLength'; } execute(value, min = this.param) { @@ -50,9 +47,8 @@ export class MinLength extends Validator { } export class MaxLength extends Validator { - constructor(...args) { - super(...args); - this.name = 'MaxLength'; + static get validatorName() { + return 'MaxLength'; } execute(value, max = this.param) { @@ -65,9 +61,8 @@ export class MaxLength extends Validator { } export class MinMaxLength extends Validator { - constructor(...args) { - super(...args); - this.name = 'MinMaxLength'; + static get validatorName() { + return 'MinMaxLength'; } 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,}))$/; export class IsEmail extends Validator { - constructor(...args) { - super(...args); - this.name = 'IsEmail'; + static get validatorName() { + return 'IsEmail'; } // eslint-disable-next-line class-methods-use-this diff --git a/packages/validate/stories/Overview.stories.mdx b/packages/validate/stories/Overview.stories.mdx index 06a88a473..92589370b 100644 --- a/packages/validate/stories/Overview.stories.mdx +++ b/packages/validate/stories/Overview.stories.mdx @@ -46,11 +46,6 @@ All validators extend from the default `Validator` class. Below example is an ex ```js class MyValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'MyValidator'; - } - execute(modelValue, param) { const hasFeedback = false; if (modelValue === param) { @@ -59,6 +54,10 @@ class MyValidator extends Validator { return hasFeedback; } + static get validatorName() { + return 'Required'; + } + static getMessage({ 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 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 class AsyncValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'AsyncValidator'; - this.async = true; - } - async execute() { console.log('async pending...'); await pause(2000); @@ -126,6 +119,12 @@ class AsyncValidator extends Validator { return true; } + static get validatorName() { + return 'AsyncValidator'; + } + + static get async() { return true; } + static getMessage({ modelValue }) { return `Validated for modelValue: ${modelValue}`; } diff --git a/packages/validate/test-helpers/ExampleValidators.js b/packages/validate/test-helpers/ExampleValidators.js index 014d0d08f..cc54bc5ab 100644 --- a/packages/validate/test-helpers/ExampleValidators.js +++ b/packages/validate/test-helpers/ExampleValidators.js @@ -2,9 +2,8 @@ import { Validator } from '../src/Validator.js'; export class AlwaysInvalid extends Validator { - constructor(...args) { - super(...args); - this.name = 'AlwaysInvalid'; + static get validatorName() { + return 'AlwaysInvalid'; } execute() { @@ -14,9 +13,8 @@ export class AlwaysInvalid extends Validator { } export class AlwaysValid extends Validator { - constructor(...args) { - super(...args); - this.name = 'AlwaysValid'; + static get validatorName() { + return 'AlwaysValid'; } execute() { @@ -26,9 +24,8 @@ export class AlwaysValid extends Validator { } export class AsyncAlwaysValid extends AlwaysValid { - constructor(...args) { - super(...args); - this.async = true; + static get async() { + return true; } execute() { @@ -37,9 +34,8 @@ export class AsyncAlwaysValid extends AlwaysValid { } export class AsyncAlwaysInvalid extends AlwaysValid { - constructor(...args) { - super(...args); - this.async = true; + static get async() { + return true; } async execute() { diff --git a/packages/validate/test-suites/ValidateMixin.suite.js b/packages/validate/test-suites/ValidateMixin.suite.js index 8b965b548..3ee9baa92 100644 --- a/packages/validate/test-suites/ValidateMixin.suite.js +++ b/packages/validate/test-suites/ValidateMixin.suite.js @@ -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 { aTimeout, defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing'; +import sinon from 'sinon'; import { - AlwaysValid, - AlwaysInvalid, - AsyncAlwaysValid, - AsyncAlwaysInvalid, -} from '../test-helpers.js'; - -import { - ValidateMixin, - Unparseable, - Validator, - ResultValidator, - Required, - MinLength, MaxLength, + MinLength, + Required, + ResultValidator, + Unparseable, + ValidateMixin, + Validator, } from '../index.js'; +import { + AlwaysInvalid, + AlwaysValid, + AsyncAlwaysInvalid, + AsyncAlwaysValid, +} from '../test-helpers.js'; export function runValidateMixinSuite(customConfig) { const cfg = { @@ -104,9 +103,12 @@ export function runValidateMixinSuite(customConfig) { class MajorValidator extends Validator { constructor() { super(); - this.name = 'MajorValidator'; this.type = 'major error'; } + + static get validatorName() { + return 'MajorValidator'; + } } const el = await fixture(html`<${tag}>`); expect(() => { @@ -218,9 +220,8 @@ export function runValidateMixinSuite(customConfig) { it('finally checks for ResultValidators: creates TotalValidationResult', async () => { class MyResult extends ResultValidator { - constructor(...args) { - super(...args); - this.name = 'ResultValidator'; + static get validatorName() { + return 'ResultValidator'; } } @@ -285,21 +286,27 @@ export function runValidateMixinSuite(customConfig) { class IsCat extends Validator { constructor(...args) { super(...args); - this.name = 'isCat'; this.execute = (modelValue, param) => { const validateString = param && param.number ? `cat${param.number}` : 'cat'; const showError = modelValue !== validateString; return showError; }; } + + static get validatorName() { + return 'isCat'; + } } class OtherValidator extends Validator { constructor(...args) { super(...args); - this.name = 'otherValidator'; this.execute = () => true; } + + static get validatorName() { + return 'otherValidator'; + } } it('Validators will be called with ".modelValue" as first argument', async () => { @@ -357,11 +364,11 @@ export function runValidateMixinSuite(customConfig) { `); el.modelValue = 'cat'; - expect(el.validationStates.error.isCat).to.be.undefined; + expect(el.validationStates.error.IsCat).to.be.undefined; el.modelValue = 'dog'; - expect(el.validationStates.error.isCat).to.be.true; + expect(el.validationStates.error.IsCat).to.be.true; 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 () => { @@ -391,10 +398,12 @@ export function runValidateMixinSuite(customConfig) { }); class IsAsyncCat extends Validator { - constructor(param, config) { - super(param, config); - this.name = 'delayed-cat'; - this.async = true; + static get validatorName() { + return 'delayed-cat'; + } + + static get async() { + return true; } /** @@ -769,20 +778,26 @@ export function runValidateMixinSuite(customConfig) { class ContainsLowercaseA extends Validator { constructor(...args) { super(...args); - this.name = 'ContainsLowercaseA'; this.execute = modelValue => !modelValue.includes('a'); } + + static get validatorName() { + return 'ContainsLowercaseA'; + } } class ContainsLowercaseB extends Validator { constructor(...args) { super(...args); - this.name = 'containsLowercaseB'; 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` <${tag} .modelValue=${'a'} diff --git a/packages/validate/test-suites/ValidateMixinFeedbackPart.suite.js b/packages/validate/test-suites/ValidateMixinFeedbackPart.suite.js index 85150094c..fc845fe8e 100644 --- a/packages/validate/test-suites/ValidateMixinFeedbackPart.suite.js +++ b/packages/validate/test-suites/ValidateMixinFeedbackPart.suite.js @@ -41,9 +41,8 @@ export function runValidateMixinFeedbackPart() { tag = unsafeStatic(tagString); ContainsLowercaseA = class extends Validator { - constructor(...args) { - super(...args); - this.name = 'ContainsLowercaseA'; + static get validatorName() { + return 'ContainsLowercaseA'; } execute(modelValue) { @@ -53,9 +52,8 @@ export function runValidateMixinFeedbackPart() { }; class ContainsCat extends Validator { - constructor(...args) { - super(...args); - this.name = 'ContainsCat'; + static get validatorName() { + return 'ContainsCat'; } execute(modelValue) { @@ -244,7 +242,7 @@ export function runValidateMixinFeedbackPart() { render() { return html` - Custom for ${this.feedbackData[0].validator.name} + Custom for ${this.feedbackData[0].validator.constructor.name} `; } }, diff --git a/packages/validate/test/DateValidators.test.js b/packages/validate/test/DateValidators.test.js index ea392c247..4415b6023 100644 --- a/packages/validate/test/DateValidators.test.js +++ b/packages/validate/test/DateValidators.test.js @@ -13,7 +13,7 @@ describe('Date Validation', () => { it('provides new isDate() to allow only dates', () => { let isEnabled; const validator = new IsDate(); - expect(validator.name).to.equal('IsDate'); + expect(validator.constructor.name).to.equal('IsDate'); isEnabled = validator.execute(new Date()); expect(isEnabled).to.be.false; @@ -28,7 +28,7 @@ describe('Date Validation', () => { it('provides new minDate(x) to allow only dates after min', () => { let isEnabled; 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')); expect(isEnabled).to.be.false; @@ -46,7 +46,7 @@ describe('Date Validation', () => { it('provides maxDate() to allow only dates before max', () => { let isEnabled; 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')); expect(isEnabled).to.be.false; @@ -67,7 +67,7 @@ describe('Date Validation', () => { min: new Date('2018/02/02'), 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')); expect(isEnabled).to.be.false; @@ -88,7 +88,7 @@ describe('Date Validation', () => { it('provides new IsDateDisabled() to disable dates matching specified condition', () => { let isDisabled; 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')); expect(isDisabled).to.be.false; diff --git a/packages/validate/test/NumberValidators.test.js b/packages/validate/test/NumberValidators.test.js index 43aadbaea..d4a9ed991 100644 --- a/packages/validate/test/NumberValidators.test.js +++ b/packages/validate/test/NumberValidators.test.js @@ -11,7 +11,7 @@ describe('Number Validation', () => { it('provides new IsNumber() to allow only numbers', () => { let isEnabled; const validator = new IsNumber(); - expect(validator.name).to.equal('IsNumber'); + expect(validator.constructor.name).to.equal('IsNumber'); isEnabled = validator.execute(4); expect(isEnabled).to.be.false; @@ -26,7 +26,7 @@ describe('Number Validation', () => { it('provides new MinNumber(x) to allow only numbers longer then min', () => { let isEnabled; const validator = new MinNumber(3); - expect(validator.name).to.equal('MinNumber'); + expect(validator.constructor.name).to.equal('MinNumber'); isEnabled = validator.execute(3); expect(isEnabled).to.be.false; @@ -38,7 +38,7 @@ describe('Number Validation', () => { it('provides new MaxNumber(x) to allow only number shorter then max', () => { let isEnabled; const validator = new MaxNumber(3); - expect(validator.name).to.equal('MaxNumber'); + expect(validator.constructor.name).to.equal('MaxNumber'); isEnabled = validator.execute(3); 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', () => { let isEnabled; 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); expect(isEnabled).to.be.false; diff --git a/packages/validate/test/StringValidators.test.js b/packages/validate/test/StringValidators.test.js index b29d7aaaf..df98ff375 100644 --- a/packages/validate/test/StringValidators.test.js +++ b/packages/validate/test/StringValidators.test.js @@ -13,7 +13,7 @@ describe('String Validation', () => { it('provides new IsString() to allow only strings', () => { let isEnabled; const validator = new IsString(); - expect(validator.name).to.equal('IsString'); + expect(validator.constructor.name).to.equal('IsString'); isEnabled = validator.execute('foo'); expect(isEnabled).to.be.false; @@ -28,7 +28,7 @@ describe('String Validation', () => { it('provides new EqualsLength(x) to allow only a specific string length', () => { let isEnabled; const validator = new EqualsLength(3); - expect(validator.name).to.equal('EqualsLength'); + expect(validator.constructor.name).to.equal('EqualsLength'); isEnabled = validator.execute('foo'); expect(isEnabled).to.be.false; @@ -43,7 +43,7 @@ describe('String Validation', () => { it('provides new MinLength(x) to allow only strings longer then min', () => { let isEnabled; const validator = new MinLength(3); - expect(validator.name).to.equal('MinLength'); + expect(validator.constructor.name).to.equal('MinLength'); isEnabled = validator.execute('foo'); expect(isEnabled).to.be.false; @@ -55,7 +55,7 @@ describe('String Validation', () => { it('provides new MaxLength(x) to allow only strings shorter then max', () => { let isEnabled; const validator = new MaxLength(3); - expect(validator.name).to.equal('MaxLength'); + expect(validator.constructor.name).to.equal('MaxLength'); isEnabled = validator.execute('foo'); 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', () => { let isEnabled; 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'); expect(isEnabled).to.be.false; @@ -82,7 +82,7 @@ describe('String Validation', () => { it('provides new IsEmail() to allow only valid email formats', () => { let isEnabled; const validator = new IsEmail(); - expect(validator.name).to.equal('IsEmail'); + expect(validator.constructor.name).to.equal('IsEmail'); isEnabled = validator.execute('foo@bar.com'); expect(isEnabled).to.be.false; diff --git a/packages/validate/test/Validator.test.js b/packages/validate/test/Validator.test.js index 166b3f428..06184ba1c 100644 --- a/packages/validate/test/Validator.test.js +++ b/packages/validate/test/Validator.test.js @@ -32,14 +32,15 @@ describe('Validator', () => { class MyValidator extends Validator {} expect(() => { 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 () => { class MyValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'MyValidator'; + static get validatorName() { + return 'MyValidator'; } } @@ -62,9 +63,8 @@ describe('Validator', () => { it('has access to name, type, params, config in getMessage provided by config', () => { const configSpy = sinon.spy(); class MyValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'MyValidator'; + static get validatorName() { + return 'MyValidator'; } } 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', () => { let staticArgs; class MyValidator extends Validator { - constructor(...args) { - super(...args); - this.name = 'MyValidator'; + static get validatorName() { + return 'MyValidator'; } static getMessage(...args) {