Skip to content

Commit

Permalink
Refactor: Enhance 'ContactForm' accessibility, Fix form focus and more
Browse files Browse the repository at this point in the history
- The new 'showSubmitButton' local state will be used to conditionally
render the submit button.
- Added label elements to all inputs.
- With the addition of label elements, now in 'setInputStyle' we need
to access the second 'nextSibling' element to access the next input after
that input's label.
- Removed 'setSubmitButtonStyle' function since now the styles will be
applied conditionally in the component class name.
- Without the 'setSubmitButtonStyle' function, now at the 'validateInput'
function we now will style and show the button by toggling the
'showSubmitButton' state based on the validity of all inputs.
  • Loading branch information
ITurres committed Mar 11, 2024
1 parent 7313bfd commit dffe163
Showing 1 changed file with 70 additions and 46 deletions.
116 changes: 70 additions & 46 deletions src/components/UI/ContactForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ const ContactForm: React.FC = () => {
// * sessionStorage.getItem('formContent')! is not null.
);

// * This state is to disable the submit button after the form is submitted.
const [formSubmittedStatus, setFormSubmittedStatus] = useState(false);
const [showSubmitButton, setShowSubmitButton] = useState(false);

const actionURL = process.env.REACT_APP_FORM_ACTION_URL;

Expand Down Expand Up @@ -54,8 +56,14 @@ const ContactForm: React.FC = () => {
) => {
if (isValid) {
inputDomElement.classList.remove('invalid');
if (inputDomElement.nextSibling instanceof HTMLElement) {
inputDomElement.nextSibling.classList.add('show');

// * If the input is valid, remove invalid class, add show class to next
// * input after label, and add valid class.
// ! Note: This method of accessing the next input after the label may not
// ! be the most robust.
const inputElementAfterLabelElement = inputDomElement.nextSibling?.nextSibling;
if (inputElementAfterLabelElement instanceof HTMLElement) {
inputElementAfterLabelElement.classList.add('show');
}
inputDomElement.classList.add('valid');
} else {
Expand All @@ -64,45 +72,46 @@ const ContactForm: React.FC = () => {
}
};

const setSubmitButtonStyle = (isValid: boolean) => {
if (isValid) {
$submitButton.current?.classList.remove('invalid');
$submitButton.current?.classList.add('valid');
} else {
$submitButton.current?.classList.remove('valid');
$submitButton.current?.classList.add('invalid');
}
};

const validateInput = useCallback((input: any, regex: RegExp) => {
const inputKey = input.obj.current?.name as keyof typeof inputs;
const isValid = regex.test(input.obj.current?.value || '');
const validateInput = useCallback(
(input: any, regex: RegExp) => {
const inputKey = input.obj.current?.name as keyof typeof inputs;
const isValid = regex.test(input.obj.current?.value || '');

inputs[inputKey].validated = isValid;
setInputStyle(input.obj.current, isValid);
inputs[inputKey].validated = isValid;
setInputStyle(input.obj.current, isValid);

// * set the submit button style based on the validity of all inputs.
setSubmitButtonStyle(
Object.values(inputs).every((inputObj) => inputObj.validated),
);
}, [inputs]);
// * set the submit button style based on the validity of all inputs.
const areAllValidInputs = Object.values(inputs).every(
(inputObj) => inputObj.validated,
);
if (areAllValidInputs) {
setShowSubmitButton(true);
} else {
setShowSubmitButton(false);
}
},
[inputs],
);

useEffect(() => {
if (formContent) {
sessionStorage.setItem('formContent', JSON.stringify(formContent));
}
}, [formContent]);

const updateFormContent = useCallback((input: any) => () => {
const inputKey = input.obj.current?.name as keyof typeof inputs;
const updateFormContent = useCallback(
(input: any) => () => {
const inputKey = input.obj.current?.name as keyof typeof inputs;

setFormContent((previousFormContent: any) => {
const currentFormContent = { ...previousFormContent };
currentFormContent[inputKey] = input.obj.current?.value;
setFormContent((previousFormContent: any) => {
const currentFormContent = { ...previousFormContent };
currentFormContent[inputKey] = input.obj.current?.value;

return currentFormContent;
});
}, []);
return currentFormContent;
});
},
[],
);

useEffect(() => {
Object.values(inputs).forEach((input) => {
Expand Down Expand Up @@ -236,7 +245,10 @@ const ContactForm: React.FC = () => {
ref={$form}
onSubmit={handleSubmission}
>
{/* eslint-disable jsx-a11y/label-has-associated-control */}
<label htmlFor="name">Name:</label>
<input
id="name"
type="text"
ref={$nameInputRef}
name="name"
Expand All @@ -245,7 +257,9 @@ const ContactForm: React.FC = () => {
required
/>

<label htmlFor="company">Company name: </label>
<input
id="company"
type="text"
ref={$companyInputRef}
name="company"
Expand All @@ -254,15 +268,19 @@ const ContactForm: React.FC = () => {
required
/>

<label htmlFor="email">Email: </label>
<input
id="email"
type="email"
ref={$emailInputRef}
name="email"
placeholder="an email, e.g [email protected]"
required
/>

<label htmlFor="message">Message: </label>
<textarea
id="message"
ref={$messageInputRef}
name="message"
placeholder="your message, at least 20 characters long"
Expand All @@ -271,22 +289,28 @@ const ContactForm: React.FC = () => {
required
/>

<button
type="submit"
disabled={formSubmittedStatus}
ref={$submitButton}
className="my-btn"
>
{formSubmittedStatus ? 'Thank you!' : 'Send'}
{formSubmittedStatus && (
<img
src={successButtonIcon}
alt="alien waving hand"
aria-hidden="true"
className="send-email-icon"
/>
)}
</button>
{showSubmitButton && (
<button
type="submit"
disabled={formSubmittedStatus}
ref={$submitButton}
className={`my-btn ${showSubmitButton ? 'valid show' : 'invalid'}`}
aria-label={
formSubmittedStatus ? 'Thank you! email sent' : 'Send email'
}
>
{formSubmittedStatus ? 'Thank you!' : 'Send'}

{formSubmittedStatus && (
<img
src={successButtonIcon}
alt="alien waving hand"
aria-hidden="true"
className="send-email-icon"
/>
)}
</button>
)}
</form>
);
};
Expand Down

0 comments on commit dffe163

Please sign in to comment.