public/video-ui/src/components/ManagedForm/ManagedField.jsx (147 lines of code) (raw):

import React from 'react'; import { PropTypes } from 'prop-types'; import _get from 'lodash/fp/get'; import _set from 'lodash/fp/set'; import validateField from '../../util/validateField'; import {getTextFromHtml} from '../../util/getTextFromHtml'; import FieldNotification from '../../constants/FieldNotification'; import {fieldsWithHtml} from '../../constants/fieldsWithHtml'; import RequiredForComposer from '../../constants/requiredForComposer'; export class ManagedField extends React.Component { static propTypes = { fieldLocation: PropTypes.string.isRequired, children: PropTypes.oneOfType([ PropTypes.node, PropTypes.arrayOf(PropTypes.node) ]), updateData: PropTypes.func, updateFormErrors: PropTypes.func, updateWarnings: PropTypes.func, customValidation: PropTypes.func, data: PropTypes.object, fieldName: PropTypes.string, isRequired: PropTypes.bool, isDesired: PropTypes.bool, disabled: PropTypes.bool, editable: PropTypes.bool, maxLength: PropTypes.number, fieldDetails: PropTypes.string, tagType: PropTypes.string, inputPlaceholder: PropTypes.string, tooltip: PropTypes.string, updateSideEffects: PropTypes.func }; state = { fieldNotification: null, touched: false }; componentDidMount() { const value = this.props.data ? this.props.data[this.props.fieldLocation] : null; this.checkErrorsAndWarnings(value); this.placeholder = 'No ' + this.props.name.split('(')[0]; } checkErrorsAndWarnings(value) { if (value && fieldsWithHtml.includes(this.props.fieldLocation)) { value = getTextFromHtml(value); } const composerValidation = RequiredForComposer.fields.includes(this.props.fieldLocation); const notification = validateField( value, this.props.isRequired, this.props.isDesired, this.props.customValidation, composerValidation, this.props.maxLength, ); if (this.props.updateFormErrors) { if (notification && notification.type === FieldNotification.error) { this.props.updateFormErrors(notification, this.props.fieldLocation); } else { this.props.updateFormErrors(null, this.props.fieldLocation); } } if (this.props.updateWarnings) { if (notification && notification.type === FieldNotification.warning) { this.props.updateWarnings(true, this.props.fieldLocation); } else { this.props.updateWarnings(false, this.props.fieldLocation); } } this.setState({ fieldNotification: notification }); } updateFn = newValue => { this.setState({ touched: true }); this.checkErrorsAndWarnings(newValue); return this.props.updateData( _set(this.props.fieldLocation, newValue === '' ? null : newValue, this.props.data) ).then(() => { if (this.props.updateSideEffects) { return this.props.updateSideEffects(this.props.data); } }); }; getFieldValue(value) { if (!this.props.editable && !value) { return this.placeholder; } if (!value) { return ''; } return value; } hasError(props) { return ( props.notification && props.notification.type === FieldNotification.error ); } hasWarning(props) { return ( props.notification && props.notification.type === FieldNotification.warning ); } displayPlaceholder(placeholder, fieldValue) { return placeholder && placeholder === fieldValue; } render() { let editable = this.props.editable; let className = 'form-element'; if (this.props.disabled) { editable = false; className += ' form-element--hidden'; } const hydratedChildren = React.Children.map(this.props.children, child => { return React.cloneElement(child, { fieldName: this.props.name, fieldValue: this.getFieldValue( _get(this.props.fieldLocation, this.props.data) ), rawFieldValue: _get(this.props.fieldLocation, this.props.data), onUpdateField: this.updateFn, editable, maxLength: this.props.maxLength, notification: this.state.fieldNotification, placeholder: this.placeholder, touched: this.state.touched, fieldDetails: this.props.fieldDetails, hasError: this.hasError, hasWarning: this.hasWarning, displayPlaceholder: this.displayPlaceholder, derivedFrom: this.props.derivedFrom, maxWordLength: this.props.maxWordLength, tagType: this.props.tagType, inputPlaceholder: this.props.inputPlaceholder, tooltip: this.props.tooltip, fieldLocation: this.props.fieldLocation, updateSideEffects: this.props.updateSideEffects }); }); return <div className={className}>{hydratedChildren}</div>; } }