= (value: T) => void;
214 | type OnTouchedFn = () => void;
215 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/003-bellsprout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
9 |
10 |
11 |
13 |
14 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
34 |
35 |
36 |
38 |
39 |
40 |
43 |
44 |
45 |
46 |
47 |
49 |
50 |
51 |
53 |
54 |
55 |
57 |
58 |
59 |
61 |
62 |
63 |
66 |
67 |
68 |
69 |
71 |
72 |
73 |
74 |
75 |
80 |
81 |
82 |
84 |
85 |
86 |
87 |
88 |
93 |
94 |
95 |
97 |
98 |
99 |
101 |
102 |
103 |
106 |
107 |
108 |
109 |
111 |
112 |
113 |
115 |
116 |
117 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/004-squirtle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
18 |
19 |
33 |
34 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
44 |
45 |
46 |
48 |
49 |
50 |
52 |
53 |
54 |
58 |
59 |
60 |
61 |
63 |
64 |
65 |
66 |
67 |
71 |
72 |
73 |
75 |
76 |
77 |
78 |
79 |
83 |
84 |
85 |
91 |
92 |
93 |
96 |
97 |
98 |
101 |
102 |
103 |
104 |
105 |
108 |
109 |
110 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/007-pikachu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
14 |
15 |
16 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
31 |
33 |
34 |
35 |
37 |
38 |
39 |
41 |
42 |
43 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
54 |
55 |
56 |
58 |
59 |
65 |
66 |
67 |
68 |
69 |
70 |
73 |
74 |
75 |
77 |
78 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
107 |
108 |
110 |
111 |
112 |
113 |
114 |
119 |
120 |
121 |
123 |
124 |
125 |
126 |
127 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Angular Bar Rating
4 | Minimal, light-weight Angular ratings.
5 |
6 |
7 | [](https://ngx-bar-rating.netlify.app/)
8 | [](https://stackblitz.com/edit/ngx-bar-rating)
9 | [](https://www.npmjs.com/package/ngx-bar-rating)
10 | [](https://github.com/MurhafSousli/ngx-bar-rating/actions/workflows/integrate.yml)[](https://codecov.io/gh/MurhafSousli/ngx-bar-rating)
11 | [](https://www.npmjs.com/package/ngx-bar-rating)
12 | [](https://www.npmjs.com/package/ngx-bar-rating)
13 | [](https://bundlephobia.com/result?p=ngx-bar-rating)
14 | [](/LICENSE)
15 |
16 | ___
17 |
18 | If you like this plugin, please give it a star ⭐.
19 |
20 | ## Table of Contents
21 |
22 | - [Live Demo](https://ngx-bar-rating.netlify.app/)
23 | - [Installation](#installation)
24 | - [Usage](#usage)
25 | - [Options](#options)
26 | - [Click Effect](#click-effect)
27 | - [CSS Variables](#css-variables)
28 | - [Themes](#themes)
29 | - [Issues](#issues)
30 | - [Author](#author)
31 |
32 |
33 |
34 | ## Installation
35 |
36 | Install it with npm
37 |
38 | ```bash
39 | npm i ngx-bar-rating
40 | ```
41 |
42 |
43 |
44 |
45 | ## Basic usage:
46 |
47 | Import `BarRating` or `BarRatingModule` in your component imports.
48 |
49 | ```ts
50 | @Component({
51 | standalone: true,
52 | selector: 'bars',
53 | template: `
54 |
55 | `,
56 | styleUrl: './example.component.scss',
57 | imports: [BarRating]
58 | })
59 | export class BarsComponent {
60 | rate: number = 4;
61 | }
62 | ```
63 |
64 | Import the theme in your global styles (unless you want to use custom template)
65 |
66 | ```scss
67 | @import 'ngx-bar-rating/themes/br-default-theme';
68 | ```
69 |
70 |
71 |
72 | ## Rating inputs and outputs:
73 |
74 | | Name | Description | Default |
75 | |------------------|----------------------------------------------------------------------|---------|
76 | | **[rate]** | Current rating. Can be a decimal value like 3.14 | null |
77 | | **[max]** | Maximal rating that can be given using this widget | 5 |
78 | | **[theme]** | Theme class, see available [themes](#themes) | default |
79 | | **[readonly]** | A flag that indicates if rating can be changed | false |
80 | | **[showText]** | Display rating title if available, otherwise display rating value | false |
81 | | **[required]** | A flag that indicates if rating is disabled. works only with forms | false |
82 | | **[disabled]** | A flag that indicates if rating is disabled. works only with forms | false |
83 | | **[titles]** | Titles array. array should represent all possible values including 0 | [] |
84 | | **(rateChange)** | A stream that emits when the rating value is changed | |
85 |
86 | ### Custom rating template
87 |
88 | `BarRatingModule` provides a couple of directives to set custom rating template of your choice.
89 |
90 | - `*ratingActive`: Set template, when a bar/star is active or hovered.
91 | - `*ratingInactive`: Set template, when a bar/star is inactive.
92 | - `*ratingFraction`: Set template, when a bar/star is a fraction.
93 |
94 | Here are some example:
95 |
96 | #### Bootstrap rating example
97 |
98 | ```html
99 |
100 |
101 |
102 |
103 | ```
104 |
105 | #### FontAwesome rating example
106 |
107 | ```html
108 |
109 |
110 |
111 |
112 |
113 | ```
114 |
115 | #### Movie rating example
116 |
117 | ```html
118 |
120 | ```
121 |
122 | It can be used with Angular forms:
123 |
124 | ```html
125 |
128 | form is valid: {{ form.valid ? 'true' : 'false' }}
129 | {{ formRating }}
130 | ```
131 |
132 | And reactive forms:
133 |
134 | ```html
135 |
138 | form is valid: {{ form.valid ? 'true' : 'false' }}
139 | {{ formRating }}
140 | ```
141 |
142 |
143 |
144 | ## Click effect
145 |
146 | To apply a scale-fade effect when a bar is clicked, simply add the `effect` directive to the `` component:
147 |
148 | ```html
149 |
150 | ```
151 |
152 | ## CSS variables
153 |
154 | * `--br-font-size`: Defines the font size for the step element. Affects the themes: [`default`, `square`, `stars`].
155 | * `--br-width`: Specifies the width of the step element. Affects the themes: [`stars`, `square`, `movie`, `vertical`, `horizontal`].
156 | * `--br-height`: Specifies the height of the step element. Affects the themes: [`stars`, `square`, `movie`, `vertical`, `horizontal`].
157 | * `--br-gap`: Sets the gap between the individual steps.
158 | * `--br-active-color`: Defines the color for active steps.
159 | * `--br-inactive-color`: Defines the color for inactive steps.
160 | * `--br-effect-scale`: Specifies the scale value for the scale-fade effect (e.g., `2`).
161 | * `--br-effect-duration`: Sets the duration of the scale-fade animation (e.g., `0.4s`).
162 | * `--br-effect-ease`: Defines the easing function for the scale-fade animation (e.g., `ease-out`).
163 |
164 |
165 |
166 | ## Predefined themes
167 |
168 | > If you want to use a custom rating template, you don't need to import any CSS theme.
169 |
170 | If you want to use one of the predefined themes, you will need to import it in the global style `style.scss`
171 |
172 | - Pure CSS stars (default) `theme="default"`
173 |
174 | ```css
175 | @import 'ngx-bar-rating/themes/br-default-theme';
176 | ```
177 |
178 | - Horizontal bars `theme="horizontal"`
179 |
180 | ```css
181 | @import 'ngx-bar-rating/themes/br-horizontal-theme';
182 | ```
183 |
184 | - Vertical bars `theme="vertical"`
185 |
186 | ```css
187 | @import 'ngx-bar-rating/themes/br-vertical-theme';
188 | ```
189 |
190 | - Custom stars `theme="stars"`
191 |
192 | ```css
193 | @import 'ngx-bar-rating/themes/br-stars-theme';
194 | ```
195 |
196 | - Movie rating `theme="movie"`
197 |
198 | ```css
199 | @import 'ngx-bar-rating/themes/br-movie-theme';
200 | ```
201 |
202 | - Square rating `theme="square"`
203 |
204 | ```css
205 | @import 'ngx-bar-rating/themes/br-square-theme';
206 | ```
207 |
208 | Rating style can be easily customized, check the classes used in any theme and add your own css.
209 |
210 | You can also do the same for forms classes such as `untouched, touched, dirty, invalid, valid ...etc`
211 |
212 |
213 |
214 | ## Issues
215 |
216 | If you identify any errors in this component, or have an idea for an improvement, please open
217 | an [issue](https://github.com/MurhafSousli/ngx-bar-rating/issues). I am excited to see what the community thinks of this
218 | project, and I would love your input!
219 |
220 |
221 |
222 | ## Author
223 |
224 | **Murhaf Sousli**
225 |
226 | - [github/murhafsousli](https://github.com/MurhafSousli)
227 | - [twitter/murhafsousli](https://twitter.com/MurhafSousli)
228 |
229 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Angular Bar Rating
4 | Minimal, light-weight Angular ratings.
5 |
6 |
7 | [](https://ngx-bar-rating.netlify.app/)
8 | [](https://stackblitz.com/edit/ngx-bar-rating)
9 | [](https://www.npmjs.com/package/ngx-bar-rating)
10 | [](https://github.com/MurhafSousli/ngx-bar-rating/actions/workflows/integrate.yml)[](https://codecov.io/gh/MurhafSousli/ngx-bar-rating)
11 | [](https://www.npmjs.com/package/ngx-bar-rating)
12 | [](https://www.npmjs.com/package/ngx-bar-rating)
13 | [](https://bundlephobia.com/result?p=ngx-bar-rating)
14 | [](/LICENSE)
15 |
16 | ___
17 |
18 | If you like this plugin, please give it a star ⭐.
19 |
20 | ## Table of Contents
21 |
22 | - [Live Demo](https://ngx-bar-rating.netlify.app/)
23 | - [Installation](#installation)
24 | - [Usage](#usage)
25 | - [Options](#options)
26 | - [Click Effect](#click-effect)
27 | - [CSS Variables](#css-variables)
28 | - [Themes](#themes)
29 | - [Issues](#issues)
30 | - [Author](#author)
31 |
32 |
33 |
34 | ## Installation
35 |
36 | Install it with npm
37 |
38 | ```bash
39 | npm i ngx-bar-rating
40 | ```
41 |
42 |
43 |
44 |
45 | ## Basic usage:
46 |
47 | Import `BarRating` or `BarRatingModule` in your component imports.
48 |
49 | ```ts
50 | @Component({
51 | standalone: true,
52 | selector: 'bars',
53 | template: `
54 |
55 | `,
56 | styleUrl: './example.component.scss',
57 | imports: [BarRating]
58 | })
59 | export class BarsComponent {
60 | rate: number = 4;
61 | }
62 | ```
63 |
64 | Import the theme in your global styles (unless you want to use custom template)
65 |
66 | ```scss
67 | @import 'ngx-bar-rating/themes/br-default-theme';
68 | ```
69 |
70 |
71 |
72 | ## Rating inputs and outputs:
73 |
74 | | Name | Description | Default |
75 | |------------------|----------------------------------------------------------------------|---------|
76 | | **[rate]** | Current rating. Can be a decimal value like 3.14 | null |
77 | | **[max]** | Maximal rating that can be given using this widget | 5 |
78 | | **[theme]** | Theme class, see available [themes](#themes) | default |
79 | | **[readonly]** | A flag that indicates if rating can be changed | false |
80 | | **[showText]** | Display rating title if available, otherwise display rating value | false |
81 | | **[required]** | A flag that indicates if rating is disabled. works only with forms | false |
82 | | **[disabled]** | A flag that indicates if rating is disabled. works only with forms | false |
83 | | **[titles]** | Titles array. array should represent all possible values including 0 | [] |
84 | | **(rateChange)** | A stream that emits when the rating value is changed | |
85 |
86 | ### Custom rating template
87 |
88 | `BarRatingModule` provides a couple of directives to set custom rating template of your choice.
89 |
90 | - `*ratingActive`: Set template, when a bar/star is active or hovered.
91 | - `*ratingInactive`: Set template, when a bar/star is inactive.
92 | - `*ratingFraction`: Set template, when a bar/star is a fraction.
93 |
94 | Here are some example:
95 |
96 | #### Bootstrap rating example
97 |
98 | ```html
99 |
100 |
101 |
102 |
103 | ```
104 |
105 | #### FontAwesome rating example
106 |
107 | ```html
108 |
109 |
110 |
111 |
112 |
113 | ```
114 |
115 | #### Movie rating example
116 |
117 | ```html
118 |
120 | ```
121 |
122 | It can be used with Angular forms:
123 |
124 | ```html
125 |
128 | form is valid: {{ form.valid ? 'true' : 'false' }}
129 | {{ formRating }}
130 | ```
131 |
132 | And reactive forms:
133 |
134 | ```html
135 |
138 | form is valid: {{ form.valid ? 'true' : 'false' }}
139 | {{ formRating }}
140 | ```
141 |
142 |
143 |
144 | ## Click effect
145 |
146 | To apply a scale-fade effect when a bar is clicked, simply add the `effect` directive to the `` component:
147 |
148 | ```html
149 |
150 | ```
151 |
152 | ## CSS variables
153 |
154 | * `--br-font-size`: Defines the font size for the step element. Affects the themes: [`default`, `square`, `stars`].
155 | * `--br-width`: Specifies the width of the step element. Affects the themes: [`stars`, `square`, `movie`, `vertical`, `horizontal`].
156 | * `--br-height`: Specifies the height of the step element. Affects the themes: [`stars`, `square`, `movie`, `vertical`, `horizontal`].
157 | * `--br-gap`: Sets the gap between the individual steps.
158 | * `--br-active-color`: Defines the color for active steps.
159 | * `--br-inactive-color`: Defines the color for inactive steps.
160 | * `--br-effect-scale`: Specifies the scale value for the scale-fade effect (e.g., `2`).
161 | * `--br-effect-duration`: Sets the duration of the scale-fade animation (e.g., `0.4s`).
162 | * `--br-effect-ease`: Defines the easing function for the scale-fade animation (e.g., `ease-out`).
163 |
164 |
165 |
166 | ## Predefined themes
167 |
168 | > If you want to use a custom rating template, you don't need to import any CSS theme.
169 |
170 | If you want to use one of the predefined themes, you will need to import it in the global style `style.scss`
171 |
172 | - Pure CSS stars (default) `theme="default"`
173 |
174 | ```css
175 | @import 'ngx-bar-rating/themes/br-default-theme';
176 | ```
177 |
178 | - Horizontal bars `theme="horizontal"`
179 |
180 | ```css
181 | @import 'ngx-bar-rating/themes/br-horizontal-theme';
182 | ```
183 |
184 | - Vertical bars `theme="vertical"`
185 |
186 | ```css
187 | @import 'ngx-bar-rating/themes/br-vertical-theme';
188 | ```
189 |
190 | - Custom stars `theme="stars"`
191 |
192 | ```css
193 | @import 'ngx-bar-rating/themes/br-stars-theme';
194 | ```
195 |
196 | - Movie rating `theme="movie"`
197 |
198 | ```css
199 | @import 'ngx-bar-rating/themes/br-movie-theme';
200 | ```
201 |
202 | - Square rating `theme="square"`
203 |
204 | ```css
205 | @import 'ngx-bar-rating/themes/br-square-theme';
206 | ```
207 |
208 | Rating style can be easily customized, check the classes used in any theme and add your own css.
209 |
210 | You can also do the same for forms classes such as `untouched, touched, dirty, invalid, valid ...etc`
211 |
212 |
213 |
214 | ## Issues
215 |
216 | If you identify any errors in this component, or have an idea for an improvement, please open
217 | an [issue](https://github.com/MurhafSousli/ngx-bar-rating/issues). I am excited to see what the community thinks of this
218 | project, and I would love your input!
219 |
220 |
221 |
222 | ## Author
223 |
224 | **Murhaf Sousli**
225 |
226 | - [github/murhafsousli](https://github.com/MurhafSousli)
227 | - [twitter/murhafsousli](https://twitter.com/MurhafSousli)
228 |
229 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/001-caterpie.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
9 |
10 |
11 |
13 |
14 |
15 |
17 |
18 |
19 |
21 |
22 |
23 |
25 |
26 |
27 |
29 |
30 |
31 |
33 |
34 |
35 |
37 |
38 |
47 |
49 |
50 |
51 |
52 |
56 |
57 |
58 |
60 |
61 |
62 |
63 |
64 |
68 |
69 |
70 |
73 |
74 |
75 |
78 |
79 |
90 |
91 |
92 |
94 |
95 |
96 |
98 |
99 |
100 |
102 |
103 |
104 |
106 |
107 |
108 |
112 |
113 |
114 |
115 |
116 |
118 |
119 |
120 |
122 |
123 |
124 |
126 |
127 |
128 |
130 |
131 |
132 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating/src/lib/bar-rating.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 | import { By } from '@angular/platform-browser';
3 | import {
4 | FormsModule,
5 | ReactiveFormsModule,
6 | ControlValueAccessor,
7 | NG_VALIDATORS,
8 | NG_VALUE_ACCESSOR,
9 | UntypedFormControl
10 | } from '@angular/forms';
11 | import { BarRating } from 'ngx-bar-rating';
12 |
13 | describe('BarRating Component', () => {
14 | let component: BarRating;
15 | let fixture: ComponentFixture;
16 |
17 | beforeEach(async () => {
18 | await TestBed.configureTestingModule({
19 | imports: [FormsModule, ReactiveFormsModule, BarRating],
20 | }).compileComponents();
21 |
22 | fixture = TestBed.createComponent(BarRating);
23 | component = fixture.componentInstance;
24 | fixture.detectChanges();
25 | });
26 |
27 | it('should create the component', () => {
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | it('should register BarRating as a provider for NG_VALUE_ACCESSOR', () => {
32 | const injector = fixture.debugElement.injector;
33 |
34 | const valueAccessorProviders = injector.get(NG_VALUE_ACCESSOR);
35 | expect(valueAccessorProviders.some(provider => provider instanceof BarRating)).toBeTrue();
36 | });
37 |
38 | it('should register BarRating as a provider for NG_VALIDATORS', () => {
39 | const injector = fixture.debugElement.injector;
40 |
41 | const valueAccessorProviders = injector.get(NG_VALIDATORS);
42 | expect(valueAccessorProviders.some(provider => provider instanceof BarRating)).toBeTrue();
43 | });
44 |
45 | it('should call writeValue, registerOnChange, and registerOnTouched', () => {
46 | const onChangeSpy = jasmine.createSpy('onChange');
47 | const onTouchedSpy = jasmine.createSpy('onTouched');
48 |
49 | // Register onChange and onTouched
50 | component.registerOnChange(onChangeSpy);
51 | component.registerOnTouched(onTouchedSpy);
52 |
53 | // Call writeValue and verify value propagation
54 | component.writeValue(3);
55 | expect(component.rate()).toBe(3);
56 |
57 | // Simulate interaction
58 | component.updateRating(4);
59 | expect(onChangeSpy).toHaveBeenCalledWith(4);
60 |
61 | // Simulate touch
62 | component.onTouched();
63 | expect(onTouchedSpy).toHaveBeenCalled();
64 | });
65 |
66 | it('should initialize with default options', () => {
67 | expect(component.rate()).toBeUndefined();
68 | expect(component.max()).toBe(component.defaultOptions.maxValue);
69 | expect(component.readonly()).toBe(component.defaultOptions.readonly);
70 | });
71 |
72 | it('should update rating and emit onChange', () => {
73 | const spy = spyOn(component, 'onChange');
74 | component.updateRating(4);
75 | expect(component.rate()).toBe(4);
76 | expect(spy).toHaveBeenCalledWith(4);
77 | });
78 |
79 | it('should validate required input', () => {
80 | fixture.componentRef.setInput('required', true);
81 | const control = new UntypedFormControl('');
82 | const validationResult = component.validate(control);
83 | expect(validationResult).toEqual({ required: true });
84 |
85 | control.setValue(3);
86 | expect(component.validate(control)).toBeNull();
87 | });
88 |
89 | it('should handle writeValue correctly', () => {
90 | component.writeValue(5);
91 | expect(component.rate()).toBe(5);
92 | });
93 |
94 | it('should set disabled state', () => {
95 | component.setDisabledState(true);
96 | expect(component.disabled).toBeTrue();
97 | });
98 |
99 | it('should compute correct contexts for units', () => {
100 | fixture.componentRef.setInput('max', 5);
101 | component.updateRating(3.5);
102 | fixture.detectChanges();
103 |
104 | const contexts = component.contexts();
105 | expect(contexts).toEqual([
106 | 'selected',
107 | 'selected',
108 | 'selected',
109 | 'fraction',
110 | 'inactive'
111 | ]);
112 | });
113 |
114 | it('should compute correct rating text', () => {
115 | fixture.componentRef.setInput('titles', ['Poor', 'Fair', 'Good', 'Very Good', 'Excellent']);
116 | component.updateRating(3);
117 | fixture.detectChanges();
118 |
119 | expect(component.ratingText()).toBe('Very Good');
120 | });
121 |
122 | it('should display rating value if titles input is not provided', () => {
123 | component.updateRating(2);
124 | fixture.detectChanges();
125 |
126 | expect(component.ratingText()).toBe(2);
127 | });
128 |
129 | it('should compute correct contexts for units when hovered', () => {
130 | component.hoveredIndex.set(3);
131 | fixture.detectChanges();
132 |
133 | expect(component.contexts()).toEqual([
134 | 'active',
135 | 'active',
136 | 'active',
137 | 'inactive',
138 | 'inactive'
139 | ]);
140 | });
141 |
142 | describe('handleKeydown', () => {
143 | it('should increase rating with ArrowRight', () => {
144 | component.rate.set(3);
145 | const event = new KeyboardEvent('keydown', { key: 'ArrowRight' });
146 | component.handleKeydown(event);
147 | expect(component.rate()).toBe(4); // Increased from 3 to 4
148 | });
149 |
150 | it('should decrease rating with ArrowLeft', () => {
151 | component.rate.set(3);
152 | const event = new KeyboardEvent('keydown', { key: 'ArrowLeft' });
153 | component.handleKeydown(event);
154 | expect(component.rate()).toBe(2); // Decreased from 3 to 2
155 | });
156 |
157 | it('should not increase rating above max', () => {
158 | component.rate.set(5); // Set to max
159 | const event = new KeyboardEvent('keydown', { key: 'ArrowRight' });
160 | component.handleKeydown(event);
161 | expect(component.rate()).toBe(5); // Remains at max
162 | });
163 |
164 | it('should not decrease rating below 1', () => {
165 | component.rate.set(1); // Set to min
166 | const event = new KeyboardEvent('keydown', { key: 'ArrowLeft' });
167 | component.handleKeydown(event);
168 | expect(component.rate()).toBe(1); // Remains at min
169 | });
170 |
171 | it('should emit barClick event when rating changes', () => {
172 | component.rate.set(3);
173 | spyOn(component.barClick, 'emit');
174 | const event = new KeyboardEvent('keydown', { key: 'ArrowRight' });
175 | component.handleKeydown(event);
176 | expect(component.barClick.emit).toHaveBeenCalledWith(4); // Emit new rating
177 | });
178 |
179 | it('should do nothing for unsupported keys', () => {
180 | component.rate.set(3);
181 | const event = new KeyboardEvent('keydown', { key: 'a' });
182 | component.handleKeydown(event);
183 | expect(component.rate()).toBe(3); // Unchanged
184 | });
185 |
186 | it('should not process keydown if readonly or disabled', () => {
187 | component.rate.set(3);
188 | fixture.componentRef.setInput('readonly', true);
189 | const event = new KeyboardEvent('keydown', { key: 'ArrowRight' });
190 | component.handleKeydown(event);
191 | expect(component.rate()).toBe(3); // Unchanged
192 |
193 | fixture.componentRef.setInput('readonly', false);
194 | component.disabled = true;
195 | component.handleKeydown(event);
196 | expect(component.rate()).toBe(3); // Unchanged
197 | });
198 |
199 | it('should handle Enter key without modifying the rate', () => {
200 | spyOn(component, 'updateRating'); // Spy on updateRating method
201 | spyOn(component.barClick, 'emit'); // Spy on barClick emitter
202 |
203 | // Set initial rate
204 | component.rate.set(3);
205 |
206 | // Simulate keydown for Enter key
207 | const event = new KeyboardEvent('keydown', { key: 'Enter' });
208 | component.handleKeydown(event);
209 |
210 | // Assert that updateRating was not called
211 | expect(component.updateRating).not.toHaveBeenCalled();
212 |
213 | // Assert that barClick was not emitted
214 | expect(component.barClick.emit).not.toHaveBeenCalled();
215 |
216 | // Assert that rate remains unchanged
217 | expect(component.rate()).toBe(3);
218 | });
219 |
220 | it('should handle Space key without modifying the rate', () => {
221 | spyOn(component, 'updateRating'); // Spy on updateRating method
222 | spyOn(component.barClick, 'emit'); // Spy on barClick emitter
223 |
224 | // Set initial rate
225 | component.rate.set(3);
226 |
227 | // Simulate keydown for Space key
228 | const event = new KeyboardEvent('keydown', { key: ' ' });
229 | component.handleKeydown(event);
230 |
231 | // Assert that updateRating was not called
232 | expect(component.updateRating).not.toHaveBeenCalled();
233 |
234 | // Assert that barClick was not emitted
235 | expect(component.barClick.emit).not.toHaveBeenCalled();
236 |
237 | // Assert that rate remains unchanged
238 | expect(component.rate()).toBe(3);
239 | });
240 | });
241 |
242 | describe('handleClick', () => {
243 | it('should update rating when clicking on a bar', () => {
244 | const target = fixture.debugElement.query(By.css('.br-unit[data-value="4"]'));
245 | const event = new MouseEvent('click', { bubbles: true });
246 | spyOn(component.barClick, 'emit');
247 |
248 | target.nativeElement.dispatchEvent(event);
249 | component.handleClick(event);
250 |
251 | expect(component.rate()).toBe(4); // Updated to clicked value
252 | expect(component.barClick.emit).toHaveBeenCalledWith(4); // Emit clicked rating
253 | });
254 |
255 | it('should not process click if readonly or disabled', () => {
256 | component.rate.set(3);
257 | fixture.componentRef.setInput('readonly', true);
258 |
259 | const target = fixture.debugElement.query(By.css('.br-unit[data-value="4"]'));
260 | const event = new MouseEvent('click', { bubbles: true });
261 |
262 | target.nativeElement.dispatchEvent(event);
263 | component.handleClick(event);
264 |
265 | expect(component.rate()).toBe(3); // Unchanged
266 |
267 | fixture.componentRef.setInput('readonly', false);
268 | component.disabled = true;
269 |
270 | component.handleClick(event);
271 | expect(component.rate()).toBe(3); // Unchanged
272 | });
273 |
274 | it('should do nothing if clicking outside a bar', () => {
275 | component.rate.set(3);
276 | const event = new MouseEvent('click', { bubbles: true });
277 | const outsideTarget = fixture.debugElement.query(By.css('.br')).nativeElement;
278 |
279 | outsideTarget.dispatchEvent(event);
280 | component.handleClick(event);
281 |
282 | expect(component.rate()).toBe(3); // Unchanged
283 | });
284 | });
285 | });
286 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/006-bullbasaur.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
15 |
16 |
17 |
20 |
21 |
22 |
29 |
30 |
31 |
32 |
33 |
35 |
36 |
37 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
53 |
54 |
55 |
57 |
58 |
59 |
61 |
62 |
63 |
65 |
66 |
67 |
68 |
69 |
70 |
73 |
74 |
75 |
77 |
78 |
79 |
80 |
81 |
85 |
86 |
87 |
89 |
90 |
91 |
92 |
93 |
97 |
99 |
100 |
101 |
104 |
105 |
106 |
109 |
110 |
111 |
112 |
113 |
114 |
117 |
118 |
119 |
122 |
123 |
124 |
129 |
130 |
131 |
132 |
133 |
136 |
137 |
138 |
141 |
142 |
143 |
148 |
149 |
150 |
151 |
152 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/002-venonat.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
15 |
16 |
17 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
38 |
39 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
54 |
55 |
56 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
74 |
75 |
76 |
77 |
79 |
80 |
81 |
83 |
84 |
85 |
91 |
92 |
93 |
94 |
95 |
96 |
98 |
99 |
100 |
102 |
103 |
104 |
111 |
112 |
113 |
114 |
115 |
117 |
118 |
119 |
121 |
122 |
123 |
129 |
130 |
131 |
132 |
133 |
135 |
136 |
137 |
138 |
139 |
143 |
145 |
146 |
148 |
149 |
150 |
151 |
152 |
156 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
185 |
186 |
187 |
188 |
189 |
193 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/008-charmander.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
64 |
65 |
66 |
73 |
74 |
75 |
76 |
77 |
79 |
80 |
81 |
83 |
84 |
85 |
87 |
88 |
89 |
91 |
92 |
93 |
97 |
98 |
99 |
100 |
102 |
103 |
104 |
105 |
106 |
110 |
111 |
112 |
114 |
115 |
116 |
117 |
118 |
123 |
125 |
126 |
127 |
130 |
131 |
132 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
154 |
155 |
156 |
158 |
159 |
160 |
161 |
162 |
163 |
165 |
166 |
167 |
168 |
169 |
170 |
175 |
176 |
177 |
178 |
179 |
181 |
182 |
183 |
184 |
185 |
186 |
188 |
189 |
190 |
191 |
192 |
193 |
198 |
199 |
200 |
201 |
202 |
204 |
205 |
206 |
207 |
208 |
209 |
211 |
212 |
213 |
214 |
215 |
216 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
--------------------------------------------------------------------------------
/projects/ngx-bar-rating-demo/public/custom/005-jigglypuff.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
27 |
28 |
29 |
31 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
48 |
49 |
50 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
62 |
63 |
64 |
67 |
68 |
69 |
70 |
71 |
73 |
74 |
75 |
77 |
78 |
79 |
81 |
82 |
104 |
105 |
107 |
108 |
109 |
111 |
112 |
113 |
115 |
116 |
133 |
134 |
135 |
137 |
138 |
139 |
141 |
142 |
143 |
145 |
146 |
147 |
151 |
152 |
153 |
154 |
156 |
157 |
158 |
159 |
160 |
165 |
166 |
167 |
169 |
170 |
171 |
172 |
173 |
178 |
179 |
180 |
182 |
183 |
184 |
185 |
186 |
187 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
--------------------------------------------------------------------------------