UNPKG

cspace-ui

Version:
234 lines (199 loc) 6.38 kB
import React, { isValidElement, PureComponent } from 'react'; import PropTypes from 'prop-types'; import Immutable from 'immutable'; import get from 'lodash/get'; import classNames from 'classnames'; import warning from 'warning'; import { getCsid } from '../../helpers/recordDataHelpers'; import styles from '../../../styles/cspace-ui/RecordForm.css'; function renderTemplate(component, messages, handlers) { const overrideProps = {}; const type = get(component, 'type'); if (type) { // FIXME: Do this without looking at propTypes, so that propTypes may be removed in the // production build. // eslint-disable-next-line react/forbid-foreign-prop-types const { propTypes } = type; if (propTypes) { Object.keys(handlers).forEach((handlerName) => { if (propTypes[handlerName] && !component.props[handlerName]) { overrideProps[handlerName] = handlers[handlerName]; } }); } return React.cloneElement( component, overrideProps, React.Children.map( component.props.children, (child) => renderTemplate(child, messages, handlers), ), ); } return component; } const propTypes = { config: PropTypes.shape({ recordTypes: PropTypes.object, }), recordTypeConfig: PropTypes.shape({ defaultForm: PropTypes.string, fields: PropTypes.object, forms: PropTypes.object, messages: PropTypes.object, }), recordType: PropTypes.string.isRequired, vocabulary: PropTypes.string, csid: PropTypes.string, data: PropTypes.instanceOf(Immutable.Map), formName: PropTypes.string, readOnly: PropTypes.bool, roleNames: PropTypes.instanceOf(Immutable.List), subrecordData: PropTypes.instanceOf(Immutable.Map), onAddInstance: PropTypes.func, onCommit: PropTypes.func, onMoveInstance: PropTypes.func, onRemoveInstance: PropTypes.func, onSortInstances: PropTypes.func, }; const defaultProps = { data: Immutable.Map(), }; const childContextTypes = { config: PropTypes.shape({ recordTypes: PropTypes.object, }), formName: PropTypes.string, recordData: PropTypes.instanceOf(Immutable.Map), recordType: PropTypes.string, recordTypeConfig: PropTypes.PropTypes.shape({ fields: PropTypes.object, }), roleNames: PropTypes.instanceOf(Immutable.List), subrecordData: PropTypes.instanceOf(Immutable.Map), vocabulary: PropTypes.string, csid: PropTypes.string, readOnly: PropTypes.bool, }; export default class RecordForm extends PureComponent { getChildContext() { const { config, csid, data, formName, recordType, recordTypeConfig, roleNames, subrecordData, vocabulary, readOnly, } = this.props; // Get the csid from the data. This may differ from the csid in props, for example if a // urn-style csid was entered in the address bar. We always want to supply the guid-style csid // in the context. const dataCsid = getCsid(data); return { config, formName, readOnly, recordType, recordTypeConfig, roleNames, subrecordData, vocabulary, csid: dataCsid || csid, recordData: data, }; } render() { const { config, csid, data, formName, readOnly, recordType, recordTypeConfig, onAddInstance, onCommit, onMoveInstance, onRemoveInstance, onSortInstances, } = this.props; if (!recordTypeConfig) { return null; } const { fields, forms, messages, } = recordTypeConfig; const handlers = { onAddInstance: (path, position) => { onAddInstance(recordTypeConfig, csid, path, position); }, onCommit: (path, value) => { onCommit(recordTypeConfig, csid, path, value); }, onSortInstances: (path, byField) => { onSortInstances(config, recordTypeConfig, csid, path, byField); }, onMoveInstance: (path, newPosition) => { onMoveInstance(recordTypeConfig, csid, path, newPosition); }, onRemoveInstance: (path) => { onRemoveInstance(recordTypeConfig, csid, path); }, }; let formTemplate; if (formName) { formTemplate = get(forms, [formName, 'template']); } if (!formTemplate) { // Try to get the configured default form. const defaultFormName = recordTypeConfig.defaultForm || 'default'; if (defaultFormName) { formTemplate = get(forms, [defaultFormName, 'template']); } warning(formTemplate, `No form template found for form name ${formName} or default form name ${defaultFormName} in record type ${recordType}. Check the record type plugin configuration.`); } if (typeof formTemplate === 'function') { const result = formTemplate(data, config); if (!result) { return null; } if (typeof result === 'string') { // The form template function returned a string. This will be the name of another form // template to use. formTemplate = get(forms, [result, 'template']); warning(formTemplate, `No form template found for computed form name ${result} for form name ${formName} in record type ${recordType}. Check the record type plugin configuration.`); } else { if (isValidElement(result)) { // The form template function returned a React element to use. formTemplate = result; } warning(formTemplate, `The computed form template for form name ${formName} in record type ${recordType} did not return a string or a React element. Check the record type plugin configuration.`); } } const rootPropertyName = Object.keys(fields)[0]; const formContent = React.cloneElement(formTemplate, { readOnly, name: rootPropertyName, value: data.get(rootPropertyName), children: React.Children.map( formTemplate.props.children, (child) => renderTemplate(child, messages, handlers), ), }); const className = classNames(styles.common, `cspace-ui-RecordForm--${recordType}`); return ( <div className={className}> {formContent} </div> ); } } RecordForm.propTypes = propTypes; RecordForm.defaultProps = defaultProps; RecordForm.childContextTypes = childContextTypes;