Skip to content

Commit

Permalink
feat(rating): allow decimal numbers as rating values
Browse files Browse the repository at this point in the history
Closes #817
  • Loading branch information
maxokorokov committed Sep 30, 2016
1 parent 8b0fb3b commit 964d8cf
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template #t let-fill="fill">
<span *ngIf="fill === 100" class="star full">&hearts;</span>
<span *ngIf="fill === 0" class="star">&hearts;</span>
<span *ngIf="fill < 100 && fill > 0" class="star">
<span class="half" [style.width.%]="fill">&hearts;</span>&hearts;
</span>
</template>
<ngb-rating [(rate)]="currentRate" [starTemplate]="t" [readonly]="true" max="5"></ngb-rating>
<hr>
<pre>Rate: <b>{{currentRate}}</b></pre>
<button class="btn btn-sm btn-outline-primary" (click)="currentRate = 1.35">1.35</button>
<button class="btn btn-sm btn-outline-primary" (click)="currentRate = 4.72">4.72</button>
26 changes: 26 additions & 0 deletions demo/src/app/components/rating/demos/decimal/decimal.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {Component} from '@angular/core';

@Component({
selector: 'ngbd-rating-decimal',
templateUrl: './decimal.component.html',
styles: [`
.star {
position: relative;
display: inline-block;
font-size: 3rem;
color: #d3d3d3;
}
.full {
color: red;
}
.half {
position: absolute;
display: inline-block;
overflow: hidden;
color: red;
}
`]
})
export class RatingDecimalComponent {
currentRate = 3.14;
}
3 changes: 3 additions & 0 deletions demo/src/app/components/rating/demos/decimal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './decimal.component';
export const decimalTsContent = require('!!prismjs?lang=typescript!./decimal.component.ts');
export const decimalHtmlContent = require('!!prismjs?lang=markup!./decimal.component.html');
8 changes: 7 additions & 1 deletion demo/src/app/components/rating/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import {RatingBasicComponent} from './basic/basic.component';
import {RatingConfigComponent} from './config/config.component';
import {RatingTemplateComponent} from './template/template.component';
import {RatingEventsComponent} from './events/events.component';
import {RatingDecimalComponent} from './decimal/decimal.component';

export const DEMO_DIRECTIVES = [RatingBasicComponent, RatingConfigComponent, RatingEventsComponent, RatingTemplateComponent];
export const DEMO_DIRECTIVES = [RatingBasicComponent, RatingConfigComponent,
RatingEventsComponent, RatingTemplateComponent, RatingDecimalComponent];

