import { useEffect } from 'react'; import { useFormikContext } from 'formik'; import smoothscroll from 'smoothscroll-polyfill'; import { ServerErrorContextProps } from './ServerErrorContext'; export interface FocusErrorProps { /** Validation errors that have been received from the server. */ serverErrors: ServerErrorContextProps; } /** Function to scroll to the field that has an error. */ export default function FocusError(props: FocusErrorProps) { const { errors, isSubmitting, isValidating } = useFormikContext(); smoothscroll.polyfill(); useEffect(() => { if (!isSubmitting || isValidating) { return; } //This block handles any front-end input validation errors //e.g. required fields, max characters, etc const keys = Object.keys(errors); if (keys.length > 0) { return scrollToErrorElement(keys); } //This block handles any input-specific server-side errors //e.g. improper email formatting, invalid phone number, etc. if ( props.serverErrors.errors && Object.values(props.serverErrors.errors).some((x) => !!x) ) { const names = Object.keys(props.serverErrors.errors); return scrollToErrorElement(names); } }, [errors, isSubmitting, isValidating, props]); return null; } const scrollToErrorElement = (keys: string[]) => { let firstErrorElement: HTMLElement | null = document.getElementById( `${keys[0].toLowerCase()}-error-scroll-target` ); if (!firstErrorElement || !firstErrorElement.parentNode) { return; } firstErrorElement = firstErrorElement.parentNode as HTMLElement; const headerOffset = -110; const y = firstErrorElement.getBoundingClientRect().top + window.pageYOffset + headerOffset; window.scrollTo({ top: y, behavior: 'smooth' }); setTimeout(() => { firstErrorElement?.focus(); }, 500); };