Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 6b5e636

Browse files
bekospkozlowski-opensource
authored andcommitted
feat(rating): add rating directive
Closes #337
1 parent e75cea9 commit 6b5e636

File tree

6 files changed

+201
-0
lines changed

6 files changed

+201
-0
lines changed

src/rating/docs/demo.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<div ng-controller="RatingDemoCtrl">
2+
<rating value="rate" max="10" readonly="isReadonly"></rating>
3+
4+
<hr/>
5+
<pre>Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i></pre>
6+
7+
<hr/>
8+
<button class="btn btn-small btn-danger" ng-click="rate = 0" ng-disabled="isReadonly">Clear</button>
9+
<button class="btn btn-small" ng-click="isReadonly = ! isReadonly">Toggle Readonly</button>
10+
</div>

src/rating/docs/demo.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var RatingDemoCtrl = function ($scope) {
2+
$scope.rate = 7;
3+
$scope.isReadonly = false;
4+
};

src/rating/docs/readme.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Rating directive that will take care of visualising a star rating bar.
2+
3+
It also provides optional attribute `max` to vary the number of stars and `readonly` attribute to diasble user's interaction.

src/rating/rating.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
angular.module('ui.bootstrap.rating', [])
2+
3+
.constant('ratingConfig', {
4+
max: 5
5+
})
6+
7+
.directive('rating', ['ratingConfig', '$parse', function(ratingConfig, $parse) {
8+
return {
9+
restrict: 'EA',
10+
scope: {
11+
value: '='
12+
},
13+
templateUrl: 'template/rating/rating.html',
14+
replace: true,
15+
link: function(scope, element, attrs) {
16+
17+
var maxRange = angular.isDefined(attrs.max) ? scope.$eval(attrs.max) : ratingConfig.max;
18+
19+
scope.range = [];
20+
for (var i = 1; i <= maxRange; i++) {
21+
scope.range.push(i);
22+
}
23+
24+
scope.rate = function(value) {
25+
if ( ! scope.readonly ) {
26+
scope.value = value;
27+
}
28+
};
29+
30+
scope.enter = function(value) {
31+
if ( ! scope.readonly ) {
32+
scope.val = value;
33+
}
34+
};
35+
36+
scope.reset = function() {
37+
scope.val = angular.copy(scope.value);
38+
};
39+
scope.reset();
40+
41+
scope.$watch('value', function(value) {
42+
scope.val = value;
43+
});
44+
45+
scope.readonly = false;
46+
if (attrs.readonly) {
47+
scope.$parent.$watch($parse(attrs.readonly), function(value) {
48+
scope.readonly = !!value;
49+
});
50+
}
51+
}
52+
};
53+
}]);

