@crave/farmblocks-hoc-input
Version:
A High Order Component that adds behaviour of label support, different styles for focus and errors, and validation error messages.
282 lines (249 loc) • 9.96 kB
JavaScript
import _styled from "styled-components";
function _extends() { _extends = Object.assign || 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 _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/* eslint-disable react/static-property-placement */
import * as React from "react";
import PropTypes from "prop-types";
import wrapDisplayName from "recompose/wrapDisplayName";
import Link from "@crave/farmblocks-link";
import Label from "@crave/farmblocks-label";
import { SmMagnifier, SmCross, SmChevronDown } from "@crave/farmblocks-icon";
import Text from "@crave/farmblocks-text";
import { fontSizes, fontTypes } from "@crave/farmblocks-theme";
import Wrapper from "./styledComponents/Wrapper";
const ICON_SIZE = 18;
export const formInputProps = {
label: PropTypes.node,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
type: PropTypes.string,
small: PropTypes.bool,
active: PropTypes.bool,
focused: PropTypes.bool,
disabled: PropTypes.bool,
invalid: PropTypes.bool,
onChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
input: PropTypes.shape({
value: PropTypes.any,
onChange: PropTypes.func
}),
readOnly: PropTypes.bool,
refName: PropTypes.string,
leftIcon: PropTypes.elementType,
prefix: PropTypes.node,
suffix: PropTypes.node,
autoControlFocusedStyle: PropTypes.bool,
className: PropTypes.string,
borderRadius: PropTypes.string,
mb: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
validationMessages: PropTypes.arrayOf(PropTypes.string)
};
const getValueFromProps = ({
input,
value
}) => input ? input.value : value;
const formInput = WrappedComponent => {
var _class, _temp;
return _temp = _class = class Input extends React.Component {
constructor(props) {
super(props);
_defineProperty(this, "componentDidUpdate", prevProps => {
if (this.props.focused && !prevProps.focused) {
this.setInputFocus();
}
if (!this.props.focused && prevProps.focused) {
var _this$inputRef;
(_this$inputRef = this.inputRef) === null || _this$inputRef === void 0 ? void 0 : _this$inputRef.blur();
}
if (!this.props.autoControlFocusedStyle && this.props.focused !== prevProps.focused) {
this.setState({
focused: this.props.focused
});
}
const nextValue = getValueFromProps(this.props);
const prevValue = getValueFromProps(prevProps);
if (nextValue !== prevValue) {
this.setState({
value: nextValue
});
}
});
_defineProperty(this, "handleClearClick", () => {
var _this$props$onChange, _this$props;
this.setState({
value: ""
});
(_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, {
type: "change",
value: "",
target: {
value: ""
}
});
});
_defineProperty(this, "setInputFocus", () => {
var _this$inputRef2;
(_this$inputRef2 = this.inputRef) === null || _this$inputRef2 === void 0 ? void 0 : _this$inputRef2.focus();
});
_defineProperty(this, "onChange", event => {
var _this$props$input, _this$props$input$onC, _this$props$onChange2, _this$props2;
this.setState({
value: event.value || event.target.value
});
(_this$props$input = this.props.input) === null || _this$props$input === void 0 ? void 0 : (_this$props$input$onC = _this$props$input.onChange) === null || _this$props$input$onC === void 0 ? void 0 : _this$props$input$onC.call(_this$props$input, event);
(_this$props$onChange2 = (_this$props2 = this.props).onChange) === null || _this$props$onChange2 === void 0 ? void 0 : _this$props$onChange2.call(_this$props2, event);
});
_defineProperty(this, "onFocus", event => {
var _this$props$onFocus, _this$props3;
(_this$props$onFocus = (_this$props3 = this.props).onFocus) === null || _this$props$onFocus === void 0 ? void 0 : _this$props$onFocus.call(_this$props3, event);
if (!this.props.autoControlFocusedStyle) {
return;
}
this.setState({
focused: true
});
});
_defineProperty(this, "onBlur", event => {
var _this$props$onBlur, _this$props4;
(_this$props$onBlur = (_this$props4 = this.props).onBlur) === null || _this$props$onBlur === void 0 ? void 0 : _this$props$onBlur.call(_this$props4, event);
if (!this.props.autoControlFocusedStyle) {
return;
}
this.setState({
focused: false
});
});
_defineProperty(this, "renderInput", ({
innerRef,
refName,
leftIcon,
prefix,
suffix,
...inputProps
}) => {
const handlers = {
onChange: this.onChange,
onFocus: this.onFocus,
onBlur: this.onBlur
};
const isSearch = inputProps.type && inputProps.type.toLowerCase() === "search";
const LeftIcon = leftIcon || isSearch && SmMagnifier;
const clearButton = isSearch && this.state.value && /*#__PURE__*/React.createElement(Link, {
className: "clear icon right",
"data-testid": "input-clear",
onClick: this.handleClearClick
}, /*#__PURE__*/React.createElement(SmCross, {
size: 24
}));
const isDropdown = inputProps.role === "combobox";
const RightIcon = isDropdown && SmChevronDown;
return (
/*#__PURE__*/
// Although we use onMouseDown, this element don't need role=button
// as it just prevents the input blur on click in the clear icon.
// there's no functionality in keyboard navigation.
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
React.createElement("div", {
className: `input ${isDropdown ? "dropdown" : ""}`,
ref: element => {
this.inputRef = element && element.querySelector("input, textarea");
}
}, prefix && /*#__PURE__*/React.createElement("div", {
className: "prefix"
}, prefix), LeftIcon && /*#__PURE__*/React.createElement("div", {
className: "icon left"
}, /*#__PURE__*/React.createElement(LeftIcon, {
size: ICON_SIZE
})), /*#__PURE__*/React.createElement(WrappedComponent, _extends({
className: `wrapped ${this.state.focused ? "focused" : ""}`
}, inputProps, handlers, {
[refName]: innerRef,
value: this.state.value
})), clearButton, RightIcon && /*#__PURE__*/React.createElement("div", {
className: "icon right"
}, /*#__PURE__*/React.createElement(SmChevronDown, {
size: ICON_SIZE
})), suffix && /*#__PURE__*/React.createElement("div", {
className: "suffix"
}, suffix))
);
});
this.state = {
value: getValueFromProps(props),
focused: props.focused
};
}
componentDidMount() {
if (this.props.focused) {
this.setInputFocus();
}
}
render() {
const {
value
} = this.state;
const {
label,
active,
focused,
onChange,
onFocus,
onBlur,
invalid,
input,
meta,
protected: covered,
autoControlFocusedStyle,
className,
borderRadius,
validationMessages,
mb,
small,
...wrappedComponentProps
} = this.props;
const wrapperProps = {
protected: covered,
active,
focused: this.state.focused,
invalid: invalid || (validationMessages === null || validationMessages === void 0 ? void 0 : validationMessages.length) > 0,
filled: !!value || value === 0,
disabled: wrappedComponentProps.disabled,
type: wrappedComponentProps.type,
small,
className,
borderRadius,
mb
};
return /*#__PURE__*/React.createElement(Wrapper, _extends({}, wrapperProps, {
onClick: this.setInputFocus
}), this.renderInput(wrappedComponentProps), label && /*#__PURE__*/React.createElement(Label, {
className: "label",
focused: this.state.focused || active,
invalid: wrapperProps.invalid,
protected: covered,
disabled: wrapperProps.disabled
}, label), validationMessages === null || validationMessages === void 0 ? void 0 : validationMessages.map(text => /*#__PURE__*/React.createElement(_StyledText, {
className: "error-message-text",
size: fontSizes.SMALL,
type: fontTypes.NEGATIVE,
key: `err-${text}`
}, text)));
}
}, _defineProperty(_class, "displayName", wrapDisplayName(WrappedComponent, "formInput")), _defineProperty(_class, "propTypes", { ...WrappedComponent.propTypes,
...formInputProps
}), _defineProperty(_class, "defaultProps", {
type: "text",
refName: "ref",
autoControlFocusedStyle: true,
focused: false,
borderRadius: "4px",
mb: "24px"
}), _temp;
};
export default formInput;
var _StyledText = _styled(Text).withConfig({
displayName: "formInput___StyledText",
componentId: "sc-181syp4-0"
})(["margin-top:8px;order:3;"]);