Skip to content

Commit

Permalink
Merge pull request #202 from DarDevDuck/master
Browse files Browse the repository at this point in the history
Feature: Validation Message with complex Validations
  • Loading branch information
pabloai committed Aug 25, 2020
2 parents 553f43e + 84e14ba commit ce86aab
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 20 deletions.
Expand Up @@ -3,12 +3,15 @@ import { useEffect, useRef, useState } from 'preact/hooks';
import PersonalDetails from '../../../internal/PersonalDetails/PersonalDetails';
import useCoreContext from '../../../../core/Context/useCoreContext';
import { EcontextInputSchema } from '../../types';
import Validator from '../../../../utils/Validator';
import { econtextValidationRules } from '../../validate';

export default function EcontextInput(props) {
const [data, setData] = useState<EcontextInputSchema>({ ...props.data });
const [isValid, setIsValid] = useState(false);
const personalDetailsRef = useRef(null);
const { i18n } = useCoreContext();
const validator = new Validator(econtextValidationRules);

useEffect(() => {
props.onChange({ data, isValid });
Expand Down Expand Up @@ -37,6 +40,7 @@ export default function EcontextInput(props) {
onChange={handleChange}
namePrefix="econtext"
ref={personalDetailsRef}
validator={validator}
/>

{props.showPayButton && props.payButton({ status, label: i18n.get('confirmPurchase') })}
Expand Down
20 changes: 20 additions & 0 deletions src/components/Econtext/validate.ts
@@ -0,0 +1,20 @@
import { email, telephoneNumber } from '../../utils/regex';
export const econtextValidationRules = {
blur: {
default: value => {
return { isValid: value && value.length > 0, errorMessage: true };
},
telephoneNumber: value => {
return {
isValid: value && telephoneNumber.test(value) && (value.length === 10 || value.length === 11),
errorMessage: 'voucher.econtext.telephoneNumber.invalid'
};
},
shopperEmail: value => {
return {
isValid: email.test(value),
errorMessage: true
};
}
}
};
25 changes: 15 additions & 10 deletions src/components/internal/PersonalDetails/PersonalDetails.tsx
Expand Up @@ -7,16 +7,15 @@ import { renderFormField } from '../FormFields';
import { personalDetailsValidationRules } from './validate';
import Validator from '../../../utils/Validator';
import useCoreContext from '../../../core/Context/useCoreContext';
import { PersonalDetailsProps, PersonalDetailsStateError, PersonalDetailsStateValid } from './types';
import { PersonalDetailsProps, PersonalDetailsStateError, PersonalDetailsStateValid, ValidationResult } from './types';
import { checkDateInputSupport } from '../FormFields/InputDate/utils';
import { PersonalDetailsSchema } from '../../../types';

const personalDetailsSchema = ['firstName', 'lastName', 'gender', 'dateOfBirth', 'telephoneNumber', 'shopperEmail'];

export default function PersonalDetails(props: PersonalDetailsProps) {
const { label = '', namePrefix, requiredFields, visibility } = props;
const { label = '', namePrefix, requiredFields, visibility, validator } = props;
const { i18n } = useCoreContext();
const validator = new Validator(personalDetailsValidationRules);
const [data, setData] = useState<PersonalDetailsSchema>(props.data);
const [errors, setErrors] = useState<PersonalDetailsStateError>({});
const [valid, setValid] = useState<PersonalDetailsStateValid>({});
Expand All @@ -25,23 +24,24 @@ export default function PersonalDetails(props: PersonalDetailsProps) {
const eventHandler = (mode: string): Function => (e: Event): void => {
const { name, value } = e.target as HTMLInputElement;
const key = name.split(`${namePrefix}.`).pop();
const isValid = validator.validate(key, mode)(value);
const { isValid, errorMessage }: ValidationResult = validator.validate(key, mode)(value);

setData(prevData => ({ ...prevData, [key]: value }));
setValid(prevValid => ({ ...prevValid, [key]: isValid }));
setErrors(prevErrors => ({ ...prevErrors, [key]: !isValid }));
setErrors(prevErrors => ({ ...prevErrors, [key]: !isValid && errorMessage }));
};

const generateFieldName = (name: string): string => `${namePrefix ? `${namePrefix}.` : ''}${name}`;

useEffect(() => {
const isValid = requiredFields.every(field => validator.validate(field, 'blur')(data[field]));
const isValid = requiredFields.every(field => validator.validate(field, 'blur')(data[field]).isValid);
props.onChange({ data, isValid });
}, [data, valid, errors]);

this.showValidation = () => {
const errorsReducer = (acc, field) => {
acc[field] = !validator.validate(field, 'blur')(data[field]);
const { isValid, errorMessage }: ValidationResult = validator.validate(field, 'blur')(data[field]);
acc[field] = !isValid && errorMessage;
return acc;
};

Expand Down Expand Up @@ -100,7 +100,7 @@ export default function PersonalDetails(props: PersonalDetailsProps) {
<Field
label={i18n.get('dateOfBirth')}
classNameModifiers={['col-50', 'lastName']}
errorMessage={!!errors.dateOfBirth}
errorMessage={errors.dateOfBirth && i18n.get(errors.dateOfBirth.toString())}
helper={isDateInputSupported ? null : i18n.get('dateOfBirth.format')}
>
{renderFormField('date', {
Expand All @@ -114,7 +114,11 @@ export default function PersonalDetails(props: PersonalDetailsProps) {
)}

{requiredFields.includes('telephoneNumber') && (
<Field label={i18n.get('telephoneNumber')} classNameModifiers={['telephoneNumber']} errorMessage={!!errors.telephoneNumber}>
<Field
label={i18n.get('telephoneNumber')}
classNameModifiers={['telephoneNumber']}
errorMessage={errors.telephoneNumber && i18n.get(errors.telephoneNumber.toString())}
>
{renderFormField('tel', {
name: generateFieldName('telephoneNumber'),
value: data.telephoneNumber,
Expand Down Expand Up @@ -144,5 +148,6 @@ PersonalDetails.defaultProps = {
data: {},
onChange: () => {},
visibility: 'editable',
requiredFields: personalDetailsSchema
requiredFields: personalDetailsSchema,
validator: new Validator(personalDetailsValidationRules)
};
11 changes: 9 additions & 2 deletions src/components/internal/PersonalDetails/types.ts
@@ -1,4 +1,5 @@
import { FieldsetVisibility, PersonalDetailsSchema } from '../../../types';
import Validator from '../../../utils/Validator';

export interface PersonalDetailsProps {
label?: string;
Expand All @@ -9,14 +10,15 @@ export interface PersonalDetailsProps {
onChange: (newState: object) => void;
readonly?: boolean;
ref?: any;
validator?: Validator;
}

export interface PersonalDetailsStateError {
firstName?: boolean;
lastName?: boolean;
gender?: boolean;
dateOfBirth?: boolean;
telephoneNumber?: boolean;
dateOfBirth?: string | boolean;
telephoneNumber?: string | boolean;
shopperEmail?: boolean;
}

Expand All @@ -35,3 +37,8 @@ export interface ReadOnlyPersonalDetailsProps {
shopperEmail?: string;
telephoneNumber?: string;
}

export interface ValidationResult {
errorMessage: string;
isValid: boolean;
}
27 changes: 19 additions & 8 deletions src/components/internal/PersonalDetails/validate.ts
@@ -1,17 +1,28 @@
import { email, telephoneNumber } from '../../../utils/regex';
import { unformatDate } from '../FormFields/InputDate/utils';

const isDateOfBirthValid = value => {
if (!value) return false;
const rawValue = unformatDate(value);
const ageDiff = Date.now() - Date.parse(rawValue);
const age = new Date(ageDiff).getFullYear() - 1970;
return age >= 18;
};

export const personalDetailsValidationRules = {
blur: {
default: value => value && value.length > 0,
default: value => {
const isValid: boolean = value && value.length > 0;
return { isValid: isValid, errorMessage: true };
},
dateOfBirth: value => {
if (!value) return false;
const rawValue = unformatDate(value);
const ageDiff = Date.now() - Date.parse(rawValue);
const age = new Date(ageDiff).getFullYear() - 1970;
return age >= 18;
return { isValid: isDateOfBirthValid(value), errorMessage: 'dateOfBirth.invalid' };
},
telephoneNumber: value => {
return { isValid: telephoneNumber.test(value), errorMessage: true };
},
telephoneNumber: value => telephoneNumber.test(value),
shopperEmail: value => email.test(value)
shopperEmail: value => {
return { isValid: email.test(value), errorMessage: true };
}
}
};
2 changes: 2 additions & 0 deletions src/language/locales/en-US.json
Expand Up @@ -88,6 +88,7 @@
"voucher.merchantName": "Merchant",
"voucher.introduction.econtext": "Thank you for your purchase, please use the following information to complete your payment.",
"voucher.telephoneNumber": "Phone Number",
"voucher.econtext.telephoneNumber.invalid": "Telephone number must be 10 or 11 digits long",
"voucher.shopperReference": "Shopper Reference",
"voucher.collectionInstitutionNumber": "Collection Institution Number",
"boletobancario.btnLabel": "Generate Boleto",
Expand Down Expand Up @@ -134,6 +135,7 @@
"mbway.confirmPayment": "Confirm your payment on the MB WAY app",
"shopperEmail.invalid": "Invalid email address",
"dateOfBirth.format": "DD/MM/YYYY",
"dateOfBirth.invalid": "You must be at least 18 years old",
"blik.confirmPayment": "Open your banking app to confirm the payment.",
"blik.invalid": "Enter 6 numbers",
"blik.code": "6-digit code",
Expand Down

0 comments on commit ce86aab

Please sign in to comment.