Add react-select component and integrate into FormHTML

Added `react-select` library dependency to `package.json` and lock file. Created a new `FormHTML` component in `formHTML.tsx` to handle form field rendering, including support for `select` fields using `react-select` for multi-select functionality.
This commit is contained in:
2024-08-10 00:25:44 +02:00
parent 51642612ec
commit d005b72634
6 changed files with 620 additions and 78 deletions
+10 -7
View File
@@ -30,7 +30,7 @@ export const apply: FormData = {
{
label: "Do you meet the minimum pc requirements?",
name: "pc_requirements",
type: "text",
type: "dropdown",
min_length: 2,
max_length: 3,
required: true,
@@ -50,7 +50,7 @@ export const apply: FormData = {
{
label: "What is your preferred pronoun?",
name: "pronoun",
type: "text",
type: "dropdown",
min_length: 0,
max_length: 16,
required: true,
@@ -75,10 +75,12 @@ export const apply: FormData = {
{
label: "Which days are you normally able to come on?",
name: "available_days",
type: "textarea",
type: "select",
min_length: 6,
max_length: 128,
required: true,
drop_down: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
multiple: true
},
{
label: "What time of the day are you normally online?",
@@ -177,10 +179,11 @@ export const apply: FormData = {
.max(168, 'There are only 168 hours in a week')
.required('Please enter the average time you will be available each week'),
available_days: Yup.string()
.min(6, 'Please provide at least 6 characters')
.max(128, 'Please provide at most 128 characters')
.required('Please enter your available days are required'),
available_days: Yup.lazy(value =>
Array.isArray(value)
? Yup.array().of(Yup.string()).min(1).max(7).required('Please select the days you will be available on')
: Yup.string().required('Please select the days you will be available on')
),
available_time: Yup.string()
.min(3, 'Please provide at least 3 characters')
+91
View File
@@ -0,0 +1,91 @@
import React, {useState} from "react";
import './GenericForm.css';
import {Field} from "formik";
import {FormHandlerProps} from "./formInterfaces";
import ReactSelect from 'react-select';
const FormHTML: React.FC<FormHandlerProps> = ({
steps,
currentStep,
handleChange,
setFieldTouched,
setFieldValue,
values,
prevLength,
setPrevLength
}) => {
const currentField = steps[currentStep];
let fieldOptions = currentField.drop_down && Array.isArray(currentField.drop_down)
? currentField.drop_down.map((value: string) => ({ value, label: value }))
: [];
const fieldValues = values[currentStep] ? values[currentStep].split(', ').map(value => ({ value, label: value })) : []
const [selectedOptions, setSelectedOptions] = useState(fieldValues);
if (currentField.type === 'select') {
return (
<ReactSelect
name={currentField.name}
isMulti={true}
options={fieldOptions}
value={selectedOptions}
onChange={(selected) => {
let value: string = '';
if (selected === null) {
value = '';
setSelectedOptions([]);
} else if (Array.isArray(selected)) {
value = selected.map(s => s.value).join(', ');
setSelectedOptions(selected);
}
setFieldValue(currentField.name, value);
if (prevLength !== value.length) {
setFieldTouched(currentField.name);
setPrevLength(value.length);
}
}}
/>
);
} else if (currentField.type === 'dropdown') {
return (
<Field as="select"
name={steps[currentStep].name}
{...(steps[currentStep].multiple && { multiple: true })}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
handleChange(e);
if (prevLength !== values[steps[currentStep].name].length) {
setFieldTouched(steps[currentStep].name);
setPrevLength(values[steps[currentStep].name].length);
}
}}>
{steps[currentStep].drop_down?.map((option, i) => (
<option key={i} value={option}>
{option}
</option>
))}
</Field>
);
} else {
return (
<Field
type={steps[currentStep].type}
name={steps[currentStep].name}
required={steps[currentStep].required}
min={steps[currentStep].min_length}
max={steps[currentStep].max_length}
as={(steps[currentStep].type === "textarea") ? "textarea" : "input"}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleChange(e);
if (prevLength !== values[steps[currentStep].name].length) {
setFieldTouched(steps[currentStep].name);
setPrevLength(values[steps[currentStep].name].length);
}
}}
/>
);
}
};
export default FormHTML;
+15 -2
View File
@@ -1,4 +1,5 @@
import * as Yup from "yup";
import React from "react";
type InputNames = "username" | "email" | "question" | "discord" | "pc_requirements" | "age" | "pronoun" | "join_date" |
"avg_time" | "available_days" | "available_time" | "staff_experience" | "plugin_experience" | "why_staff" |
@@ -7,12 +8,13 @@ type InputNames = "username" | "email" | "question" | "discord" | "pc_requiremen
export interface Step {
label: string;
name: InputNames;
type: "text" | "email" | "textarea" | "date";
type: "text" | "email" | "textarea" | "date" | "dropdown" | "select";
min_length: number;
max_length: number;
required: boolean;
additional_info?: string;
drop_down?: string[]
multiple?: boolean
}
export interface UserInput {
@@ -30,4 +32,15 @@ export type FormData = {
export interface FormProperties {
path: string
formData: FormData;
}
}
export type FormHandlerProps = {
steps: Step[];
currentStep: number;
handleChange: (event: React.ChangeEvent<any>) => void;
setFieldTouched: (name: string) => void;
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void;
values: UserInput;
prevLength: number;
setPrevLength: (len: number) => void;
};
+8 -35
View File
@@ -4,6 +4,8 @@ import {useNavigate} from 'react-router-dom'
import {ErrorMessage, Field, Form, FormikProvider, FormikValues, useFormik} from "formik";
import {Step, UserInput, FormData} from './formInterfaces';
import * as Yup from "yup";
import formHTML from "./formHTML";
import FormHTML from "./formHTML";
const GenericForm = (formData: FormData) => {
const steps: Step[] = formData.steps;
@@ -16,6 +18,7 @@ const GenericForm = (formData: FormData) => {
const [currentStep, setCurrentStep] = useState<number>(0);
const handleSubmit = async (formikValues: FormikValues) => {
console.log(JSON.stringify(formikValues))
try {
const response = await fetch(backend, {
method: 'POST',
@@ -71,6 +74,7 @@ const GenericForm = (formData: FormData) => {
handleChange,
values,
setFieldTouched,
setFieldValue,
} = formik;
const [prevLength, setPrevLength] = useState(0);
@@ -99,41 +103,10 @@ const GenericForm = (formData: FormData) => {
</label>
</label>
<label>
{
steps[currentStep].drop_down ? (
<Field as="select"
name={steps[currentStep].name}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
handleChange(e);
if (prevLength !== values[steps[currentStep].name].length) {
setFieldTouched(steps[currentStep].name);
setPrevLength(values[steps[currentStep].name].length);
}
}}>
{steps[currentStep].drop_down?.map((option, i) => (
<option key={i} value={option}>
{option}
</option>
))}
</Field>
) : (
<Field
type={steps[currentStep].type}
name={steps[currentStep].name}
required={steps[currentStep].required}
min={steps[currentStep].min_length}
max={steps[currentStep].max_length}
as={(steps[currentStep].type === "textarea") ? "textarea" : "input"}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleChange(e);
if (prevLength !== values[steps[currentStep].name].length) {
setFieldTouched(steps[currentStep].name);
setPrevLength(values[steps[currentStep].name].length);
}
}}
/>
)
}
<FormHTML steps={steps} currentStep={currentStep} handleChange={handleChange}
setFieldTouched={setFieldTouched} setFieldValue={setFieldValue} values={values}
prevLength={prevLength} setPrevLength={setPrevLength}>
</FormHTML>
</label>
</div>
<div>