wix-style-react
Version:
wix-style-react
155 lines • 7.44 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { withFocusable } from '../common/Focusable';
import { st, classes } from './FacesRatingBar.st.css';
import { dataHooks, facesData } from './constants';
import Box from '../Box/Box';
import Tooltip from '../Tooltip/Tooltip';
import { FaceDisapointed, FaceFrowning, FaceNeutral, FaceSmiling, FaceGrining, } from './icons/FaceIcons';
const faceIconsMap = {
5: {
1: FaceDisapointed,
2: FaceFrowning,
3: FaceNeutral,
4: FaceSmiling,
5: FaceGrining,
},
4: {
1: FaceDisapointed,
2: FaceFrowning,
3: FaceSmiling,
4: FaceGrining,
},
3: {
1: FaceDisapointed,
2: FaceNeutral,
3: FaceSmiling,
},
2: {
1: FaceDisapointed,
2: FaceSmiling,
},
};
/** A rating component that will enable the user to rate on a 1-5 scale. */
class FacesRatingBar extends React.PureComponent {
constructor() {
super(...arguments);
this.state = {
faceHoveredIndex: 0,
};
this._onFaceClick = index => {
this.props.onChange(index);
};
this._onFaceMouseEnter = index => {
this.setState({ faceHoveredIndex: index });
};
this._onFaceMouseLeave = () => {
this.setState({ faceHoveredIndex: 0 });
};
this._onFaceFocus = (faceIndex, focusableProps) => {
// We would like to change the faces color on focus / hover
this.setState({ faceHoveredIndex: faceIndex });
focusableProps.focusableOnFocus();
};
this._onFaceBlur = focusableProps => {
// We would like to change the faces color on focus / hover
this.setState({ faceHoveredIndex: 0 });
focusableProps.focusableOnBlur();
};
this._shouldShowDescriptionValues = () => {
const { readOnly, descriptionValues, maxValue } = this.props;
let shouldShowDescriptionValues = false;
// Adding description values is not available in read only mode and it must be an array of strings at size of at least 2.
if (!readOnly && descriptionValues) {
shouldShowDescriptionValues =
Array.isArray(descriptionValues) &&
descriptionValues.length === maxValue;
}
return shouldShowDescriptionValues;
};
}
componentDidMount() {
const { readOnly, value } = this.props;
if (readOnly && value === 0) {
throw new Error('In readOnly mode the value couldn’t be 0. Please enter a value between 1 to 5.');
}
}
render() {
const { dataHook, readOnly, value, descriptionValues, maxValue } = this.props;
const { faceHoveredIndex } = this.state;
const showDescriptionValues = this._shouldShowDescriptionValues();
return (React.createElement(Box, { className: st(classes.root, {}, this.props), dataHook: dataHook },
React.createElement(Faces, { readOnly: readOnly, maxValue: maxValue, selectedIndex: value, hoveredIndex: faceHoveredIndex, showDescriptionValues: showDescriptionValues, descriptionValues: descriptionValues, onClick: this._onFaceClick, onMouseEnter: this._onFaceMouseEnter, onMouseLeave: this._onFaceMouseLeave, handleFocus: this._onFaceFocus, handleBlur: this._onFaceBlur })));
}
}
const Faces = ({ readOnly, selectedIndex, hoveredIndex, showDescriptionValues, descriptionValues, onClick, onMouseEnter, onMouseLeave, handleFocus, handleBlur, maxValue, }) => {
const facesArr = [];
for (let i = 1; i <= maxValue; i++) {
const isSelected = selectedIndex === i;
const isHovered = hoveredIndex === i;
const iconType = facesData[i].name;
const commonProps = {
key: i,
faceIndex: i,
isSelected,
iconType,
maxValue,
};
const face = readOnly ? (React.createElement(ReadOnlyModeFace, { ...commonProps })) : (React.createElement(FocusableInteractiveModeFace, { ...commonProps, isHovered: isHovered, showDescriptionValues: showDescriptionValues, descriptionValues: descriptionValues, onClick: onClick, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, handleFocus: handleFocus, handleBlur: handleBlur }));
facesArr.push(face);
}
return facesArr;
};
const InteractiveModeFace = ({ faceIndex, isSelected, iconType, isHovered, showDescriptionValues, descriptionValues, onClick, onMouseEnter, onMouseLeave, handleFocus, handleBlur, maxValue, ...focusableProps }) => {
const IconTagName = faceIconsMap[maxValue][faceIndex];
const tooltipContent = showDescriptionValues
? descriptionValues[faceIndex - 1]
: '';
return (React.createElement("button", { "data-hook": dataHooks.face, "data-index": faceIndex, "data-selected": isSelected, className: st(classes.buttonWrapper), onClick: () => onClick(faceIndex), onMouseEnter: () => onMouseEnter(faceIndex), onMouseLeave: onMouseLeave, onFocus: () => handleFocus(faceIndex, focusableProps), onBlur: () => handleBlur(focusableProps) },
React.createElement("div", { "data-hook": dataHooks.face, "data-selected": isSelected, className: st(classes.faceContainer, {
type: 'interactive',
hovered: isHovered,
selected: isSelected,
iconType,
}) },
React.createElement(Tooltip, { dataHook: facesData[faceIndex].tooltipDataHook, content: tooltipContent, disabled: !showDescriptionValues },
React.createElement(IconTagName, { width: 22, height: 22 })))));
};
const FocusableInteractiveModeFace = withFocusable(InteractiveModeFace);
const ReadOnlyModeFace = ({ faceIndex, isSelected, iconType, maxValue }) => {
const IconTagName = faceIconsMap[maxValue][faceIndex];
return (React.createElement("div", { "data-hook": dataHooks.face, "data-selected": isSelected, className: st(classes.faceContainer, {
type: 'readOnly',
selected: isSelected,
iconType,
}) },
React.createElement(IconTagName, null)));
};
FacesRatingBar.displayName = 'FacesRatingBar';
FacesRatingBar.propTypes = {
/** Applies a data-hook HTML attribute that can be used in the tests */
dataHook: PropTypes.string,
/** Allows you to apply a CSS class to the component’s root element */
className: PropTypes.string,
/** Specifies whether the rating bar is in read only mode */
readOnly: PropTypes.bool,
/** Specifies the rate value descriptions’ texts. The array length must match the maxValue prop number (5 by default). */
descriptionValues: PropTypes.arrayOf(PropTypes.string),
/** Specifies the selected rate. `0` indicates undefined rating. `readOnly` mode value cannot be `0`. */
value: PropTypes.oneOf([0, 1, 2, 3, 4, 5]).isRequired,
/** Specifies the maximum rate value. */
maxValue: PropTypes.oneOf([2, 3, 4, 5]),
/** Defines a handler that is called whenever a rating changes.
* ##### Signature:
* function(rating: number) => void
* * `rating`: 1 | 2 | 3 | 4 | 5
*/
onChange: PropTypes.func,
};
FacesRatingBar.defaultProps = {
readOnly: false,
onChange: () => { },
maxValue: 5,
};
export default FacesRatingBar;
//# sourceMappingURL=FacesRatingBar.js.map