Skip to content

Commit

Permalink
React: Extended StatelessForm
Browse files Browse the repository at this point in the history
  • Loading branch information
gaborrosta committed Apr 20, 2024
1 parent d320da2 commit 79ae5a1
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 30 deletions.
74 changes: 44 additions & 30 deletions frontend/src/utils/StatelessForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,45 +31,59 @@ export default function StatelessForm({ formData, validationSchema, fieldErrors,
//Handle form field changes
const handleChange = (event) => {
//Get the name, value, files of the field
let { name, value, files } = event.target;
let { name, value, files, more } = event.target;

//Update the form data
const newFormData = { ...formData, [name]: value };
//Initialize new form data and field errors
let newFormData = { ...formData };
let newFieldErrors = { ...fieldErrors };

//Update the field errors
const newFieldErrors = { ...fieldErrors };
//Function to update form data and field errors
const updateDataAndErrors = (name, value, files) => {
//Update the form data
newFormData[name] = value;

//Validate the field
const fieldSchema = validationSchema[name];
if (fieldSchema.fileTypes !== undefined) {
//It is a file, so delete the saved value.
delete newFormData[name];
//Validate the field
const fieldSchema = validationSchema[name];
if (fieldSchema.fileTypes !== undefined) {
//It is a file, so delete the saved value.
delete newFormData[name];

if (files === null) {
newFieldErrors[name] = "field-required";
newFormData[name] = null;
} else {
//Only one file is handled
const file = files[0];

//Validate the file
if (file === undefined && fieldSchema.required) {
if (files === null) {
newFieldErrors[name] = "field-required";
newFormData[name] = null;
} else if (fieldSchema.fileTypes.includes(file.type)) {
delete newFieldErrors[name];
newFormData[name] = { file: file, url: URL.createObjectURL(file) };
} else {
newFieldErrors[name] = fieldSchema.fileError;
newFormData[name] = null;
//Only one file is handled
const file = files[0];

//Validate the file
if (file === undefined && fieldSchema.required) {
newFieldErrors[name] = "field-required";
newFormData[name] = null;
} else if (fieldSchema.fileTypes.includes(file.type)) {
delete newFieldErrors[name];
newFormData[name] = { file: file, url: URL.createObjectURL(file) };
} else {
newFieldErrors[name] = fieldSchema.fileError;
newFormData[name] = null;
}
}
} else if (value === "" && fieldSchema.required) {
newFieldErrors[name] = "field-required";
} else if (!fieldSchema.regex.test(value)) {
newFieldErrors[name] = fieldSchema.regexError;
} else {
delete newFieldErrors[name];
}
} else if (value === "" && fieldSchema.required) {
newFieldErrors[name] = "field-required";
} else if (!fieldSchema.regex.test(value)) {
newFieldErrors[name] = fieldSchema.regexError;
} else {
delete newFieldErrors[name];
};

//Update form data and field errors for the main target
updateDataAndErrors(name, value, files);

//If the more property exists, update form data and field errors for each item in the more array
if (more) {
more.forEach((item) => {
updateDataAndErrors(item.name, item.value, item.files);
});
}

//Call the custom validator
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/utils/StatelessForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,4 +380,51 @@ describe("StatelessForm", () => {

global.URL.createObjectURL.mockRestore();
});


it("handles form field changes with more", async () => {
//Parameters
const formData = { name: "", email: "" };
const validationSchema = {
name: { required: true, regex: new RegExp(/.{2,100}/), regexError: "name-error" },
email: { required: true, regex: new RegExp(/^[\w-.]+@([\w-]+.)+[\w-]{2,4}$/), regexError: "email-error" },
};
const fieldErrors = {};
const setFieldErrors = jest.fn((a) => {});
const setIsThereAnyError = jest.fn((a) => {});
const onStateChanged = jest.fn((a) => {});
const form = (formData, handleChange, fieldErrors) => {
const handle = (e) => {
const value = e.target.value;
handleChange({ target: { name: "name", value: "test", more: [{ name: "email", value: value }] } });
}

return (
<>
<Form.Group>
<Form.Control required type="text" name="name" value={formData.name} onChange={handle} aria-label="name" />
{fieldErrors.name}
</Form.Group>
<Form.Group>
<Form.Control required type="email" name="email" value={formData.email} onChange={handleChange} aria-label="email" />
{fieldErrors.email}
</Form.Group>
</>
);
};

//Render the component
render(<StatelessForm formData={formData} validationSchema={validationSchema} fieldErrors={fieldErrors} setFieldErrors={setFieldErrors} setIsThereAnyError={setIsThereAnyError} onStateChanged={onStateChanged} form={form} />);

//Get the elements
const inputName = screen.getByRole("textbox", { name: "name" });
const inputEmail = screen.getByRole("textbox", { name: "email" });

fireEvent.change(inputName, { target: { value: "test" } });

expect(onStateChanged).toHaveBeenCalledTimes(1);
expect(onStateChanged).toHaveBeenCalledWith({ name: "test", email: "test" });
expect(setFieldErrors).toHaveBeenCalledTimes(1);
expect(setFieldErrors).toHaveBeenCalledWith({ email: "email-error" })
});
});

0 comments on commit 79ae5a1

Please sign in to comment.