import { useCallback, useRef } from 'react'; import PropTypes from 'prop-types'; import { BREAKPOINTS } from '../../constants'; import { BUTTON_VARIANT_CLASSES as variantClasses } from './FormTheme'; import { clsm, noop } from '../../utils'; import { useResponsiveDevice } from '../../contexts/ResponsiveDevice'; import Button from '../../components/Button'; import Input from '../../components/Input'; import useForm from './useForm'; import useThrottledCallback from '../../hooks/useThrottledCallback'; const Form = ({ 'data-testid': dataTestId, className, clearFormOnSuccess, disableSubmit, disableValidation, errorHandler, footer, formVariant, inputsData, inputVariant, onFailure, onSuccess, submitBtnVariant, submitHandler, submitText, title, validationCheck }) => { const [formProps, isLoading, onChange, onSubmit, presubmitValidation] = useForm({ disableValidation, errorHandler, inputsData, onFailure, onSuccess, submitHandler, validationCheck }); const isFormComplete = Object.values(formProps).every(({ value }) => value); const { currentBreakpoint } = useResponsiveDevice(); const isMobileView = currentBreakpoint < BREAKPOINTS.sm; const classes = clsm(variantClasses[formVariant], className); const SubmitButton = useCallback( () => (
{!!footer &&
{footer}
}
), [ disableSubmit, footer, formProps, formVariant, inputVariant, isFormComplete, isLoading, submitBtnVariant, submitText, dataTestId ] ); const throttledPresubmitValidation = useThrottledCallback((name) => { presubmitValidation(name); initialFocusedInputValue.current = ''; }, 1000); const initialFocusedInputValue = useRef(''); const onFocus = ({ target: { value } }) => { initialFocusedInputValue.current = value; }; const onBlur = ({ relatedTarget, target: { name, value } }) => { if (relatedTarget?.type === 'submit') return; if (value && initialFocusedInputValue.current !== value) { // If onBlur causes a re-render, then onSubmit will not be called. // So we wrap the presubmit validation inside a setTimeout to give onSubmit a chance to be called. setTimeout(() => throttledPresubmitValidation(name)); } }; return (
onSubmit(e, clearFormOnSuccess)} > {title &&

{title}

} {Object.values(formProps).map((inputProps, i, arr) => { if (formVariant === 'vertical') { return ( ); } const hasSubmitButton = i === arr.length - 1 && formVariant === 'horizontal'; return (
{hasSubmitButton && }
); })} {formVariant === 'vertical' && } ); }; Form.defaultProps = { 'data-testid': undefined, className: '', clearFormOnSuccess: true, disableSubmit: noop, disableValidation: false, errorHandler: noop, footer: null, formVariant: 'vertical', inputsData: {}, inputVariant: 'vertical', onFailure: noop, onSuccess: noop, submitBtnVariant: 'primary', submitText: 'Submit', title: '', validationCheck: noop }; Form.propTypes = { 'data-testid': PropTypes.string, className: PropTypes.string, clearFormOnSuccess: PropTypes.bool, disableSubmit: PropTypes.func, disableValidation: PropTypes.bool, errorHandler: PropTypes.func, footer: PropTypes.node, formVariant: PropTypes.oneOf(['vertical', 'horizontal']), inputsData: PropTypes.object, inputVariant: PropTypes.oneOf(['vertical', 'horizontal']), onFailure: PropTypes.func, onSuccess: PropTypes.func, submitBtnVariant: PropTypes.oneOf([ 'primary', 'tertiary', 'secondary', 'destructive', 'link' ]), submitHandler: PropTypes.func.isRequired, submitText: PropTypes.string.isRequired, title: PropTypes.string, validationCheck: PropTypes.func }; export default Form;