wix-style-react
Version:
197 lines (181 loc) • 6.46 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import StatusAlertSmall from 'wix-ui-icons-common/StatusAlertSmall';
import Input from '../Input';
import LabelledElement from '../LabelledElement';
import Text from '../Text';
import InputWithOptions from '../InputWithOptions';
import { classes } from './AutoCompleteWithLabel.st.css';
import dataHooks from './dataHooks';
import { optionValidator } from '../DropdownLayout/DropdownLayout';
class AutoCompleteWithLabel extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
value: props.value || '',
isEditing: false,
};
}
static displayName = 'AutoCompleteWithLabel';
static propTypes = {
/** Sets a default value for those who want to use this component un-controlled. */
dataHook: PropTypes.string,
/** Defines a value label to show inside of a field. */
label: PropTypes.string.isRequired,
/** Specify an array of options to display in the dropdown list. */
options: PropTypes.arrayOf(optionValidator).isRequired,
/** Pass a component you want to show as the suffix of the input, e.g., text string, icon. */
suffix: PropTypes.arrayOf(PropTypes.element),
/** Specify the status of a field. */
status: PropTypes.oneOf(['error']),
/** Defines the message to display on status icon hover. If not given or empty there will be no tooltip. */
statusMessage: PropTypes.node,
/** Defines a standard input onFocus callback. */
onFocus: PropTypes.func,
/** Defines a standard input onBlur callback */
onBlur: PropTypes.func,
/** Defines a standard input onChange callback. */
onChange: PropTypes.func,
/** Reference element data when a form is submitted. */
name: PropTypes.string,
/** Specifies the type of `<input/>` element to display. Default is text string. */
type: PropTypes.string,
/** Define a string that labels the current element in case where a text label is not visible on the screen. */
ariaLabel: PropTypes.string,
/** Focus the element on mount (standard React input autoFocus). */
autoFocus: PropTypes.bool,
/** Sets the value of native autocomplete attribute (check the [HTML spec](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete) for possible values */
autocomplete: PropTypes.string,
/** Specifies whether input should be disabled or not. */
disabled: PropTypes.bool,
/** Specifies a CSS class name to be appended to the component’s root element. */
className: PropTypes.string,
/** Sets the maximum number of characters that can be entered into a field. */
maxLength: PropTypes.number,
/** Sets a placeholder message to display. */
placeholder: PropTypes.string,
/** Defines a callback function which is called whenever user selects a different option in the list. */
onSelect: PropTypes.func,
/** Indicates whether to render using the native select element */
native: PropTypes.bool,
/** Value of rendered child input */
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Sets the maximum height of the dropdownLayout in pixels. */
maxHeightPixels: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
static defaultProps = {
...Input.defaultProps,
label: '',
options: [],
onSelect: () => {},
};
onSelect = option => {
const { value } = option;
this.setState({
value,
});
this.props.onSelect(option);
this.setState({ isEditing: false });
};
onChange = event => {
const { value } = event.target;
this.setState({ value, isEditing: true });
this.props.onChange && this.props.onChange(event);
};
_isInputControlled = () => typeof this.props.value !== 'undefined';
render() {
const {
label,
dataHook,
options,
status,
suffix,
statusMessage,
onFocus,
name,
type,
ariaLabel,
autoFocus,
autocomplete,
disabled,
className,
maxLength,
placeholder,
native,
onBlur,
maxHeightPixels,
} = this.props;
const { value } = this._isInputControlled() ? this.props : this.state;
const predicate = this.state.isEditing
? option => option.value.toLowerCase().includes(value.toLowerCase())
: () => true;
const filteredOptions = options.filter(predicate);
const suffixContainer = suffix
? suffix.map((item, index) => {
return (
<div className={classes.suffix} key={`${dataHook}-${index}`}>
{item}
</div>
);
})
: [];
return (
<div data-hook={dataHook}>
<LabelledElement
label={label}
dataHook={dataHooks.labelledElement}
value={value}
>
<InputWithOptions
onChange={this.onChange}
onSelect={this.onSelect}
dataHook={dataHooks.inputWithOptions}
hideStatusSuffix
onFocus={onFocus}
onBlur={onBlur}
size={'large'}
inputElement={
<Input
name={name}
type={type}
ariaLabel={ariaLabel}
autoFocus={autoFocus}
autocomplete={autocomplete}
disabled={disabled}
maxLength={maxLength}
placeholder={placeholder}
dataHook={dataHooks.inputWithLabel}
value={value}
className={className}
suffix={suffixContainer}
status={status}
/>
}
options={filteredOptions}
native={native}
maxHeightPixels={maxHeightPixels}
/>
</LabelledElement>
{status === Input.StatusError && statusMessage && (
<Text
skin="error"
weight="normal"
size="small"
className={classes.statusMessage}
>
<span className={classes.statusMessageIcon}>
<StatusAlertSmall />
</span>
<span
data-hook={dataHooks.errorMessage}
className={classes.errorMessageContent}
>
{statusMessage}
</span>
</Text>
)}
</div>
);
}
}
export default AutoCompleteWithLabel;