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

Change some autocomplete fields to dropdown menus (#225)

* changed purchasing process to dropdown menu

* changed membership level to dropdown menu

* changed wg level to dropdown menu

* Added a dropdown component

* improved autocomplete field

* fixed react only mode issue

* fixed an issue when clear wg selections in wg page

* Updated autocomplete's behaviour

* Made minor changes to work with latest back end logic

* updated autocomplete for wg field

* updated the error text style
parent d2143617
......@@ -68,7 +68,7 @@ export const CONTACT_TYPE = {
SIGNING: 'SIGNING',
};
export const OPTIONS_FOR_PURCHASING_PROCES = [
export const OPTIONS_FOR_PURCHASING_PROCESS = [
{ label: 'Yes', value: 'yes' },
{ label: 'No', value: 'no' },
{ label: 'Not Applicable', value: 'na' },
......
......@@ -40,7 +40,6 @@ export interface FormValue {
};
purchasingAndVAT: {
purchasingProcess: string;
'purchasingProcess-label': { label: string; value: string };
vatNumber: number | string;
countryOfRegistration: string;
};
......
......@@ -7,7 +7,6 @@ import {
getCurrentMode,
MODE_REACT_ONLY,
MODE_REACT_API,
OPTIONS_FOR_PURCHASING_PROCES,
PATH_NAME_ARRAY,
HAS_TOKEN_EXPIRED,
} from '../Constants/Constants';
......@@ -96,37 +95,16 @@ export function matchCompanyFields(existingOrganizationData) {
* Existing purchasing process and VAT data, fetched from server
*/
export function mapPurchasingAndVAT(existingPurchasingAndVATData) {
const currentOption = OPTIONS_FOR_PURCHASING_PROCES.find(
(item) =>
item.value === existingPurchasingAndVATData?.purchase_order_required
);
return {
// Step1: purchasing process and VAT Info
id: existingPurchasingAndVATData?.id || '',
isRegistered: !!existingPurchasingAndVATData?.registration_country,
purchasingProcess: existingPurchasingAndVATData?.purchase_order_required,
'purchasingProcess-label': currentOption,
vatNumber: existingPurchasingAndVATData?.vat_number,
countryOfRegistration: existingPurchasingAndVATData?.registration_country,
};
}
/**
* @param membershipLevel -
* Existing membershipLevel data, fetched from server
* @param membership_levels
* Options of membership levels, created in Constants file, passed from membership level step
*/
export function mapMembershipLevel(existingMembershipLevel, membership_levels) {
let membership = membership_levels.find(
(el) => el.value === existingMembershipLevel
);
return {
label: membership?.label,
value: existingMembershipLevel,
};
}
/**
* @param existingContactData -
* Existing Contacts data, fetched from server
......@@ -279,6 +257,7 @@ export function matchMembershipLevelFieldsToBackend(
vat_number: membershipLevelFormData.purchasingAndVAT.vatNumber,
registration_country:
membershipLevelFormData.purchasingAndVAT.countryOfRegistration,
state: 'INPROGRESS',
};
}
......@@ -670,6 +649,8 @@ export function handleNewForm(setCurrentFormId, goToCompanyInfoStep) {
var dataBody = {
membership_level: '',
signing_authority: false,
purchase_order_required: 'na',
state: 'INPROGRESS',
};
fetch(API_PREFIX_FORM, {
......
import { useContext, useEffect, useState } from 'react';
import MembershipContext from '../../../Context/MembershipContext';
import {
mapMembershipLevel,
mapPurchasingAndVAT,
matchCompanyFields,
matchContactFields,
......@@ -18,7 +17,6 @@ import {
getCurrentMode,
MODE_REACT_ONLY,
MODE_REACT_API,
MEMBERSHIP_LEVELS,
} from '../../../Constants/Constants';
import CustomStepButton from '../../UIComponents/Button/CustomStepButton';
import CompanyInformationVAT from './CompanyInformationVAT';
......@@ -41,7 +39,9 @@ const useStyles = makeStyles(() => ({
textField: {
marginBottom: 14,
marginTop: 6,
backgroundColor: 'white',
'& > div': {
backgroundColor: 'white',
},
},
}));
......@@ -174,16 +174,9 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
})
.then((data) => {
if (data) {
// mapMembershipLevel(): Call the the function to map
// the retrived membership level backend data to fit frontend, and
// setFieldValue(): Prefill Data --> Call the setFieldValue of
// Formik, to set membershipLevel field with the mapped data
const tempMembershipLevel = mapMembershipLevel(
data.membership_level,
MEMBERSHIP_LEVELS
);
setFieldValue('membershipLevel', tempMembershipLevel.value);
setFieldValue('membershipLevel-label', tempMembershipLevel);
setFieldValue('membershipLevel', data.membership_level);
const tempPurchasingAndVAT = mapPurchasingAndVAT(data);
setFieldValue('purchasingAndVAT', tempPurchasingAndVAT);
......@@ -225,7 +218,7 @@ const CompanyInformation = ({ formik, isStartNewForm }) => {
</p>
<CompanyInformationCompany formik={formik} useStyles={useStyles} />
<CompanyInformationContacts formik={formik} />
<CompanyInformationVAT formik={formik} useStyles={useStyles} />
<CompanyInformationVAT formik={formik} />
</div>
<CustomStepButton
......
......@@ -86,14 +86,16 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
<Autocomplete
id={organizationAddress.country.name}
options={countryList}
getOptionLabel={(option) => (option?.label ? option.label : '')}
getOptionLabel={(option) => option?.label || ''}
getOptionSelected={(option, value) => option.value === value.value}
fullWidth={true}
freeSolo={true}
openOnFocus={true}
onChange={(ev, value) => {
// this is only for display
formik.setFieldValue(
`${organizationAddress.country.name}-label`,
value ? value : null
value || null
);
// this is the data will be actually used
......@@ -102,11 +104,7 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
value ? value.value : null
);
}}
value={
formik.values.organization.address['country-label']
? formik.values.organization.address['country-label']
: null
}
value={formik.values.organization.address['country-label'] || null}
renderInput={(params) => {
params.inputProps = {
...params.inputProps,
......@@ -115,12 +113,20 @@ const CompanyInformationCompany = ({ formik, useStyles }) => {
return (
<TextField
{...params}
onChange={(ev) => {
formik.setFieldValue(
organizationAddress.country.name,
ev.target.value || null
);
}}
label="Country"
placeholder="Country"
variant="outlined"
size="small"
required={true}
className={classes.textField}
error={Boolean(formik.errors.organization?.address?.country)}
helperText={formik.errors.organization?.address?.country}
/>
);
}}
......
import Input from '../../UIComponents/Inputs/Input';
import { formField } from '../../UIComponents/FormComponents/formFieldModel';
import { TextField, Checkbox, FormControlLabel } from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { OPTIONS_FOR_PURCHASING_PROCES } from '../../../Constants/Constants';
import { Checkbox, FormControlLabel } from '@material-ui/core';
import DropdownMenu from '../../UIComponents/Inputs/DropdownMenu';
import { OPTIONS_FOR_PURCHASING_PROCESS } from '../../../Constants/Constants';
const { purchasingProcess, vatRegistration } = formField;
export default function CompanyInformationVAT({ formik, useStyles }) {
const classes = useStyles();
export default function CompanyInformationVAT({ formik }) {
const handleIsRegistered = () => {
const isRegistered = formik.values.purchasingAndVAT.isRegistered;
// use spread operator to avoid editing formik.values directly
let purchasingAndVATValue = { ...formik.values.purchasingAndVAT };
if (isRegistered) {
......@@ -38,47 +36,12 @@ export default function CompanyInformationVAT({ formik, useStyles }) {
</p>
<div className="row">
<div className="col-md-12 margin-bottom-40">
<Autocomplete
id={purchasingProcess.name}
options={OPTIONS_FOR_PURCHASING_PROCES}
getOptionLabel={(option) => (option?.label ? option.label : '')}
getOptionSelected={(option, value) => option.value === value.value}
fullWidth={true}
onChange={(ev, value) => {
// this is only for display
formik.setFieldValue(
`${purchasingProcess.name}-label`,
value ? value : null
);
// this is the data will be actually used
formik.setFieldValue(
purchasingProcess.name,
value ? value.value : null
);
}}
value={
formik.values.purchasingAndVAT['purchasingProcess-label']
? formik.values.purchasingAndVAT['purchasingProcess-label']
: null
}
renderInput={(params) => {
params.inputProps = {
...params.inputProps,
'aria-labelledby': `${purchasingProcess.name}-ctn`,
};
return (
<TextField
{...params}
label={purchasingProcess.label}
placeholder={purchasingProcess.placeholder}
variant="outlined"
size="small"
required={true}
className={classes.textField}
/>
);
}}
<DropdownMenu
inputLabel={purchasingProcess.label}
inputName={purchasingProcess.name}
inputValue={formik.values.purchasingAndVAT.purchasingProcess}
optionsArray={OPTIONS_FOR_PURCHASING_PROCESS}
handleChange={formik.handleChange}
/>
</div>
</div>
......
import MembershipLevelFeeTable from './MembershipLevelFeeTable';
import { makeStyles, TextField } from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CustomStepButton from '../../UIComponents/Button/CustomStepButton';
import { formField } from '../../UIComponents/FormComponents/formFieldModel';
import { MEMBERSHIP_LEVELS } from '../../../Constants/Constants';
import { useEffect } from 'react';
import { scrollToTop } from '../../../Utils/formFunctionHelpers';
import { MEMBERSHIP_LEVELS } from '../../../Constants/Constants';
import DropdownMenu from '../../UIComponents/Inputs/DropdownMenu';
/**
* Render membership select component (use React-Select), with fetch and prefill data operation
......@@ -13,19 +12,10 @@ import { scrollToTop } from '../../../Utils/formFunctionHelpers';
* - Props:
* - otherProps: any other props passing down from FormikStepper components, including formik props of formik library (such as "formik.values", "formik.setFieldValue");
* - formField: the form field in formModels/formFieldModel.js;
*/
const useStyles = makeStyles(() => ({
textField: {
marginBottom: 14,
marginTop: 6,
backgroundColor: 'white',
},
}));
*/
const MembershipLevel = ({ formik }) => {
const { membershipLevel } = formField;
const classes = useStyles();
useEffect(() => {
scrollToTop();
......@@ -44,49 +34,12 @@ const MembershipLevel = ({ formik }) => {
</h2>
<div className="row">
<div className="col-md-12">
<Autocomplete
id={membershipLevel.name}
options={MEMBERSHIP_LEVELS}
fullWidth={true}
getOptionLabel={(option) => (option?.label ? option.label : '')}
getOptionSelected={(option, value) => {
return option.value === value.value;
}}
onChange={(ev, value) => {
// this is only for display
formik.setFieldValue(
`${membershipLevel.name}-label`,
value ? value : null
);
// this is the data will be actually used
formik.setFieldValue(
membershipLevel.name,
value ? value.value : null
);
}}
value={
formik.values['membershipLevel-label']
? formik.values['membershipLevel-label']
: null
}
renderInput={(params) => {
params.inputProps = {
...params.inputProps,
'aria-labelledby': membershipLevel.name,
};
return (
<TextField
{...params}
label="Select a level"
placeholder="Select a level"
variant="outlined"
size="small"
required={true}
className={classes.textField}
/>
);
}}
<DropdownMenu
inputLabel="Select a level"
inputName={membershipLevel.name}
inputValue={formik.values.membershipLevel}
optionsArray={MEMBERSHIP_LEVELS}
handleChange={formik.handleChange}
/>
</div>
</div>
......
......@@ -169,7 +169,7 @@ const Review: React.FC<ReviewProps> = ({ values, submitForm }) => {
<div className="col-md-8">
<label>Require Purchasing Process</label>
<div className="preview-field">
{values.purchasingAndVAT['purchasingProcess-label']['label']}
{values.purchasingAndVAT.purchasingProcess}
</div>
</div>
......
......@@ -43,6 +43,7 @@ class SignIn extends React.Component {
getFakeUser = (setFurthestPage) => {
setFurthestPage({ index: 1, pathName: '/company-info' });
this.context.setCurrentFormId('reactOnly');
fetch('membership_data/fake_user.json', { headers: FETCH_HEADER })
.then((resp) => resp.json())
.then((data) => {
......
......@@ -45,7 +45,9 @@ const useStyles = makeStyles(() => ({
textField: {
marginBottom: 14,
marginTop: 6,
backgroundColor: 'white',
'& > div': {
backgroundColor: 'white',
},
},
}));
......@@ -65,6 +67,26 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, isLoading }) => {
);
};
const updateValidationSchema = (workingGroupsLabel, index) => {
const allWorkingGroups = fullWorkingGroupList.map((item) => item.label);
const savedAllWorkingGroups =
formik.values.workingGroups?.[index]?.['allWorkingGroups'];
if (
(!savedAllWorkingGroups || savedAllWorkingGroups.length === 0) &&
allWorkingGroups.length > 0
) {
// using setTimeout here will avoid a React warning:
// "Cannot update a component (`Application`) while rendering a different component (`FieldArrayInner`)"
// with setTimeout, formik.setFieldValue will run after FieldArrayInner finishes rendering
setTimeout(() => {
formik.setFieldValue(
`${workingGroupsLabel}.${index}.allWorkingGroups`,
allWorkingGroups
);
}, 0);
}
};
return isLoading ? (
<Loading />
) : (
......@@ -75,6 +97,7 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, isLoading }) => {
{formik.values.workingGroups?.length > 0 &&
formik.values.workingGroups.map((workingGroup, index) => (
<div key={index}>
{updateValidationSchema(workingGroupsLabel, index)}
<h2
className="h4 fw-600"
id={`${formik.values.workingGroups}.${index}.workingGroup`}
......@@ -86,46 +109,40 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, isLoading }) => {
<Autocomplete
id={`${workingGroupsLabel}.${index}.workingGroup`}
options={fullWorkingGroupList}
getOptionLabel={(option) =>
option?.label ? option.label : ''
}
getOptionLabel={(option) => option?.label || ''}
getOptionSelected={(option, value) =>
option.value === value.value
}
getOptionDisabled={(option) => {
// getOptionDisabled need a boolen,
// getOptionDisabled needs a boolen,
// so here we use !! for the result of array.find
// selectedWG will be true if the WG is already selected
// In this way, all selected WGs will be disabled
const selectedWG = !!formik.values.workingGroups.find(
(selectedWG) =>
selectedWG.workingGroup.label === option.label
selectedWG?.workingGroup?.label === option?.label
);
return selectedWG;
}}
fullWidth={true}
freeSolo={true}
openOnFocus={true}
onChange={(ev, value) => {
// this to clear the participation level when user selects another working group
formik.setFieldValue(
`workingGroups.${index}.participationLevel`,
''
);
// this is only for display
formik.setFieldValue(
`${workingGroupsLabel}.${index}.workingGroup-label`,
value ? value.label : null
);
// this is the data will be actually used
const currentValue = formik.values.workingGroups[index];
const updatedValue = {
...currentValue,
// this to clear the participation level when user selects another working group
participationLevel: '',
'workingGroup-label': value?.label || null,
workingGroup: value || null,
};
formik.setFieldValue(
`${workingGroupsLabel}.${index}.workingGroup`,
value ? value : null
`workingGroups.${index}`,
updatedValue
);
}}
value={
formik.values.workingGroups[index]['workingGroup']
? formik.values.workingGroups[index]['workingGroup']
: null
formik.values.workingGroups[index]['workingGroup'] || null
}
renderInput={(params) => {
params.inputProps = {
......@@ -136,12 +153,49 @@ const WorkingGroup = ({ formik, fullWorkingGroupList, isLoading }) => {
return (
<TextField
{...params}
onChange={(ev) => {
const inputValue = ev.target.value;
const wgLabelPropery = `${workingGroupsLabel}.${index}.workingGroup-label`;
// if array.find returns a wg obejct, then it means it's already selected
const selectedWGValue =
formik.values.workingGroups.find((item) => {
if (
item.workingGroup?.label === inputValue &&
item['workingGroup-label'] === inputValue
) {
return true;
} else {
return false;
}
});
// if the wg user types is already selected somewhere else,
// then make the validation fail and show error message
if (selectedWGValue) {
formik.setFieldValue(
wgLabelPropery,
`${inputValue} already selected`
);
} else {
formik.setFieldValue(
wgLabelPropery,
inputValue || null
);
}
}}
label={WORKING_GROUPS}
placeholder="Select a group"
variant="outlined"
size="small"
required={true}
className={classes.textField}
error={Boolean(
formik.errors.workingGroups?.[index]?.['workingGroup']
)}
helperText={
formik.errors.workingGroups?.[index]?.['workingGroup']
}
/>
);
}}
......
import { useState, useEffect } from 'react';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { makeStyles, TextField } from '@material-ui/core';
import DropdownMenu from '../../UIComponents/Inputs/DropdownMenu';
/**
* Render Participation level selector component (React-Select)
......@@ -11,14 +10,6 @@ import { makeStyles, TextField } from '@material-ui/core';
* - workingGroup: selected working group
*/
const useStyles = makeStyles(() => ({
textField: {
marginBottom: 14,
marginTop: 6,
backgroundColor: 'white',
},
}));
const ParticipationLevel = ({
name,
workingGroupUserJoined,
......@@ -26,7 +17,6 @@ const ParticipationLevel = ({
formik,
index,
}) => {
const classes = useStyles();
const [participationLevelOptions, setParticipationLevelOptions] = useState(
[]
);
......@@ -41,53 +31,45 @@ const ParticipationLevel = ({
);
// extract all the participation_levels
const optionsForParticipationLevels = temp?.participation_levels
? temp?.participation_levels.map((item) => item.description)
let optionsForParticipationLevels = temp?.participation_levels
? temp?.participation_levels.map(
(item) => item.description || item.level
)
: [];
// the Set will deduplicate participation_levels options
setParticipationLevelOptions([...new Set(optionsForParticipationLevels)]);