Skip to content

Commit

Permalink
globalized-errors
Browse files Browse the repository at this point in the history
  • Loading branch information
ankman007 committed Sep 20, 2024
1 parent eb58c9c commit 9e33533
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 71 deletions.
14 changes: 11 additions & 3 deletions src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface CustomCheckboxProps {
label: string;
value: boolean;
onChange: (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => void;
onError?: (name: string, error: string | null) => void;
required?: boolean;
}

Expand All @@ -14,27 +15,34 @@ export const CheckboxComponent: React.FC<CustomCheckboxProps> = ({
label,
value,
onChange,
onError,
required = false,
}) => {
const [error, setError] = useState<string | null>(null);
const [active, setActive] = useState(false);

useEffect(() => {
if (active && required && !value) {
setError(`${label} is required.`);
const errorMessage = `${label} is required.`;
setError(errorMessage);
onError?.(name, errorMessage);
} else {
setError(null);
onError?.(name, null);
}
}, [required, value, label, active]);
}, [required, value, label, active, name, onError]);

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const checked = event.target.checked;
onChange(event, checked);

if (required && !checked) {
setError(`${label} is required.`);
const errorMessage = `${label} is required.`;
setError(errorMessage);
onError?.(name, errorMessage);
} else {
setError(null);
onError?.(name, null);
setActive(true);
}
};
Expand Down
20 changes: 12 additions & 8 deletions src/components/DateField/DateFieldComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,28 @@ export const DateFieldComponent: React.FC<DateFieldComponentProps> = ({
name,
value,
onChange,
localError = null,
onError,
helperText,
required = false,
}) => {
const [error, setError] = useState<string | null>(localError);
const [active, setActive] = useState(false);
const [error, setError] = useState<string | null>(null);
const [touched, setTouched] = useState(false);

useEffect(() => {
if (active && required && !value) {
setError(`${label} is required.`);
if (touched && required && !value) {
const errorMessage = `${label} is required.`;
setError(errorMessage);
onError?.(name, errorMessage);
} else {
setError(null);
onError?.(name, null);
}
}, [value, required, label, active]);
}, [value, required, label, touched, onError, name]);

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event);
setActive(true);
const newValue = event.target.value;
onChange(newValue);
setTouched(true);
};

return (
Expand Down
22 changes: 16 additions & 6 deletions src/components/FileUpload/FileUploadComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,28 @@ import React, { useState } from "react";
import { Button, Box, Typography, FormHelperText } from "@mui/material";
import { FileUploadComponentProps } from "../../types/types";

export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({ label, name, onChange }) => {
export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
label,
name,
onChange,
onError,
}) => {
const [error, setError] = useState<string | null>(null);

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];

if (file) {
if (file.size > 10000000) {
setError("File size exceeds 1MB.");
if (file.size > 100000000) {
const errorMessage = "File size is too big.";
setError(errorMessage);
onError?.(name, errorMessage);
return;
}
setError(null);
onChange(event);

setError(null);
onError?.(name, null);
onChange(file);
}
};

Expand All @@ -33,7 +43,7 @@ export const FileUploadComponent: React.FC<FileUploadComponentProps> = ({ label,
hidden
/>
</Button>
{error && <FormHelperText error>{error}</FormHelperText>}
{error && <FormHelperText error>{error}</FormHelperText>}
</Box>
);
};
37 changes: 27 additions & 10 deletions src/components/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import InputField from "./InputField";
import data from "../data/fields.json";
import { FieldProps } from "../types/types";
import { FieldProps, FormData, FormErrors } from "../types/types";
import { Button, Box, Typography } from "@mui/material";

const fields: FieldProps[] = (data as { fields: FieldProps[] }).fields;

type FormData = {
[key: string]: string | boolean | string[] | undefined;
};

