diff --git a/npm/ng-packs/packages/core/src/lib/tests/validators.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/validators.spec.ts index 699402751e..179bcccaaa 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/validators.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/validators.spec.ts @@ -1,5 +1,5 @@ import { FormControl, Validators } from '@angular/forms'; -import { AbpValidators, validateRange } from '../validators'; +import { AbpValidators, validateMinAge, validateRange } from '../validators'; import { validateCreditCard } from '../validators/credit-card.validator'; import { validateRequired } from '../validators/required.validator'; import { validateStringLength } from '../validators/string-length.validator'; @@ -53,6 +53,44 @@ describe('Validators', () => { }); }); + describe('Min Age Validator', () => { + const date = new Date(); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + const sameDay21yAgo = `${year - 21}-${month}-${day}`; + const nextDay21yAgo = `${year - 21}-${month}-${day + 1}`; + + test.each` + input | options | expected + ${null} | ${undefined} | ${null} + ${undefined} | ${undefined} | ${null} + ${''} | ${undefined} | ${null} + ${0} | ${undefined} | ${null} + ${Infinity} | ${undefined} | ${{ minAge: { age: 18 } }} + ${sameDay21yAgo} | ${undefined} | ${null} + ${sameDay21yAgo} | ${{ age: 21 }} | ${null} + ${nextDay21yAgo} | ${{ age: 21 }} | ${{ minAge: { age: 21 } }} + `( + 'should return $expected when input is $input and options are $options', + ({ input, options, expected }) => { + const control = new FormControl(input, [validateMinAge(options)]); + control.markAsDirty({ onlySelf: true }); + control.updateValueAndValidity({ onlySelf: true, emitEvent: false }); + + expect(control.errors).toEqual(expected); + }, + ); + + it('should return null when control is pristine', () => { + const invalidDate = ''; + const control = new FormControl(invalidDate, [validateMinAge({ age: Infinity })]); + // control is not dirty + + expect(control.valid).toBe(true); + }); + }); + describe('Range Validator', () => { test.each` input | options | expected diff --git a/npm/ng-packs/packages/core/src/lib/validators/age.validator.ts b/npm/ng-packs/packages/core/src/lib/validators/age.validator.ts new file mode 100644 index 0000000000..a57c2ba38b --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/validators/age.validator.ts @@ -0,0 +1,29 @@ +import { AbstractControl, ValidatorFn } from '@angular/forms'; + +export interface MinAgeError { + minAge: { + age: number; + }; +} + +export interface MinAgeOptions { + age?: number; +} + +export function validateMinAge({ age = 18 }: MinAgeOptions = {}): ValidatorFn { + return (control: AbstractControl): MinAgeError | null => { + if (control.pristine) return null; + + if (['', null, undefined].indexOf(control.value) > -1) return null; + + return isValidMinAge(control.value, age) ? null : { minAge: { age } }; + }; +} + +function isValidMinAge(value: string | number, minAge: number) { + const date = new Date(); + date.setFullYear(date.getFullYear() - minAge); + date.setHours(23, 59, 59, 999); + + return Number(new Date(value)) <= date.valueOf(); +} diff --git a/npm/ng-packs/packages/core/src/lib/validators/index.ts b/npm/ng-packs/packages/core/src/lib/validators/index.ts index 4fcf06262d..fb06e7f1d8 100644 --- a/npm/ng-packs/packages/core/src/lib/validators/index.ts +++ b/npm/ng-packs/packages/core/src/lib/validators/index.ts @@ -1,9 +1,11 @@ import { Validators } from '@angular/forms'; +import { validateMinAge } from './age.validator'; import { validateCreditCard } from './credit-card.validator'; import { validateRange } from './range.validator'; import { validateRequired } from './required.validator'; import { validateStringLength } from './string-length.validator'; import { validateUrl } from './url.validator'; +export * from './age.validator'; export * from './credit-card.validator'; export * from './range.validator'; export * from './required.validator'; @@ -13,6 +15,7 @@ export * from './url.validator'; export const AbpValidators = { creditCard: validateCreditCard, emailAddress: () => Validators.email, + minAge: validateMinAge, range: validateRange, required: validateRequired, stringLength: validateStringLength,