src/rating/test/rating.spec.js

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
describe('rating directive', function () {
2+
var $rootScope, element;
3+
beforeEach(module('ui.bootstrap.rating'));
4+
beforeEach(module('template/rating/rating.html'));
5+
beforeEach(inject(function(_$compile_, _$rootScope_) {
6+
$compile = _$compile_;
7+
$rootScope = _$rootScope_;
8+
$rootScope.rate = 3;
9+
element = $compile('<rating value="rate"></rating>')($rootScope);
10+
$rootScope.$digest();
11+
}));
12+
13+
function getState(stars) {
14+
var state = [];
15+
for (var i = 0, n = stars.length; i < n; i++) {
16+
state.push( (stars.eq(i).hasClass('icon-star') && ! stars.eq(i).hasClass('icon-star-empty')) );
17+
}
18+
return state;
19+
}
20+
21+
it('contains the default number of icons', function() {
22+
expect(element.find('i').length).toBe(5);
23+
});
24+
25+
it('initializes the default star icons as selected', function() {
26+
var stars = element.find('i');
27+
expect(getState(stars)).toEqual([true, true, true, false, false]);
28+
});
29+
30+
it('handles correcty the click event', function() {
31+
var stars = element.find('i');
32+
33+
var star2 = stars.eq(1);
34+
star2.click();
35+
$rootScope.$digest();
36+
expect(getState(stars)).toEqual([true, true, false, false, false]);
37+
expect($rootScope.rate).toBe(2);
38+
39+
var star5 = stars.eq(4);
40+
star5.click();
41+
$rootScope.$digest();
42+
expect(getState(stars)).toEqual([true, true, true, true, true]);
43+
expect($rootScope.rate).toBe(5);
44+
});
45+
46+
it('handles correcty the hover event', function() {
47+
var stars = element.find('i');
48+
49+
var star2 = stars.eq(1);
50+
star2.trigger('mouseover');
51+
$rootScope.$digest();
52+
expect(getState(stars)).toEqual([true, true, false, false, false]);
53+
expect($rootScope.rate).toBe(3);
54+
55+
var star5 = stars.eq(4);
56+
star5.trigger('mouseover');
57+
$rootScope.$digest();
58+
expect(getState(stars)).toEqual([true, true, true, true, true]);
59+
expect($rootScope.rate).toBe(3);
60+
61+
element.trigger('mouseout');
62+
expect(getState(stars)).toEqual([true, true, true, false, false]);
63+
expect($rootScope.rate).toBe(3);
64+
});
65+
66+
it('changes the number of selected icons when value changes', function() {
67+
$rootScope.rate = 2;
68+
$rootScope.$digest();
69+
70+
var stars = element.find('i');
71+
expect(getState(stars)).toEqual([true, true, false, false, false]);
72+
});
73+
74+
it('shows different number of icons when `max` attribute is set', function() {
75+
element = $compile('<rating value="rate" max="7"></rating>')($rootScope);
76+
$rootScope.$digest();
77+
78+
expect(element.find('i').length).toBe(7);
79+
});
80+
81+
it('handles readonly attribute', function() {
82+
$rootScope.isReadonly = true;
83+
element = $compile('<rating value="rate" readonly="isReadonly"></rating>')($rootScope);
84+
$rootScope.$digest();
85+
86+
var stars = element.find('i');
87+
expect(getState(stars)).toEqual([true, true, true, false, false]);
88+
89+
var star5 = stars.eq(4);
90+
star5.trigger('mouseover');
91+
$rootScope.$digest();
92+
expect(getState(stars)).toEqual([true, true, true, false, false]);
93+
94+
$rootScope.isReadonly = false;
95+
$rootScope.$digest();
96+
97+
star5.trigger('mouseover');
98+
$rootScope.$digest();
99+
expect(getState(stars)).toEqual([true, true, true, true, true]);
100+
});
101+
102+
});
103+
104+
describe('setting ratingConfig', function() {
105+
var $rootScope, element;
106+
var originalConfig = {};
107+
beforeEach(module('ui.bootstrap.rating'));
108+
beforeEach(module('template/rating/rating.html'));
109+
beforeEach(inject(function(_$compile_, _$rootScope_, ratingConfig) {
110+
$compile = _$compile_;
111+
$rootScope = _$rootScope_;
112+
$rootScope.rate = 5;
113+
angular.extend(originalConfig, ratingConfig);
114+
ratingConfig.max = 10;
115+
element = $compile('<rating value="rate"></rating>')($rootScope);
116+
$rootScope.$digest();
117+
}));
118+
afterEach(inject(function(ratingConfig) {
119+
// return it to the original state
120+
angular.extend(ratingConfig, originalConfig);
121+
}));
122+
123+
it('should change number of icon elements', function () {
124+
expect(element.find('i').length).toBe(10);
125+
});
126+
127+
});
128+

template/rating/rating.html

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<span ng-mouseleave="reset()">
2+
<i ng-repeat="number in range" ng-mouseenter="enter(number)" ng-click="rate(number)" ng-class="{'icon-star': number <= val, 'icon-star-empty': number > val}"></i>
3+
</span>

0 commit comments

Comments
 (0)