UNPKG

cspace-ui

Version:
187 lines (147 loc) 5.21 kB
import React from 'react'; import PropTypes from 'prop-types'; import { intlShape, FormattedMessage } from 'react-intl'; import Immutable from 'immutable'; import get from 'lodash/get'; import warning from 'warning'; import { components as inputComponents, helpers as inputHelpers, } from 'cspace-input'; import { configKey, dataPathToFieldDescriptorPath, isFieldRequired, } from '../../helpers/configHelpers'; const { getPath, pathPropType, } = inputHelpers.pathHelpers; const { Label } = inputComponents; const defaultViewConfigKey = 'view'; const renderLabel = (fieldDescriptor, providedLabelMessage, recordData, props) => { const fieldConfig = fieldDescriptor[configKey]; const message = providedLabelMessage || get(fieldConfig, ['messages', 'name']); if (!message) { return null; } const configuredProps = {}; if ('required' in fieldConfig) { configuredProps.required = isFieldRequired(fieldDescriptor, recordData); } if ('readOnly' in fieldConfig) { configuredProps.readOnly = fieldConfig.readOnly; } return ( <Label {...props} {...configuredProps}> <FormattedMessage {...message} /> </Label> ); }; const propTypes = { labelMessage: PropTypes.object, viewType: PropTypes.string, // Code in this component doesn't use these props, but the propTypes need to exist, because // users of this component may check for them to determine if those props should be passed. // We want to receive all the props that our base components may need, and then we'll handle // distributing them to the base components that accept them. /* eslint-disable react/no-unused-prop-types */ name: PropTypes.string, // The value prop will be validated by the base component, so allow anything here. value: PropTypes.any, parentPath: PropTypes.array, subpath: pathPropType, tabular: PropTypes.bool, label: PropTypes.node, readOnly: PropTypes.bool, onAddInstance: PropTypes.func, onCommit: PropTypes.func, onMoveInstance: PropTypes.func, onRemoveInstance: PropTypes.func, onSortInstances: PropTypes.func, /* eslint-enable react/no-unused-prop-types */ }; const contextTypes = { config: PropTypes.object, intl: intlShape, recordData: PropTypes.instanceOf(Immutable.Map), recordType: PropTypes.string, recordTypeConfig: PropTypes.object, }; export default function Field(props, context) { const { config, intl, recordData, recordType, } = context; const { labelMessage, viewType, } = props; const recordTypeConfig = context.recordTypeConfig || get(config, ['recordTypes', recordType]); const fullPath = getPath(props); // Filter out numeric parts of the path, since they indicate repeating instances that won't be // present in the field descriptor. const path = dataPathToFieldDescriptorPath(fullPath); const fields = get(recordTypeConfig, 'fields'); warning(fields, `No field descriptor found for the record type ${recordType}. The field with path ${path} will not be rendered.`); if (!fields) { return null; } const field = get(fields, path); warning(field, `The path ${path} is not present in the field descriptors for the record type ${recordType}. The field will not be rendered.`); if (!field) { return null; } const fieldConfig = field[configKey]; const viewConfigKey = (viewType === 'search') ? 'searchView' : defaultViewConfigKey; const viewConfig = fieldConfig[viewConfigKey] || fieldConfig[defaultViewConfigKey]; const BaseComponent = viewConfig.type; const configuredProps = viewConfig.props || {}; const providedProps = {}; const basePropTypes = BaseComponent.propTypes; Object.keys(props).forEach((propName) => { if (propName in basePropTypes) { providedProps[propName] = props[propName]; } }); const effectiveReadOnly = providedProps.readOnly || configuredProps.readOnly; const computedProps = {}; if (fieldConfig.repeating && viewType !== 'search') { computedProps.repeating = fieldConfig.repeating; } if ('label' in basePropTypes) { computedProps.label = renderLabel(field, labelMessage, recordData, { readOnly: effectiveReadOnly, }); } if ('formatValue' in basePropTypes) { const valueMessage = get(fieldConfig, ['messages', 'value']); if (valueMessage) { computedProps.formatValue = value => intl.formatMessage(valueMessage, { value }); } } if ('renderChildInputLabel' in basePropTypes) { computedProps.renderChildInputLabel = (childInput) => { const childName = childInput.props.name; const childLabelMessage = childInput.props.labelMessage; const childField = field[childName]; return (childField && renderLabel(childField, childLabelMessage, recordData, { key: childName, readOnly: effectiveReadOnly, })); }; } if ('viewType' in basePropTypes) { computedProps.viewType = viewType; } const effectiveProps = Object.assign({}, computedProps, configuredProps, providedProps, { readOnly: effectiveReadOnly, }); return ( <BaseComponent {...effectiveProps} /> ); } Field.contextTypes = contextTypes; Field.propTypes = propTypes;