diff --git a/app/scripts/modules/core/src/presentation/forms/FormField.tsx b/app/scripts/modules/core/src/presentation/forms/FormField.tsx index 515b75caf87..3091c2f1529 100644 --- a/app/scripts/modules/core/src/presentation/forms/FormField.tsx +++ b/app/scripts/modules/core/src/presentation/forms/FormField.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { Subject } from 'rxjs'; +import { isString } from 'lodash'; import { noop } from 'core/utils'; @@ -13,6 +14,7 @@ import { IControlledInputProps, IFieldLayoutPropsWithoutInput, IFieldValidationStatus, + IFormFieldApi, IValidationProps, } from './interface'; @@ -32,7 +34,9 @@ interface IFormFieldState { internalValidators: Validator[]; } -export class FormField extends React.Component { +const ifString = (val: any): string => (isString(val) ? val : undefined); + +export class FormField extends React.Component implements IFormFieldApi { public static defaultProps: Partial = { layout: StandardFieldLayout, validate: noop, @@ -50,6 +54,18 @@ export class FormField extends React.Component private destroy$ = new Subject(); private value$ = new Subject(); + public name = () => this.props.name; + + public label = () => ifString(this.props.label); + + public value = () => this.props.value; + + public touched = () => this.props.touched; + + public validationMessage = () => ifString(this.props.validationMessage) || ifString(this.state.validationMessage); + + public validationStatus = () => this.props.validationStatus || this.state.validationStatus; + private addValidator = (internalValidator: Validator) => { this.setState(prevState => ({ internalValidators: prevState.internalValidators.concat(internalValidator), @@ -85,18 +101,15 @@ export class FormField extends React.Component public render() { const { input, layout } = this.props; // ICommonFormFieldProps const { label, help, required, actions } = this.props; // IFieldLayoutPropsWithoutInput - const { touched, validationMessage: message, validationStatus: status } = this.props; // IValidationProps const { onChange, onBlur, value, name } = this.props; // IControlledInputProps const fieldLayoutPropsWithoutInput: IFieldLayoutPropsWithoutInput = { label, help, required, actions }; const controlledInputProps: IControlledInputProps = { onChange, onBlur, value, name }; - const validationMessage = message || this.state.validationMessage; - const validationStatus = status || this.state.validationStatus; const validationProps: IValidationProps = { - touched, - validationMessage, - validationStatus, + touched: this.touched(), + validationMessage: this.validationMessage(), + validationStatus: this.validationStatus(), addValidator: this.addValidator, removeValidator: this.removeValidator, }; diff --git a/app/scripts/modules/core/src/presentation/forms/FormikFormField.tsx b/app/scripts/modules/core/src/presentation/forms/FormikFormField.tsx index ddceb839f23..e9964e818d5 100644 --- a/app/scripts/modules/core/src/presentation/forms/FormikFormField.tsx +++ b/app/scripts/modules/core/src/presentation/forms/FormikFormField.tsx @@ -1,9 +1,15 @@ import * as React from 'react'; import { isString, isUndefined } from 'lodash'; -import { Field, FastField, FieldProps, getIn } from 'formik'; - +import { Field, FastField, FieldProps, getIn, connect, FormikContext } from 'formik'; + +import { + ICommonFormFieldProps, + IFieldLayoutPropsWithoutInput, + IFieldValidationStatus, + IFormFieldApi, + IValidationProps, +} from './interface'; import { WatchValue } from '../WatchValue'; -import { ICommonFormFieldProps, IFieldLayoutPropsWithoutInput, IValidationProps } from './interface'; import { StandardFieldLayout } from './layouts/index'; import { composeValidators, Validator, Validation } from './Validation'; import { renderContent } from './fields/renderContent'; @@ -28,19 +34,24 @@ export interface IFormikFieldProps { onChange?: (value: T, prevValue: T) => void; } -export interface IFormikFormFieldState { +export interface IFormikFormFieldImplState { internalValidators: Validator[]; } export type IFormikFormFieldProps = IFormikFieldProps & ICommonFormFieldProps & IFieldLayoutPropsWithoutInput; +type IFormikFormFieldImplProps = IFormikFormFieldProps & { formik: FormikContext }; + +const ifString = (val: any): string => (isString(val) ? val : undefined); -export class FormikFormField extends React.Component, IFormikFormFieldState> { +export class FormikFormFieldImpl + extends React.Component, IFormikFormFieldImplState> + implements IFormFieldApi { public static defaultProps: Partial> = { layout: StandardFieldLayout, fastField: true, }; - public state: IFormikFormFieldState = { + public state: IFormikFormFieldImplState = { internalValidators: [], }; @@ -56,27 +67,41 @@ export class FormikFormField extends React.Component this.props.name; + + public label = () => ifString(this.props.label); + + public value = () => getIn(this.props.formik.values, this.props.name); + + public touched = () => { + const { formik, name, touched } = this.props; + return !isUndefined(touched) ? touched : getIn(formik.values, name); + }; + + public validationMessage = () => { + const { name, formik, validationMessage } = this.props; + return ifString(validationMessage) || getIn(formik.errors, name); + }; + + public validationStatus = () => { + return (this.props.validationStatus || (this.validationMessage() ? 'error' : null)) as IFieldValidationStatus; + }; + public render() { const { internalValidators } = this.state; const { name, validate, onChange } = this.props; // IFormikFieldProps const { input, layout } = this.props; // ICommonFieldProps const { label, help, required, actions } = this.props; // IFieldLayoutPropsWithoutInput - const { touched, validationMessage, validationStatus } = this.props; // IValidationProps const fieldLayoutPropsWithoutInput: IFieldLayoutPropsWithoutInput = { label, help, required, actions }; const render = (props: FieldProps) => { - const { field, form } = props; - - const formikError = getIn(form.errors, name); - const message = !isUndefined(validationMessage) ? validationMessage : formikError; - const status = !isUndefined(validationStatus) ? validationStatus : formikError ? 'error' : null; - const isTouched = !isUndefined(touched) ? touched : getIn(form.touched, name); + const { field } = props; const validationProps: IValidationProps = { - validationMessage: message, - validationStatus: status, - touched: isTouched, + touched: this.touched(), + validationMessage: this.validationMessage(), + validationStatus: this.validationStatus(), addValidator: this.addValidator, removeValidator: this.removeValidator, }; @@ -115,3 +140,5 @@ export function createFieldValidator( const labelString = isString(label) ? label : undefined; return (value: any) => validator(value, labelString); } + +export const FormikFormField = connect(FormikFormFieldImpl); diff --git a/app/scripts/modules/core/src/presentation/forms/interface.ts b/app/scripts/modules/core/src/presentation/forms/interface.ts index 3f194ef06de..d6f555185d1 100644 --- a/app/scripts/modules/core/src/presentation/forms/interface.ts +++ b/app/scripts/modules/core/src/presentation/forms/interface.ts @@ -46,3 +46,12 @@ export interface ICommonFormFieldProps { input: React.ComponentType; layout?: React.ComponentType; } + +export interface IFormFieldApi { + name(): string; + label(): string; + value(): any; + touched(): boolean; + validationMessage(): string; + validationStatus(): IFieldValidationStatus; +}