Unverified Commit eaabc567 authored by Zhou (Link)  Fang's avatar Zhou (Link) Fang Committed by GitHub
Browse files

Added more validation to match back-end ones (#292)

* Added more validation

* improved country validation

* corrected the max length value to 255

* Added more validation

* improved country validation

* corrected the max length value to 255

* Improved return logic based on feedback
parent 5eb16861
...@@ -31,6 +31,7 @@ export const REVIEW = 'Review'; ...@@ -31,6 +31,7 @@ export const REVIEW = 'Review';
export const HAS_TOKEN_EXPIRED = 'HAS_TOKEN_EXPIRED'; export const HAS_TOKEN_EXPIRED = 'HAS_TOKEN_EXPIRED';
export const LOGIN_EXPIRED_MSG = 'Your session has expired, please sign in again.'; export const LOGIN_EXPIRED_MSG = 'Your session has expired, please sign in again.';
export const MAX_LENGTH_HELPER_TEXT = 'The value exceeds max length 255 characters'
export const PATH_NAME_ARRAY = [ export const PATH_NAME_ARRAY = [
'/company-info', '/company-info',
......
...@@ -3,7 +3,12 @@ import { formField } from '../../UIComponents/FormComponents/formFieldModel'; ...@@ -3,7 +3,12 @@ import { formField } from '../../UIComponents/FormComponents/formFieldModel';
import Autocomplete from '@material-ui/lab/Autocomplete'; import Autocomplete from '@material-ui/lab/Autocomplete';
import { TextField } from '@material-ui/core'; import { TextField } from '@material-ui/core';
import DropdownMenu from '../../UIComponents/Inputs/DropdownMenu'; import DropdownMenu from '../../UIComponents/Inputs/DropdownMenu';
import { OPTIONS_FOR_ORG_TYPE, OPTIONS_FOR_REVENUE, OPTIONS_FOR_EMPLOYEE_COUNT, HELPERTEXT_FOR_REVENUE } from '../../../Constants/Constants'; import {
OPTIONS_FOR_ORG_TYPE,
OPTIONS_FOR_REVENUE,
OPTIONS_FOR_EMPLOYEE_COUNT,
HELPERTEXT_FOR_REVENUE,
} from '../../../Constants/Constants';
/** /**
* Render Oraganization selector (used React-Select) * Render Oraganization selector (used React-Select)
...@@ -43,6 +48,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -43,6 +48,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
requiredMark={true} requiredMark={true}
value={formik.values.organization.legalName} value={formik.values.organization.legalName}
onChange={formik.handleChange} onChange={formik.handleChange}
error={formik.touched.organization?.legalName && Boolean(formik.errors.organization?.legalName)}
helperText={formik.errors.organization?.legalName}
/> />
</div> </div>
</div> </div>
...@@ -54,6 +61,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -54,6 +61,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
inputValue={formik.values.organization.type} inputValue={formik.values.organization.type}
optionsArray={OPTIONS_FOR_ORG_TYPE} optionsArray={OPTIONS_FOR_ORG_TYPE}
handleChange={formik.handleChange} handleChange={formik.handleChange}
error={formik.touched.organization?.type && Boolean(formik.errors.organization?.type)}
helperText={formik.errors.organization?.type}
/> />
</div> </div>
<div className="col-md-8"> <div className="col-md-8">
...@@ -89,8 +98,10 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -89,8 +98,10 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
inputName={organizationRevenue.revenue.name} inputName={organizationRevenue.revenue.name}
inputValue={formik.values.organization.revenue} inputValue={formik.values.organization.revenue}
optionsArray={OPTIONS_FOR_REVENUE} optionsArray={OPTIONS_FOR_REVENUE}
helperText={HELPERTEXT_FOR_REVENUE} explanationHelperText={HELPERTEXT_FOR_REVENUE}
handleChange={(ev) => handleFieldChange(ev.target.value, 'organization.revenue')} handleChange={(ev) => handleFieldChange(ev.target.value, 'organization.revenue')}
error={formik.touched.organization?.revenue && Boolean(formik.errors.organization?.revenue)}
helperText={formik.errors.organization?.revenue}
/> />
</div> </div>
<div className="col-md-8"> <div className="col-md-8">
...@@ -100,6 +111,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -100,6 +111,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
inputValue={formik.values.organization.employeeCount} inputValue={formik.values.organization.employeeCount}
optionsArray={OPTIONS_FOR_EMPLOYEE_COUNT} optionsArray={OPTIONS_FOR_EMPLOYEE_COUNT}
handleChange={(ev) => handleFieldChange(ev.target.value, 'organization.employeeCount')} handleChange={(ev) => handleFieldChange(ev.target.value, 'organization.employeeCount')}
error={formik.touched.organization?.employeeCount && Boolean(formik.errors.organization?.employeeCount)}
helperText={formik.errors.organization?.employeeCount}
/> />
</div> </div>
</div> </div>
...@@ -117,6 +130,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -117,6 +130,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
value={formik.values.organization.address.street} value={formik.values.organization.address.street}
onChange={formik.handleChange} onChange={formik.handleChange}
ariaLabel={`${organizationName.name}-address`} ariaLabel={`${organizationName.name}-address`}
error={formik.touched.organization?.address?.street && Boolean(formik.errors.organization?.address?.street)}
helperText={formik.errors.organization?.address?.street}
/> />
</div> </div>
<div className="col-md-8"> <div className="col-md-8">
...@@ -128,6 +143,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -128,6 +143,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
value={formik.values.organization.address.city} value={formik.values.organization.address.city}
onChange={formik.handleChange} onChange={formik.handleChange}
ariaLabel={`${organizationName.name}-address`} ariaLabel={`${organizationName.name}-address`}
error={formik.touched.organization?.address?.city && Boolean(formik.errors.organization?.address?.city)}
helperText={formik.errors.organization?.address?.city}
/> />
</div> </div>
</div> </div>
...@@ -167,8 +184,9 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -167,8 +184,9 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
size="small" size="small"
required={true} required={true}
className={classes.textField} className={classes.textField}
error={Boolean(formik.errors.organization?.address?.country)} error={formik.touched.organization?.address?.city && Boolean(formik.errors.organization?.address?.country)}
helperText={formik.errors.organization?.address?.country} helperText={formik.touched.organization?.address?.city && formik.errors.organization?.address?.country}
oncha
/> />
); );
}} }}
...@@ -184,6 +202,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -184,6 +202,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
value={formik.values.organization.address.provinceOrState} value={formik.values.organization.address.provinceOrState}
onChange={formik.handleChange} onChange={formik.handleChange}
ariaLabel={`${organizationName.name}-address`} ariaLabel={`${organizationName.name}-address`}
error={Boolean(formik.errors.organization?.address?.provinceOrState)}
helperText={formik.errors.organization?.address?.provinceOrState}
/> />
</div> </div>
...@@ -196,6 +216,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => { ...@@ -196,6 +216,8 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
value={formik.values.organization.address.postalCode} value={formik.values.organization.address.postalCode}
onChange={formik.handleChange} onChange={formik.handleChange}
ariaLabel={`${organizationName.name}-address`} ariaLabel={`${organizationName.name}-address`}
error={Boolean(formik.errors.organization?.address?.postalCode)}
helperText={formik.errors.organization?.address?.postalCode}
/> />
</div> </div>
</div> </div>
......
...@@ -107,12 +107,7 @@ const Contacts = ({ formik, formikWG }) => { ...@@ -107,12 +107,7 @@ const Contacts = ({ formik, formikWG }) => {
isWGRepSameAsCompany && formikWG.setFieldValue('workingGroups', newWG); isWGRepSameAsCompany && formikWG.setFieldValue('workingGroups', newWG);
}; };
const generateContacts = ( const generateContacts = (representativeFields, prefix, type, disableInput) => (
representativeFields,
prefix,
type,
disableInput
) => (
<> <>
{representativeFields.map((el, index) => ( {representativeFields.map((el, index) => (
<div key={prefix + index} className="col-md-12"> <div key={prefix + index} className="col-md-12">
...@@ -124,12 +119,13 @@ const Contacts = ({ formik, formikWG }) => { ...@@ -124,12 +119,13 @@ const Contacts = ({ formik, formikWG }) => {
requiredMark={true} requiredMark={true}
disableInput={disableInput} disableInput={disableInput}
onChange={ onChange={
type === 'member' type === 'member' ? (ev) => handleMemberInputChange(ev.target.value, el.name) : formik.handleChange
? (ev) => handleMemberInputChange(ev.target.value, el.name)
: formik.handleChange
} }
value={formik.values.representative?.[type]?.[el.name]} value={formik.values.representative?.[type]?.[el.name]}
error={Boolean(formik.errors.representative?.[type]?.[el.name])} error={
formik.touched.representative?.[type]?.[el.name] &&
Boolean(formik.errors.representative?.[type]?.[el.name])
}
helperText={formik.errors.representative?.[type]?.[el.name]} helperText={formik.errors.representative?.[type]?.[el.name]}
/> />
</div> </div>
......
...@@ -23,17 +23,11 @@ export default function CompanyInformationVAT({ formik }) { ...@@ -23,17 +23,11 @@ export default function CompanyInformationVAT({ formik }) {
return ( return (
<> <>
<h2 <h2 className="fw-600 h4 section-header" id={`${purchasingProcess.name}-ctn`}>
className="fw-600 h4 section-header"
id={`${purchasingProcess.name}-ctn`}
>
Purchasing Process Purchasing Process
<span className="orange-star margin-left-5">*</span> <span className="orange-star margin-left-5">*</span>
</h2> </h2>
<p> <p>Does your organization require a Purchase Order to facilitate payment of your membership dues?</p>
Does your organization require a Purchase Order to facilitate payment of
your membership dues?
</p>
<div className="row"> <div className="row">
<div className="col-md-12 margin-bottom-40"> <div className="col-md-12 margin-bottom-40">
<DropdownMenu <DropdownMenu
...@@ -42,6 +36,11 @@ export default function CompanyInformationVAT({ formik }) { ...@@ -42,6 +36,11 @@ export default function CompanyInformationVAT({ formik }) {
inputValue={formik.values.purchasingAndVAT.purchasingProcess} inputValue={formik.values.purchasingAndVAT.purchasingProcess}
optionsArray={OPTIONS_FOR_PURCHASING_PROCESS} optionsArray={OPTIONS_FOR_PURCHASING_PROCESS}
handleChange={formik.handleChange} handleChange={formik.handleChange}
error={
formik.touched.purchasingAndVAT?.purchasingProcess &&
Boolean(formik.errors.purchasingAndVAT?.purchasingProcess)
}
helperText={formik.errors.purchasingAndVAT?.purchasingProcess}
/> />
</div> </div>
</div> </div>
...@@ -72,6 +71,11 @@ export default function CompanyInformationVAT({ formik }) { ...@@ -72,6 +71,11 @@ export default function CompanyInformationVAT({ formik }) {
value={formik.values.purchasingAndVAT.vatNumber} value={formik.values.purchasingAndVAT.vatNumber}
onChange={formik.handleChange} onChange={formik.handleChange}
ariaLabel={`vatRegistration`} ariaLabel={`vatRegistration`}
error={
formik.touched.purchasingAndVAT?.vatNumber &&
Boolean(formik.errors.purchasingAndVAT?.vatNumber)
}
helperText={formik.errors.purchasingAndVAT?.vatNumber}
/> />
</div> </div>
...@@ -84,6 +88,11 @@ export default function CompanyInformationVAT({ formik }) { ...@@ -84,6 +88,11 @@ export default function CompanyInformationVAT({ formik }) {
value={formik.values.purchasingAndVAT.countryOfRegistration} value={formik.values.purchasingAndVAT.countryOfRegistration}
onChange={formik.handleChange} onChange={formik.handleChange}
ariaLabel={`vatRegistration`} ariaLabel={`vatRegistration`}
error={
formik.touched.purchasingAndVAT?.countryOfRegistration &&
Boolean(formik.errors.purchasingAndVAT?.countryOfRegistration)
}
helperText={formik.errors.purchasingAndVAT?.countryOfRegistration}
/> />
</div> </div>
</div> </div>
......
...@@ -41,6 +41,8 @@ const MembershipLevel = ({ formik }) => { ...@@ -41,6 +41,8 @@ const MembershipLevel = ({ formik }) => {
inputValue={formik.values.membershipLevel} inputValue={formik.values.membershipLevel}
optionsArray={MEMBERSHIP_LEVELS} optionsArray={MEMBERSHIP_LEVELS}
handleChange={formik.handleChange} handleChange={formik.handleChange}
error={formik.touched.membershipLevel && Boolean(formik.errors.membershipLevel)}
helperText={formik.errors.membershipLevel}
/> />
</div> </div>
</div> </div>
......
...@@ -143,30 +143,17 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, formikOrgValue }) => { ...@@ -143,30 +143,17 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, formikOrgValue }) => {
const wgLabelPropery = `${workingGroupsLabel}.${index}.workingGroup-label`; const wgLabelPropery = `${workingGroupsLabel}.${index}.workingGroup-label`;
// if array.find returns a wg obejct, then it means it's already selected // if array.find returns a wg obejct, then it means it's already selected
const selectedWGValue = const selectedWGValue = formik.values.workingGroups.find(
formik.values.workingGroups.find((item) => { (item) =>
if ( item.workingGroup?.label === inputValue && item['workingGroup-label'] === inputValue
item.workingGroup?.label === inputValue && );
item['workingGroup-label'] === inputValue
) {
return true;
} else {
return false;
}
});
// if the wg user types is already selected somewhere else, // if the wg user types is already selected somewhere else,
// then make the validation fail and show error message // then make the validation fail and show error message
if (selectedWGValue) { if (selectedWGValue) {
formik.setFieldValue( formik.setFieldValue(wgLabelPropery, `${inputValue} already selected`);
wgLabelPropery,
`${inputValue} already selected`
);
} else { } else {
formik.setFieldValue( formik.setFieldValue(wgLabelPropery, inputValue || null);
wgLabelPropery,
inputValue || null
);
} }
}} }}
label={WORKING_GROUPS} label={WORKING_GROUPS}
...@@ -176,9 +163,11 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, formikOrgValue }) => { ...@@ -176,9 +163,11 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, formikOrgValue }) => {
required={true} required={true}
className={classes.textField} className={classes.textField}
error={Boolean( error={Boolean(
formik.errors.workingGroups?.[index]?.['workingGroup'] formik.touched.workingGroups?.[index]?.['workingGroup'] &&
formik.errors.workingGroups?.[index]?.['workingGroup']
)} )}
helperText={ helperText={
formik.touched.workingGroups?.[index]?.['workingGroup'] &&
formik.errors.workingGroups?.[index]?.['workingGroup'] formik.errors.workingGroups?.[index]?.['workingGroup']
} }
/> />
......
...@@ -51,6 +51,11 @@ const ParticipationLevel = ({ name, workingGroupUserJoined, fullWorkingGroupList ...@@ -51,6 +51,11 @@ const ParticipationLevel = ({ name, workingGroupUserJoined, fullWorkingGroupList
inputValue={formik.values.workingGroups[theIndex]['participationLevel']} inputValue={formik.values.workingGroups[theIndex]['participationLevel']}
optionsArray={participationLevelOptions} optionsArray={participationLevelOptions}
handleChange={formik.handleChange} handleChange={formik.handleChange}
error={
formik.touched.workingGroups?.[theIndex]?.['participationLevel'] &&
Boolean(formik.errors.workingGroups?.[theIndex]?.['participationLevel'])
}
helperText={formik.errors.workingGroups?.[theIndex]?.['participationLevel']}
/> />
)} )}
</div> </div>
......
import * as yup from 'yup'; import * as yup from 'yup';
import { MAX_LENGTH_HELPER_TEXT } from '../../../Constants/Constants';
import { requiredErrorMsg } from './formFieldModel'; import { requiredErrorMsg } from './formFieldModel';
/** /**
...@@ -35,6 +36,15 @@ const countryList = require('country-list') ...@@ -35,6 +36,15 @@ const countryList = require('country-list')
.getNames() .getNames()
.map((item) => item); .map((item) => item);
const REQUIRED_MAX_YUP = yup.string().required(requiredErrorMsg).max(255, MAX_LENGTH_HELPER_TEXT);
const MAX_YUP = yup.string().max(255, MAX_LENGTH_HELPER_TEXT);
const CONTACT_YUP = yup.object().shape({
email: yup.string().required(requiredErrorMsg).email('Please enter a valid email'),
firstName: REQUIRED_MAX_YUP,
lastName: REQUIRED_MAX_YUP,
jobtitle: REQUIRED_MAX_YUP,
});
export const validationSchema = [ export const validationSchema = [
// First step - company Info // First step - company Info
yup.object().shape({ yup.object().shape({
...@@ -43,9 +53,17 @@ export const validationSchema = [ ...@@ -43,9 +53,17 @@ export const validationSchema = [
address: yup.object().shape({ address: yup.object().shape({
country: yup country: yup
.mixed() .mixed()
.required('Please enter/select a valid country name')
.oneOf(countryList, 'Please enter/select a valid country name'), .oneOf(countryList, 'Please enter/select a valid country name'),
street: REQUIRED_MAX_YUP,
provinceOrState: MAX_YUP,
postalCode: MAX_YUP,
city: REQUIRED_MAX_YUP,
}), }),
legalName: REQUIRED_MAX_YUP,
revenue: REQUIRED_MAX_YUP,
employeeCount: REQUIRED_MAX_YUP,
type: REQUIRED_MAX_YUP,
twitterHandle: yup twitterHandle: yup
.string() .string()
.min(2, 'Twitter handle is too short') .min(2, 'Twitter handle is too short')
...@@ -53,21 +71,20 @@ export const validationSchema = [ ...@@ -53,21 +71,20 @@ export const validationSchema = [
.matches(/^@([A-Za-z0-9_])*$/, 'Please enter a valid Twitter handle'), .matches(/^@([A-Za-z0-9_])*$/, 'Please enter a valid Twitter handle'),
}), }),
representative: yup.object().shape({ representative: yup.object().shape({
member: yup.object().shape({ member: CONTACT_YUP,
email: yup.string().email('Please enter a valid email'), marketing: CONTACT_YUP,
}), accounting: CONTACT_YUP,
marketing: yup.object().shape({ }),
email: yup.string().email('Please enter a valid email'), purchasingAndVAT: yup.object().shape({
}), purchasingProcess: REQUIRED_MAX_YUP,
accounting: yup.object().shape({ vatNumber: MAX_YUP,
email: yup.string().email('Please enter a valid email'), countryOfRegistration: MAX_YUP,
}),
}), }),
}), }),
// Second step - membership level // Second step - membership level
yup.object().shape({ yup.object().shape({
'membershipLevel-label': yup.mixed(), membershipLevel: REQUIRED_MAX_YUP,
}), }),
// Third step - working groups // Third step - working groups
...@@ -77,34 +94,21 @@ export const validationSchema = [ ...@@ -77,34 +94,21 @@ export const validationSchema = [
workingGroup: yup workingGroup: yup
.object() .object()
.nullable() .nullable()
.test( .required('Please enter/select a valid working group')
'workingGroup', .test('workingGroup', 'Please enter/select a valid working group', function (selectedWG) {
'Please enter/select a valid working group', const allWorkingGroups = this.options.parent?.allWorkingGroups;
function (selectedWG) { const typedWG = this.options.parent?.['workingGroup-label'];
const allWorkingGroups = this.options.parent?.allWorkingGroups; const isValid = allWorkingGroups?.includes(typedWG) && selectedWG?.label ? true : false;
const typedWG = this.options.parent?.['workingGroup-label']; return typedWG ? isValid : true;
const isValid = }),
allWorkingGroups?.includes(typedWG) && selectedWG?.label participationLevel: REQUIRED_MAX_YUP,
? true workingGroupRepresentative: CONTACT_YUP,
: false;
return typedWG ? isValid : true;
}
),
workingGroupRepresentative: yup.object().shape({
email: yup.string().email('Please enter a valid email'),
}),
}) })
), ),
}), }),
// Forth, signing Authority // Forth, signing Authority
yup.object().shape({ yup.object().shape({
signingAuthorityRepresentative: yup.object().shape({ signingAuthorityRepresentative: CONTACT_YUP,
firstName: yup.string().required(`${requiredErrorMsg}`),
lastName: yup.string().required(`${requiredErrorMsg}`),
jobtitle: yup.string().required(`${requiredErrorMsg}`),
email: yup.string().email('Please enter a valid email'),
}),
}), }),
]; ];
...@@ -18,7 +18,7 @@ const REVENUE = 'Revenue'; ...@@ -18,7 +18,7 @@ const REVENUE = 'Revenue';
const EMPLOYEE_COUNT = 'Employee Count'; const EMPLOYEE_COUNT = 'Employee Count';
const ORG_TYPE = 'Organization Type'; const ORG_TYPE = 'Organization Type';
export const requiredErrorMsg = 'is required'; export const requiredErrorMsg = 'Required field';
// Initial values passed to Formik, this defines // Initial values passed to Formik, this defines
// the form fields, names, and nesting relations of the whole form // the form fields, names, and nesting relations of the whole form
......
...@@ -14,11 +14,11 @@ const useStyles = makeStyles(() => ({ ...@@ -14,11 +14,11 @@ const useStyles = makeStyles(() => ({
}, },
})); }));
export default function DropdownMenu({ inputLabel, inputName, inputValue, optionsArray, handleChange, helperText }) { export default function DropdownMenu({ inputLabel, inputName, inputValue, optionsArray, handleChange, explanationHelperText, error, helperText }) {
const classes = useStyles(); const classes = useStyles();
return ( return (
<FormControl margin="dense" variant="outlined" required={true} className={classes.formControl}> <FormControl margin="dense" variant="outlined" required={true} className={classes.formControl} error={error}>
<InputLabel>{inputLabel}</InputLabel> <InputLabel>{inputLabel}</InputLabel>
<Select