export const DEMO_SNIPPETS = {
basic: {
Expand All @@ -18,6 +20,10 @@ export const DEMO_SNIPPETS = {
code: require('!!prismjs?lang=typescript!./template/template.component'),
markup: require('!!prismjs?lang=markup!./template/template.component.html')
},
decimal: {
code: require('!!prismjs?lang=typescript!./decimal/decimal.component'),
markup: require('!!prismjs?lang=markup!./decimal/decimal.component.html')
},
config: {
code: require('!!prismjs?lang=typescript!./config/config.component'),
markup: require('!!prismjs?lang=markup!./config/config.component.html')
Expand Down
3 changes: 3 additions & 0 deletions demo/src/app/components/rating/rating.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<ngbd-example-box demoTitle="Custom star template" [htmlSnippet]="snippets.template.markup" [tsSnippet]="snippets.template.code">
<ngbd-rating-template></ngbd-rating-template>
</ngbd-example-box>
<ngbd-example-box demoTitle="Custom decimal rating" [htmlSnippet]="snippets.decimal.markup" [tsSnippet]="snippets.decimal.code">
<ngbd-rating-decimal></ngbd-rating-decimal>
</ngbd-example-box>
<ngbd-example-box demoTitle="Global configuration of ratings" [htmlSnippet]="snippets.config.markup" [tsSnippet]="snippets.config.code">
<ngbd-rating-config></ngbd-rating-config>
</ngbd-example-box>
Expand Down
39 changes: 36 additions & 3 deletions src/rating/rating.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ function getState(compiled) {
return stars.map(star => star.textContent.trim() === String.fromCharCode(9733));
}

function getStateText(compiled) {
const stars = getStars(compiled);
return stars.map(star => star.textContent.trim());
}

describe('ngb-rating', () => {
beforeEach(
() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbRatingModule.forRoot()]}); });
Expand Down Expand Up @@ -97,9 +102,36 @@ describe('ngb-rating', () => {
<ngb-rating [starTemplate]="t" rate="2" max="4"></ngb-rating>`);

const compiled = fixture.nativeElement;
expect(getStateText(compiled)).toEqual(['x', 'x', 'o', 'o']);
});

const textContents = getStars(compiled).map(s => s.textContent.trim());
expect(textContents).toEqual(['x', 'x', 'o', 'o']);
it('should calculate fill percentage correctly', () => {
const fixture = createTestComponent(`
<template #t let-fill="fill">{{fill}}</template>
<ngb-rating [starTemplate]="t" [rate]="rate" max="4"></ngb-rating>`);

const compiled = fixture.nativeElement;
expect(getStateText(compiled)).toEqual(['100', '100', '100', '0']);

fixture.componentInstance.rate = 0;
fixture.detectChanges();
expect(getStateText(compiled)).toEqual(['0', '0', '0', '0']);

fixture.componentInstance.rate = 2.2;
fixture.detectChanges();
expect(getStateText(compiled)).toEqual(['100', '100', '20', '0']);

fixture.componentInstance.rate = 2.25;
fixture.detectChanges();
expect(getStateText(compiled)).toEqual(['100', '100', '25', '0']);

fixture.componentInstance.rate = 2.2548;
fixture.detectChanges();
expect(getStateText(compiled)).toEqual(['100', '100', '25', '0']);

fixture.componentInstance.rate = 7;
fixture.detectChanges();
expect(getStateText(compiled)).toEqual(['100', '100', '100', '100']);
});

describe('aria support', () => {
Expand Down Expand Up @@ -192,5 +224,6 @@ describe('ngb-rating', () => {

@Component({selector: 'test-cmp', template: ''})
class TestComponent {
max: number = 10;
max = 10;
rate = 3;
}
41 changes: 34 additions & 7 deletions src/rating/rating.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import {Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnInit, TemplateRef} from '@angular/core';
import {
Component,
ChangeDetectionStrategy,
Input,
Output,
EventEmitter,
OnInit,
TemplateRef,
OnChanges,
SimpleChanges
} from '@angular/core';
import {NgbRatingConfig} from './rating-config';

/**
Expand All @@ -25,13 +35,14 @@ export interface StarTemplateContext {
<span (mouseenter)="enter(index + 1)" (click)="update(index + 1)" [title]="r.title"
[attr.aria-valuetext]="r.title"
[style.cursor]="readonly ? 'not-allowed' : 'pointer'">
<template [ngTemplateOutlet]="starTemplate || t" [ngOutletContext]="{fill: index < rate ? 100 : 0}"></template>
<template [ngTemplateOutlet]="starTemplate || t" [ngOutletContext]="{fill: getFillValue(index)}"></template>
</span>
</template>
</span>
`
})
export class NgbRating implements OnInit {
export class NgbRating implements OnInit,
OnChanges {
private _oldRate: number;
range: number[] = [];

Expand All @@ -41,7 +52,7 @@ export class NgbRating implements OnInit {
@Input() max: number;

/**
* Current rating.
* Current rating. Can be a decimal value like 3.75
*/
@Input() rate: number;

Expand Down Expand Up @@ -85,11 +96,27 @@ export class NgbRating implements OnInit {
this.hover.emit(value);
}

ngOnInit(): void {
this._oldRate = this.rate;
this.range = this._buildTemplateObjects();
getFillValue(index: number): number {
const diff = this.rate - index;

if (diff >= 1) {
return 100;
}
if (diff < 1 && diff > 0) {
return Number.parseInt((diff * 100).toFixed(2));
}

return 0;
}

ngOnChanges(changes: SimpleChanges) {
if (changes['rate']) {
this._oldRate = this.rate;
}
}

ngOnInit(): void { this.range = this._buildTemplateObjects(); }

reset(): void {
this.leave.emit(this.rate);
this.rate = this._oldRate;
Expand Down

0 comments on commit 964d8cf

Please sign in to comment.