const Form: React.FC = () => {
const [formData, setFormData] = useState<FormData>({});
const [errors, setErrors] = useState<FormErrors>({});
const [submitted, setSubmitted] = useState(false);
const [isFormValid, setIsFormValid] = useState(false);

const handleChange = (name: string, event: any) => {
const { value } = event.target;

useEffect(() => {
const hasErrors = Object.values(errors).some((error) => error !== null);
const isFormComplete = fields.every((field) => formData[field.name] !== undefined && formData[field.name] !== '');
setIsFormValid(!hasErrors && isFormComplete);
}, [errors, formData]);

const handleChange = (name: string, value: string | boolean) => {
setFormData((prevData) => ({
...prevData,
[name]: value,
}));

setErrors((prevErrors) => ({
...prevErrors,
[name]: null,
}));
};

const handleError = (name: string, error: string) => {
setErrors((prevErrors) => ({
...prevErrors,
[name]: error,
}));
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log("Form Data:", formData);
console.log("Form Data:", formData);
setSubmitted(true);
};

Expand All @@ -42,6 +56,8 @@ const Form: React.FC = () => {
options={field.options}
value={formData[field.name] ?? (field.type === 'checkbox' ? false : '')}
onChange={(value) => handleChange(field.name, value)}
onError={(name, error) => handleError(name, error)}
error={errors[field.name] ?? ''}
/>
</Box>
))}
Expand All @@ -55,6 +71,7 @@ const Form: React.FC = () => {
backgroundColor: "#129490",
},
}}
disabled={!isFormValid}
>
Submit
</Button>
Expand Down
56 changes: 39 additions & 17 deletions src/components/MultiSelect/MultiSelectComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import {
Select,
MenuItem,
Expand All @@ -11,37 +11,61 @@ import {
Chip,
SelectChangeEvent,
} from "@mui/material";
import { SelectComponentProps } from "../../types/types";
import { MultiSelectComponentProps } from "../../types/types";

const MultiSelectComponent: React.FC<SelectComponentProps> = ({
const MultiSelectComponent: React.FC<MultiSelectComponentProps> = ({
label,
name,
value,
onChange,
options,
localError,
required = false,
onError,
}) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const selectedValues = Array.isArray(value) ? value : [];

useEffect(() => {
if (required && selectedValues.length === 0) {
const error = `${label} is required.`;
setError(error);
if (onError) onError(name, error);
} else {
setError(null);
if (onError) onError(name, null);
}
}, [selectedValues, required, label, name, onError]);

const [error, setError] = React.useState<string | null>('');

const handleChange = (event: SelectChangeEvent<string[]>) => {
console.log("event.target.value", event.target.value);
// console.log('event.target', event.target)
// console.log('event', event)
onChange(event.target.value);
const newValue = event.target.value;
onChange(newValue);

if (required && newValue.length === 0) {
const errorMessage = `${label} is required.`;
setError(errorMessage);
if (onError) onError(name, errorMessage);
} else {
setError(null);
if (onError) onError(name, null);
}
};

return (
<FormControl fullWidth variant="outlined" error={!!localError}>
<FormControl fullWidth variant="outlined" error={!!error}>
<InputLabel>{label}</InputLabel>
<Select
multiple
name={name}
value={Array.isArray(value) ? value : []}
value={selectedValues}
onChange={handleChange}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{(selected as string[]).map((value) => (
{(selected as string[]).map((selectedValue) => (
<Chip
key={value}
label={options.find((option) => option.value === value)?.label}
key={selectedValue}
label={options.find((option) => option.value === selectedValue)?.label}
/>
))}
</Box>
Expand All @@ -50,14 +74,12 @@ const MultiSelectComponent: React.FC<SelectComponentProps> = ({
>
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
<Checkbox
checked={(value as unknown as string[]).includes(option.value)}
/>
<Checkbox checked={selectedValues.includes(option.value)} />
<ListItemText primary={option.label} />
</MenuItem>
))}
</Select>
{localError && <FormHelperText>{localError}</FormHelperText>}
{error && <FormHelperText>{error}</FormHelperText>}
</FormControl>
);
};
Expand Down
19 changes: 12 additions & 7 deletions src/components/Radio/RadioComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,32 @@ export const RadioComponent: React.FC<RadioComponentProps> = ({
value,
onChange,
required = false,
onError,
}) => {
const [error, setError] = useState<string | null>(null);
const [active, setActive] = useState(false);
const [touched, setTouched] = useState(false);

useEffect(() => {
if (active && required && !value) {
setError(`Please select an option for ${label}`);
if (touched && required && !value) {
const errorMessage = `Please select an option for ${label}`;
setError(errorMessage);
onError?.(name, errorMessage);
} else {
setError(null);
onError?.(name, null);
}
}, [value, required, label, active]);
}, [value, required, label, touched, onError, name]);

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event);
setActive(true);
const selectedValue = event.target.value;
onChange(selectedValue);
setTouched(true);
};

return (
<FormControl error={!!error}>
<label>{label}</label>
<RadioGroup name={name} value={value} onChange={handleChange}>
<RadioGroup name={name} value={value || ""} onChange={handleChange}>
{options.map((option) => (
<FormControlLabel
key={option.value}
Expand Down
Loading

0 comments on commit 9e33533

Please sign in to comment.