Skip to content

Commit 8bbfd4d

Browse files
authored
Merge pull request #5 from igorwessel/develop
v0.0.2
2 parents 3acce3d + 966b66f commit 8bbfd4d

File tree

9 files changed

+311
-203
lines changed

9 files changed

+311
-203
lines changed

lib/__tests__/dynamic-star.test.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ describe('Without pass props optional props', () => {
55
test('should render five star', () => {
66
const { container } = render(<DynamicStar rating={5} />)
77

8-
expect(container.querySelectorAll('.star-container')).toHaveLength(5)
8+
expect(container.querySelectorAll('.dynamic-star-container')).toHaveLength(5)
99
})
1010

1111
test('should render five star filled', () => {
1212
const { container } = render(<DynamicStar rating={5} />)
1313

14-
const stars = container.querySelectorAll('.star-svg')
14+
const stars = container.querySelectorAll('.dynamic-star-svg')
1515

1616
stars.forEach((star) => {
1717
expect(star).toHaveAttribute(
@@ -25,7 +25,7 @@ describe('Without pass props optional props', () => {
2525
const rating = 2.5
2626
const { container } = render(<DynamicStar rating={rating} />)
2727

28-
const stars = container.querySelectorAll('.star-svg')
28+
const stars = container.querySelectorAll('.dynamic-star-svg')
2929

3030
expect(stars[0]).toHaveAttribute(
3131
'style',
@@ -52,7 +52,7 @@ describe('Without pass props optional props', () => {
5252
test('when rerender with a new rating, should correcty update in screen stars filled', () => {
5353
const { container, rerender } = render(<DynamicStar rating={0} />)
5454

55-
const stars = container.querySelectorAll('.star-svg')
55+
const stars = container.querySelectorAll('.dynamic-star-svg')
5656

5757
stars.forEach((star) => {
5858
expect(star).toHaveAttribute(
@@ -97,7 +97,7 @@ describe('Passing optional props', () => {
9797
<DynamicStar rating={1} width={50} height={50} />,
9898
)
9999

100-
const stars = container.querySelectorAll('.star-svg')
100+
const stars = container.querySelectorAll('.dynamic-star-svg')
101101

102102
stars.forEach((star) => {
103103
expect(star).toHaveAttribute(
@@ -112,15 +112,15 @@ describe('Passing optional props', () => {
112112
<DynamicStar rating={6.5} totalStars={7} />,
113113
)
114114

115-
expect(container.querySelectorAll('.star-svg')).toHaveLength(7)
115+
expect(container.querySelectorAll('.dynamic-star-svg')).toHaveLength(7)
116116
})
117117

118118
test('should outlined a star when pass outlined', () => {
119119
const { container } = render(
120120
<DynamicStar rating={5} outlined />,
121121
)
122122

123-
const stars = container.querySelectorAll('.star-svg')
123+
const stars = container.querySelectorAll('.dynamic-star-svg')
124124

125125
stars.forEach((star) => {
126126
expect(star).toHaveAttribute('style', expect.not.stringMatching(/stroke: none/))
@@ -130,7 +130,7 @@ describe('Passing optional props', () => {
130130
test("should use outlined color, when pass outlined with a color (outlined='black')", () => {
131131
const { container } = render(<DynamicStar rating={5} outlined='black' />)
132132

133-
const stars = container.querySelectorAll('.star-svg')
133+
const stars = container.querySelectorAll('.dynamic-star-svg')
134134

135135
stars.forEach((star) => {
136136
expect(star).toHaveAttribute(
@@ -143,7 +143,7 @@ describe('Passing optional props', () => {
143143
test('should change outline width, when pass outlineWidth', () => {
144144
const { container } = render(<DynamicStar rating={5} outlined='black' outlineWidth={2} />)
145145

146-
const stars = container.querySelectorAll('.star-svg')
146+
const stars = container.querySelectorAll('.dynamic-star-svg')
147147

148148
stars.forEach((star) => {
149149
expect(star).toHaveAttribute(
@@ -156,14 +156,14 @@ describe('Passing optional props', () => {
156156
test('when rerender with new totalStars, need to render all stars (removing or adding)', () => {
157157
const { container, rerender } = render(<DynamicStar rating={6.5} totalStars={7} />)
158158

159-
expect(container.querySelectorAll('.star-svg')).toHaveLength(7)
159+
expect(container.querySelectorAll('.dynamic-star-svg')).toHaveLength(7)
160160

161161
rerender(<DynamicStar rating={3} totalStars={6} />)
162162

163-
expect(container.querySelectorAll('.star-svg')).toHaveLength(6)
163+
expect(container.querySelectorAll('.dynamic-star-svg')).toHaveLength(6)
164164

165165
rerender(<DynamicStar rating={3} totalStars={8} />)
166166

167-
expect(container.querySelectorAll('.star-svg')).toHaveLength(8)
167+
expect(container.querySelectorAll('.dynamic-star-svg')).toHaveLength(8)
168168
})
169169
})

lib/main.tsx

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import * as React from 'react'
22
import { useId } from '@/hooks/useId'
33
import { IDynamicStarProps, IStar } from '@/types'
4-
import { createEmptyStar, emptyStar, createFullStar, createStarWithPercentageFilled } from '@/utils'
4+
import {
5+
createEmptyStar,
6+
emptyStar,
7+
} from '@/utils'
8+
import { reducer } from '@/reducer'
59
import './style.css'
610

711
function DynamicStar ({
@@ -16,8 +20,10 @@ function DynamicStar ({
1620
fullStarColor = '#FFBC00',
1721
}: IDynamicStarProps) {
1822
const id = useId('star')
19-
const [stars, setStars] = React.useState<IStar[]>(
20-
Array(totalStars).fill(createEmptyStar()),
23+
const internalTotalStars = totalStars < 0 ? 0 : totalStars
24+
const [stars, dispatch] = React.useReducer(
25+
reducer,
26+
Array(internalTotalStars).fill(createEmptyStar()),
2127
)
2228

2329
const getFullFillColor = (starData: IStar) =>
@@ -64,51 +70,44 @@ function DynamicStar ({
6470
* Responsible to remove a star when star count changes.
6571
*/
6672
React.useEffect(() => {
67-
const removeStars = totalStars - stars.length
68-
69-
if (totalStars - stars.length < 0) {
70-
setStars((prevState) => [...prevState.slice(0, removeStars)])
73+
const removeStars = internalTotalStars - stars.length
74+
if (removeStars < 0) {
75+
dispatch({
76+
type: 'REMOVE_STAR',
77+
payload: removeStars,
78+
})
7179
}
72-
}, [totalStars, stars.length])
80+
}, [internalTotalStars, stars.length])
7381

7482
/**
7583
* Responsible to add a new star when star count changes.
7684
*/
7785
React.useEffect(() => {
78-
if (totalStars - stars.length > 0) {
79-
setStars((prevState) => [
80-
...prevState,
81-
...Array(totalStars - prevState.length).fill(createEmptyStar()),
82-
])
86+
const addStars = internalTotalStars - stars.length
87+
if (addStars > 0) {
88+
dispatch({
89+
type: 'ADD_STAR',
90+
payload: addStars,
91+
})
8392
}
84-
}, [totalStars, stars.length])
93+
}, [internalTotalStars, stars.length])
8594

8695
/**
8796
* Responsible to fill stars
8897
*/
8998
React.useEffect(() => {
90-
const fullStarsCounter = Math.floor(rating)
91-
92-
const surplus = Math.round((rating % 1) * 10) / 10
93-
const roundedOneDecimalPoint = Math.round(surplus * 10) / 10
94-
95-
setStars((prevState) =>
96-
prevState.map((_, index) =>
97-
fullStarsCounter >= index + 1
98-
? createFullStar()
99-
: rating === index + roundedOneDecimalPoint
100-
? createStarWithPercentageFilled(roundedOneDecimalPoint)
101-
: createEmptyStar(),
102-
),
103-
)
99+
dispatch({
100+
type: 'FILL_STAR',
101+
payload: typeof rating === 'string' ? parseFloat(rating) : rating,
102+
})
104103
}, [rating, stars.length])
105104

106105
return (
107-
<div className='star-rating' aria-label={`${rating} of 5`}>
106+
<div className='dynamic-star-rating' aria-label={`${rating} of 5`}>
108107
{stars.map((star, index) => (
109-
<div key={`${id}_${index}`} className='star-container'>
108+
<div key={`${id}_${index}`} className='dynamic-star-container'>
110109
<svg
111-
className='star-svg'
110+
className='dynamic-star-svg'
112111
style={{
113112
fill: `url(#${id}_gradient${star.raw})`,
114113
stroke:

lib/reducer.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { IStar } from '@/types'
2+
import {
3+
createEmptyStar,
4+
createFullStar,
5+
createStarWithPercentageFilled,
6+
} from '@/utils'
7+
8+
type Actions = 'REMOVE_STAR' | 'ADD_STAR' | 'FILL_STAR';
9+
10+
type Action = {
11+
type: Actions;
12+
payload?: number;
13+
};
14+
15+
type ReducerFunction = (state: IStar[], action: Action) => IStar[];
16+
17+
const reducer: ReducerFunction = (state, action) => {
18+
switch (action.type) {
19+
case 'ADD_STAR': {
20+
if (action.payload === undefined) {
21+
return state
22+
}
23+
24+
return [
25+
...state,
26+
...Array(action.payload).fill(createEmptyStar()),
27+
]
28+
}
29+
case 'FILL_STAR': {
30+
if (action.payload === undefined) {
31+
return state
32+
}
33+
34+
const fullStarsCounter = Math.floor(action.payload)
35+
36+
const surplus = Math.round((action.payload % 1) * 10) / 10
37+
const roundedOneDecimalPoint = Math.round(surplus * 10) / 10
38+
39+
return state.map((_, index) =>
40+
fullStarsCounter >= index + 1
41+
? createFullStar()
42+
: action.payload === index + roundedOneDecimalPoint
43+
? createStarWithPercentageFilled(roundedOneDecimalPoint)
44+
: createEmptyStar(),
45+
)
46+
}
47+
case 'REMOVE_STAR': {
48+
return [...state.slice(0, action.payload)]
49+
}
50+
default:
51+
return state
52+
}
53+
}
54+
55+
export { reducer }

lib/style.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
.star-rating {
1+
.dynamic-star-rating {
22
display: flex;
33
align-items: center;
44
}
55

6-
.star-container {
6+
.dynamic-star-container {
77
display: flex;
88
}
99

10-
.star-container:not(:last-child) {
10+
.dynamic-star-container:not(:last-child) {
1111
margin-right: 5px;
1212
}

lib/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
type IDynamicStarProps = {
2-
rating: number;
2+
rating: string | number;
33
outlined?: string | boolean;
44
outlineWidth?: number;
55
sharpnessStar?: number;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"typescript": "4.4.4",
7676
"vite": "2.6.4",
7777
"vite-plugin-dts": "0.9.2",
78+
"vite-plugin-libcss": "1.0.5",
7879
"vite-plugin-linter": "1.0.1",
7980
"vite-tsconfig-paths": "3.3.17"
8081
}

0 commit comments

Comments
 (0)