react-native-stars-testing
Version:
Fork of react-native-stars to be implemented with automated testing.
257 lines (233 loc) • 9.3 kB
JavaScript
'use strict';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
View,
Image,
TouchableOpacity,
ImageBackground
} from 'react-native';
import testID from 'react-native-testid';
export default class StarReview extends Component {
constructor(props) {
super(props);
this.state = {
rating: props.rating
};
this.isReactElement = React.isValidElement;
this.ratingMode = this.ratingMode.bind(this);
this.halfRatingMode = this.halfRatingMode.bind(this);
this.displayBar = this.displayBar.bind(this);
this.displayOpaque = this.displayOpaque.bind(this);
this.star = this.star.bind(this);
}
componentWillReceiveProps(nextProps) {
if (this.props.rating !== nextProps.rating) {
this.setState({
rating: nextProps.rating,
});
}
}
displayBar() {
let partial = this.props.value - Math.floor(this.props.value);
let blockStyle = { height: this.props.starSize, width: this.props.starSize * (1.0 - partial), backgroundColor: this.props.backingColor };
let emptyBlockStyle = { height: this.props.starSize, width: this.props.starSize * partial, backgroundColor: 'transparent' };
let starStyle = { height: this.props.starSize, width: this.props.starSize, backgroundColor: this.props.backingColor };
let stars = [];
for (let i = 1; i < this.props.count + 1; i++) {
if (i == Math.floor(this.props.value) + 1) {
//partial star
let halfStarComponent = this.isReactElement(this.props.halfStar) ?
<View key={i}>{this.props.halfStar}</View>
:
<View key={i} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<ImageBackground style={starStyle} source={this.props.fullStar}>
<View style={{ flexDirection: 'row' }}>
<View style={emptyBlockStyle}></View>
<View style={blockStyle}></View>
</View>
<Image style={{ height: this.props.starSize, width: this.props.starSize, backgroundColor: 'transparent', position: 'absolute' }} source={this.props.emptyStar} />
</ImageBackground>
</View>;
stars.push(halfStarComponent);
} else if (i > Math.floor(this.props.value) + 1) {
//empty stars
let emptyStarComponent = this.isReactElement(this.props.emptyStar) ?
<View key={i}>{this.props.emptyStar}</View>
:
<View key={i} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<Image style={starStyle} source={this.props.emptyStar} />
</View>;
stars.push(emptyStarComponent);
} else {
//filled stars
let starComponent = this.isReactElement(this.props.fullStar) ?
<View key={i}>{this.props.fullStar}</View>
:
<View key={i} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<Image style={starStyle} source={this.props.fullStar} />
</View>;
stars.push(starComponent);
}
}
return (
<View>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>{stars}</View>
</View>
);
}
displayOpaque() {
let partial = this.props.value - Math.floor(this.props.value);
let starStyle = { height: this.props.starSize, width: this.props.starSize, opacity: 1.0, backgroundColor: 'transparent' };
let stars = [];
for (let i = 1; i < this.props.count + 1; i++) {
if (i == Math.floor(this.props.value) + 1) {
//partial star
let halfStarComponent = this.isReactElement(this.props.halfStar) ?
<View key={i} style={{ opacity: partial }}>
{this.props.halfStar}
</View>
:
<View key={i} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<ImageBackground style={starStyle} source={this.props.emptyStar}>
<Image style={{
height: this.props.starSize,
width: this.props.starSize,
opacity: partial,
backgroundColor: 'transparent'
}} source={this.props.fullStar} />
</ImageBackground>
</View>;
stars.push(halfStarComponent);
} else if (i > Math.floor(this.props.value) + 1) {
//empty stars
let emptyStarComponent = this.isReactElement(this.props.emptyStar) ?
<View key={i}>{this.props.emptyStar}</View>
:
<View key={i} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<Image style={starStyle} source={this.props.emptyStar} />
</View>
stars.push(emptyStarComponent);
} else {
//filled stars
let starComponent = this.isReactElement(this.props.fullStar) ?
<View key={i}>{this.props.fullStar}</View>
:
<View key={i} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<Image style={starStyle} source={this.props.fullStar} />
</View>
stars.push(starComponent);
}
}
return (
<View>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>{stars}</View>
</View>
);
}
halfStar(val, _star, _halfStar) {
let halfStarComponent = _halfStar || _star;
let isComponent = this.isReactElement(halfStarComponent);
halfStarComponent = isComponent ? halfStarComponent :
(<ImageBackground style={{ width: this.props.starSize, height: this.props.starSize }} source={_star}>
<Image style={{ width: this.props.starSize, height: this.props.starSize }} source={_halfStar} />
</ImageBackground>);
return (
<View key={val} style={{ paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
{halfStarComponent}
<View style={{ flexDirection: 'row', position: 'absolute' }}>
<TouchableOpacity {...testID(`star${val - 0.5}`)} style={!isComponent && { height: this.props.starSize, width: this.props.starSize / 2 }} disabled={this.props.disabled} onPress={() => {
this.setState({ rating: val - 0.5 });
this.props.update(val - 0.5);
}} />
<TouchableOpacity {...testID(`star${val}`)} style={!isComponent && { height: this.props.starSize, width: this.props.starSize / 2 }} disabled={this.props.disabled} onPress={() => {
this.setState({ rating: val });
this.props.update(val);
}} />
</View>
</View>
);
}
halfRatingMode() {
let stars = [];
for (let i = 1; i < this.props.count + 1; i++) {
let _star = (i <= this.state.rating) ? this.props.fullStar : this.props.emptyStar;
let _halfStar = (this.state.rating + 0.5 == i) ? this.props.halfStar : null;
stars.push(
this.halfStar(i, _star, _halfStar)
);
}
return (
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
{stars}
</View>
);
}
star(val, _star) {
let isComponent = this.isReactElement(_star);
let starComponent = isComponent ? _star :
(<Image style={{ width: this.props.starSize, height: this.props.starSize }} source={_star} />);
return (
<View key={val} style={!isComponent && { paddingLeft: this.props.spacing / 2, paddingRight: this.props.spacing / 2 }}>
<TouchableOpacity {...testID(`star${val}`)} disabled={this.props.disabled} onPress={() => {
this.setState({ rating: val });
this.props.update(val);
}}>
{starComponent}
</TouchableOpacity>
</View>
);
}
ratingMode() {
let stars = [];
for (let i = 1; i < this.props.count + 1; i++) {
let _star = (i <= this.state.rating) ? this.props.fullStar : this.props.emptyStar;
stars.push(
this.star(i, _star)
);
}
return (
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
{stars}
</View>
);
}
render() {
let view = this.props.value == null ? (this.props.half ? this.halfRatingMode() : this.ratingMode()) : (this.props.opacity ? this.displayOpaque() : this.displayBar());
return (
<View>
{view}
</View>
);
}
}
StarReview.propTypes = {
value: PropTypes.number,
count: PropTypes.number,
rating: PropTypes.number,
emptyStar: PropTypes.oneOfType([PropTypes.number, PropTypes.object]).isRequired,
fullStar: PropTypes.oneOfType([PropTypes.number, PropTypes.object]).isRequired,
halfStar: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
update: PropTypes.func,
starSize: PropTypes.number,
backingColor: PropTypes.string,
opacity: PropTypes.bool,
half: PropTypes.bool,
spacing: PropTypes.number,
disabled: PropTypes.bool,
};
StarReview.defaultProps = {
fullStar: require('./example-images/starFilled.png'),
halfStar: require('./example-images/starHalf.png'),
emptyStar: require('./example-images/starEmpty.png'),
disabled: false,
value: null,
count: 5,
rating: 0,
starSize: 30,
update: () => { },
backingColor: 'white',
opacity: false,
half: false,
spacing: 0
};