terra-form-field
Version:
The Form Field component handles the layout of the label, help text and error text associated with a form element.
218 lines (214 loc) • 10.9 kB
JavaScript
;
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _reactIntl = require("react-intl");
var _classnames = _interopRequireDefault(require("classnames"));
var _bind = _interopRequireDefault(require("classnames/bind"));
var _terraThemeContext = _interopRequireDefault(require("terra-theme-context"));
var _IconError = _interopRequireDefault(require("terra-icon/lib/icon/IconError"));
var _FieldModule = _interopRequireDefault(require("./Field.module.scss"));
var _excluded = ["children", "error", "errorIcon", "help", "hideRequired", "htmlFor", "isInvalid", "isInline", "isLabelHidden", "label", "labelAttrs", "maxWidth", "required", "showOptional", "style"];
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } } return target; }
var cx = _bind.default.bind(_FieldModule.default);
var propTypes = {
/**
* The form control elements the Field contains.
*/
children: _propTypes.default.node,
/**
* Error message for when the input is invalid. This will only be displayed if isInvalid is true.
*/
error: _propTypes.default.node,
/**
* Error Icon to display when the input is invalid.
*/
errorIcon: _propTypes.default.element,
/**
* Help element to display with the input.
*/
help: _propTypes.default.node,
/**
* @private
* Whether or not to hide the required indicator on the label.
*/
hideRequired: _propTypes.default.bool,
/**
* The htmlFor attribute on the field label.
*/
htmlFor: _propTypes.default.string,
/**
* Whether the field displays as Invalid. Use when value does not meet validation pattern.
*/
isInvalid: _propTypes.default.bool,
/**
* Whether or not the field is an inline field.
*/
isInline: _propTypes.default.bool,
/**
* @private
* Whether or not the label is visible. Use this props to hide a label while still creating it on the DOM for accessibility.
*/
isLabelHidden: _propTypes.default.bool,
/**
* The label of the form control children.
*/
label: _propTypes.default.string.isRequired,
/**
* Attributes to attach to the label.
*/
// eslint-disable-next-line react/forbid-prop-types
labelAttrs: _propTypes.default.object,
/**
* Set the max-width of a field using `length` or `%`. Best practice recommendation to never exceed
* a rendered value of 1020px. _(Note: Providing custom inline styles will take precedence.)_
*/
maxWidth: _propTypes.default.string,
/**
* Whether or not the field is required.
*/
required: _propTypes.default.bool,
/**
* Whether or not to append the 'optional' label to a non-required field label.
*/
showOptional: _propTypes.default.bool,
/**
* Provides first class prop for custom inline styles
*/
// eslint-disable-next-line react/forbid-prop-types
style: _propTypes.default.object
};
var defaultProps = {
children: null,
error: null,
errorIcon: /*#__PURE__*/_react.default.createElement(_IconError.default, null),
help: null,
hideRequired: false,
htmlFor: undefined,
isInvalid: false,
isInline: false,
isLabelHidden: false,
labelAttrs: {},
maxWidth: undefined,
required: false,
showOptional: false
};
var hasWhiteSpace = function hasWhiteSpace(s) {
return /\s/g.test(s);
};
// Detect IE 10 or IE 11
// TODO - Delete detection logic when we drop support for IE
var isIE = function isIE() {
return window.navigator.userAgent.indexOf('Trident/6.0') > -1 || window.navigator.userAgent.indexOf('Trident/7.0') > -1;
};
var Field = function Field(props) {
var children = props.children,
error = props.error,
errorIcon = props.errorIcon,
help = props.help,
hideRequired = props.hideRequired,
htmlFor = props.htmlFor,
isInvalid = props.isInvalid,
isInline = props.isInline,
isLabelHidden = props.isLabelHidden,
label = props.label,
labelAttrs = props.labelAttrs,
maxWidth = props.maxWidth,
required = props.required,
showOptional = props.showOptional,
style = props.style,
customProps = _objectWithoutProperties(props, _excluded);
var theme = _react.default.useContext(_terraThemeContext.default);
var customStyles = maxWidth ? _objectSpread({
maxWidth: maxWidth
}, style) : style;
var fieldClasses = (0, _classnames.default)(cx(['field', {
'field-inline': isInline
}, theme.className]), customProps.className);
var labelClassNames = cx(['label', labelAttrs.className]);
// Checks to run when not in production
// TODO - Remove this once we make this a required prop.
// More info: https://github.com/cerner/terra-core/issues/2290
if (process.env.NODE_ENV !== 'production') {
if (!htmlFor) {
// eslint-disable-next-line
console.warn('The htmlFor prop will be required in the next major version bump of terra-form-field. It is needed for creating an accessible mapping from the form field to its related error and help text.');
}
if (htmlFor && hasWhiteSpace(htmlFor)) {
// eslint-disable-next-line
console.warn('The htmlFor prop should be a string without white spaces as it will be used as an HTML attribute value. Use - or _ in place of white space characters.');
}
}
/**
* IE + JAWS has trouble reading aria-describedby content with our form components.
* Using feature detect for Microsoft browsers and injecting the help and error messages
* into the label as visually hidden text so JAWS can announce them correctly in IE.
*/
var IEDescriptionText = isIE() ? /*#__PURE__*/_react.default.createElement("div", {
className: cx('visually-hidden-text')
}, isInvalid && error ? error : null, help) : null;
var content = _react.default.Children.map(children, function (child) {
if ((required || isInvalid) && child && (child.type.isInput || child.type.isSelect || child.type.isTextarea)) {
return /*#__PURE__*/_react.default.cloneElement(child, _objectSpread(_objectSpread({}, required && {
required: true
}), isInvalid && {
isInvalid: true,
'aria-invalid': true
}));
}
return child;
});
var labelGroup = /*#__PURE__*/_react.default.createElement("div", {
className: cx(['label-group', {
'label-group-hidden': isLabelHidden
}])
}, isInvalid && /*#__PURE__*/_react.default.createElement("div", {
className: cx('error-icon')
}, errorIcon), /*#__PURE__*/_react.default.createElement("label", _extends({
htmlFor: htmlFor
}, labelAttrs, {
className: labelClassNames
}), required && (isInvalid || !hideRequired) && /*#__PURE__*/_react.default.createElement("div", {
className: cx('required'),
"aria-hidden": "true"
}, "*"), label, required && !isInvalid && hideRequired && /*#__PURE__*/_react.default.createElement("div", {
className: cx('required-hidden'),
"aria-hidden": "true"
}, "*"), showOptional && !required && /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, {
id: "Terra.form.field.optional"
}, function (optionalText) {
return /*#__PURE__*/_react.default.createElement("span", {
className: cx('optional')
}, optionalText);
}), IEDescriptionText), !isInvalid && /*#__PURE__*/_react.default.createElement("div", {
className: cx('error-icon-hidden')
}, errorIcon));
/* eslint-disable react/forbid-dom-props */
return /*#__PURE__*/_react.default.createElement("div", _extends({
style: customStyles
}, customProps, {
className: fieldClasses
}), labelGroup, content, isInvalid && error && /*#__PURE__*/_react.default.createElement("div", {
id: htmlFor ? "".concat(htmlFor, "-error") : undefined,
className: cx('error-text')
}, error), help && /*#__PURE__*/_react.default.createElement("div", {
id: htmlFor ? "".concat(htmlFor, "-help") : undefined,
className: cx('help-text')
}, help));
/* eslint-enable react/forbid-dom-props */
};
Field.propTypes = propTypes;
Field.defaultProps = defaultProps;
var _default = exports.default = Field;