All files / app/ui/form BasicFormField.tsx

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130115x 115x 115x 115x   115x                 115x 1809x 1809x 1809x 1809x 1809x 1809x 1809x   3222x                                     1809x               115x 2865x 2865x 2865x 2865x 2865x 2865x 2865x   2865x                           2865x                   1664x 1664x 1664x                   115x 1307x 1307x 1307x 1307x   1307x 88x       115x   2865x 2865x 2865x 2865x   2865x   2855x     10x   10x           115x   115x  
import { getIn, Field, FieldConfig, FieldProps } from 'formik';
import { isDate, noop } from 'lodash';
import React, { createElement, memo, useCallback, useMemo, Component, FunctionComponent } from 'react';
import shallowEqual from 'shallowequal';
 
import FormFieldContainer from './FormFieldContainer';
 
export interface BasicFormFieldProps extends FieldConfig {
    additionalClassName?: string;
    className?: string;
    testId?: string;
    onChange?(value: any): void;
}
 
const BasicFormField: FunctionComponent<BasicFormFieldProps> = ({
    additionalClassName,
    className,
    component,
    render,
    testId,
    onChange,
    ...rest
}) => {
    const renderInnerField = useCallback((props: FieldProps) => (
        <InnerField
            { ...props }
            additionalClassName={ additionalClassName }
            className={ className }
            component={ component }
            onChange={ onChange }
            render={ render }
            testId={ testId }
        />
    ), [
        additionalClassName,
        className,
        component,
        render,
        testId,
        onChange,
    ]);
 
    return <Field
        { ...rest }
        render={ renderInnerField }
    />;
};
 
type InnerFieldProps = Omit<BasicFormFieldProps, keyof FieldConfig> & InnerFieldInputProps;
 
const InnerField: FunctionComponent<InnerFieldProps> = memo(({
    additionalClassName,
    component,
    field,
    form,
    onChange,
    render,
    testId,
}) => {
    const input = useMemo(() => <InnerFieldInput
        component={ component }
        field={ field }
        form={ form }
        onChange={ onChange }
        render={ render }
    />, [
        field,
        form,
        onChange,
        component,
        render,
    ]);
 
    return (
        <FormFieldContainer
            additionalClassName={ additionalClassName }
            hasError={ getIn(form.errors, field.name) }
            testId={ testId }
        >
            { input }
        </FormFieldContainer>
    );
}, (
    { form: prevForm, field: prevField, ...prevProps },
    { form: nextForm, field: nextField, ...nextProps }
) => (
    shallowEqual(prevProps, nextProps) &&
    shallowEqual(prevForm, nextForm) &&
    shallowEqual(prevField, nextField)
));
 
type InnerFieldInputProps = FieldProps & Pick<FieldConfig, 'component' | 'render'> & {
    onChange?(value: string): void;
};
 
class InnerFieldInput extends Component<InnerFieldInputProps> {
    componentDidUpdate({ field: prevField }: InnerFieldInputProps) {
        const { field: { value }, onChange = noop } = this.props;
        const comparableValue = isDate(value) ? value.getTime() : value;
        const comparablePrevValue = isDate(prevField.value) ? prevField.value.getTime() : prevField.value;
 
        if (comparableValue !== comparablePrevValue) {
            onChange(value);
        }
    }
 
    render() {
        const {
            component = 'input',
            field,
            render,
        } = this.props;
 
        if (render) {
            // tslint:disable-next-line:no-unnecessary-type-assertion
            return (render as any)(this.props);
        }
 
        Eif (typeof component === 'string') {
            // tslint:disable-next-line:no-unnecessary-type-assertion
            return createElement(component as any, field);
        }
 
        // tslint:disable-next-line:no-unnecessary-type-assertion
        return createElement(component as any, this.props);
    }
}
 
export default memo(BasicFormField);