Skip to content

Commit

Permalink
[email protected] - Refactor component to allow fractional stars. (#2170)
Browse files Browse the repository at this point in the history
* [email protected] - Refactor component to allow fractional stars.

* [email protected] - Fix test comment.

* [email protected] - Remove template string.

* [email protected] - Update test description.

* [email protected] - PR comments.
  • Loading branch information
kevinrodrigues authored Oct 20, 2022
1 parent 5975020 commit 3219260
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 89 deletions.
13 changes: 13 additions & 0 deletions packages/components/molecules/f-rating/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

v0.4.0
------------------------------
*October 19, 2022*

### Added
- Test coverage for code changes.
- `starRatingSize` to allow component sizing.
- Prop validation.

### Changed
- Refactored markup to allow fractional rating sizes.


v0.3.0
------------------------------
*October 17, 2022*
Expand Down
1 change: 1 addition & 0 deletions packages/components/molecules/f-rating/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ The props that can be defined are as follows (if any):
| :--- |:--------:|:--------:|:-------:|:-----------------------------------------------------------------------------------------------------------------------|
| `starRating` | `Number` | Yes | - | Sets the displayed rating (filled stars). i.e. for `2 out of x`, 2 would be the `starRating` |
| `maxStarRating` | `Number` | No | 5 | Sets the maximum number of stars that the rating is set against. i.e. for `x out of 5`, 5 would be the `maxStarRating` |
| `starRatingSize` | `String` | No | 'small' | Sets the component size. By default the component will use `small` the other options are `medium` & `large` |

### Events

Expand Down
2 changes: 1 addition & 1 deletion packages/components/molecules/f-rating/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@justeat/f-rating",
"description": "Fozzie Rating - Global Rating component",
"version": "0.3.0",
"version": "0.4.0",
"main": "dist/f-rating.umd.min.js",
"maxBundleSize": "20kB",
"files": [
Expand Down
103 changes: 73 additions & 30 deletions packages/components/molecules/f-rating/src/components/Rating.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,39 @@
<div
:class="$style['c-rating']"
data-test-id="rating-component">
<ul
:class="$style['c-rating-starWrapper']">
<li
v-for="star in maxStarRating"
:key="star"
:class="$style['c-rating-star']">
<star-filled-icon
v-if="isRatingStarFilled(star)"
:class="$style['c-rating-star-filled']" />
<div :class="$style['c-rating-starWrapper']">
<div :class="$style['c-rating-container']">
<star-icon
v-else
:class="$style['c-rating-star-empty']" />
</li>
</ul>
<span
data-test-id="c-rating-description"
class="is-visuallyHidden">
{{ getRatingDescription }}
</span>
v-for="star in maxStarRating"
:key="star"
:class="[
$style['c-rating-star-empty'],
$style[`c-rating-star--${starRatingSize}`]
]" />
</div>

<div
:class="[
$style['c-rating-mask'],
$style['c-rating-container']
]"
:style="`--starRatingPercentage: ${getRatingStarPercentage}`">
<star-filled-icon
v-for="star in maxStarRating"
:key="star"
:style="`--starRatingSize: c-rating-star--${starRatingSize}`"
:class="[
$style['c-rating-star-filled'],
$style[`c-rating-star--${starRatingSize}`]
]" />
</div>

<span
data-test-id="c-rating-description"
class="is-visuallyHidden">
{{ getRatingDescription }}
</span>
</div>
</div>
</template>

Expand All @@ -31,6 +45,7 @@ import {
} from '@justeattakeaway/pie-icons-vue';
import { VueGlobalisationMixin } from '@justeat/f-globalisation';
import tenantConfigs from '../tenants';
import { VALID_STAR_RATING_SIZES } from '../constants';
export default {
name: 'VRating',
Expand All @@ -48,11 +63,17 @@ export default {
},
starRating: {
type: Number,
required: true
required: true,
validator: value => value >= 0 && value <= 5
},
maxStarRating: {
type: Number,
default: 5
},
starRatingSize: {
type: String,
default: 'small',
validator: value => !!VALID_STAR_RATING_SIZES[value]
}
},
Expand Down Expand Up @@ -80,18 +101,15 @@ export default {
rating: this.starRating,
total: this.maxStarRating
});
}
},
},
methods: {
/**
* Check `star` against value passed by consumer to allow empty stars to render.
* Calculate a percentage from the `starRating` value passed in by the consuming application.
*
* @param star {Number}
* @returns {boolean}
* @returns {string}
*/
isRatingStarFilled (star) {
return star <= this.starRating;
getRatingStarPercentage () {
return `${(this.starRating / this.maxStarRating) * 100}%`;
}
}
};
Expand All @@ -103,12 +121,25 @@ export default {
.c-rating-starWrapper {
margin: 0;
padding: 0;
list-style-type: none;
position: relative;
display: inline-block;
}
.c-rating-container {
display: flex;
}
.c-rating-star {
display: inline-block;
width: 15px; // Todo - decide on how to size these. Will create a ticket around this.
&--small {
width: 12px;
}
&--medium {
width: 16px;
}
&--large {
width: 28px;
}
}
.c-rating-star-filled {
Expand All @@ -122,4 +153,16 @@ export default {
fill: f.$color-mozzarella-50;
}
}
.c-rating-mask {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
width: var(--starRatingPercentage);
svg {
flex-shrink: 0;
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -30,74 +30,39 @@ describe('Rating', () => {
expect(wrapper.exists()).toBe(true);
});

describe('methods', () => {
describe('`isRatingStarFilled`', () => {
describe('computed', () => {
describe('`getRatingStarPercentage`', () => {
it('should exist', () => {
expect(wrapper.vm.isRatingStarFilled).toBeDefined();
});

it('should contain a description `c-rating-description`', () => {
// Act
const result = wrapper.find('[data-test-id="c-rating-description"]');
// Arrange
propsData.starRating = 2;
wrapper = shallowMount(VRating, {
propsData,
localVue,
i18n
});

// Assert
expect(result).toMatchSnapshot();
// Act & Assert
expect(wrapper.vm.getRatingStarPercentage).toBeDefined();
});

describe('when invoked', () => {
it('should return truthy when the argument `star` is less than or equal to `starRating`', () => {
// Act
const star = 1;
const result = wrapper.vm.isRatingStarFilled(star);

// Assert
expect(result).toBe(true);
});

it.each([1, 2])('should return truthy when the argument star is %s', value => {
it('should return a percentage from a combination of `starRating` and `maxStarRating`', () => {
// Arrange
propsData = {
starRating: value,
maxStarRating: 5
starRating: 2
};

// Act
wrapper = shallowMount(VRating, {
propsData,
localVue,
i18n,
mocks: {
$tc
}
i18n
});
const result = wrapper.vm.isRatingStarFilled(value);

// Assert
expect(result).toBe(true);
});

it('should return truthy when the argument `star` is equal to `starRating`', () => {
// Act
const star = 2;
const result = wrapper.vm.isRatingStarFilled(star);

// Assert
expect(result).toBe(true);
});

it('should return falsey when argument `star` is greater than `starRating`', () => {
// Act
const star = 3;
const result = wrapper.vm.isRatingStarFilled(star);

// Assert
expect(result).toBe(false);
// Act & Assert
expect(wrapper.vm.getRatingStarPercentage).toBe('40%');
});
});
});
});

describe('computed', () => {
describe('`getRatingDescription`', () => {
it('should exist', () => {
// Arrange
Expand Down Expand Up @@ -145,4 +110,52 @@ describe('Rating', () => {
});
});
});

describe('props', () => {
describe('`starRatingSize`', () => {
it('should set a default value of `small`', () => {
// Act & Assert
expect(VRating.props.starRatingSize.default).toBe('small');
});

it('should return a true when type prop of exists', () => {
// Act
const { validator } = VRating.props.starRatingSize;

// Arrange
expect(validator('small')).toBe(true);
});

it('should NOT allow invalid props', () => {
// Arrange
const { validator } = VRating.props.starRatingSize;

// Act & Assert
expect(validator('supernova')).toBe(false);
});
});

describe('`starRating`', () => {
it('should be required', () => {
// Act & Assert
expect(VRating.props.starRating.required).toBe(true);
});

it.each([0, 1, 2, 3, 4, 5])('should allow value `%s`', rating => {
// Act
const { validator } = VRating.props.starRating;

// Assert
expect(validator(rating)).toBe(true);
});

it('should NOT only allow values outside `0 - 5`', () => {
// Arrange
const { validator } = VRating.props.starRating;

// Act & Assert
expect(validator(6)).toBe(false);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,3 @@
exports[`Rating computed \`getRatingDescription\` when invoked should return a plural description if the rating is greater than 1 1`] = `"2 stars out of 5"`;

exports[`Rating computed \`getRatingDescription\` when invoked should return a singular description if the rating is less than 2 1`] = `"1 star out of 5"`;

exports[`Rating methods \`isRatingStarFilled\` should contain a description \`c-rating-description\` 1`] = `
<span data-test-id="c-rating-description" class="is-visuallyHidden">
</span>
`;
6 changes: 6 additions & 0 deletions packages/components/molecules/f-rating/src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// eslint-disable-next-line import/prefer-default-export
export const VALID_STAR_RATING_SIZES = {
small: true,
medium: true,
large: true
};
17 changes: 17 additions & 0 deletions packages/components/molecules/f-rating/src/tests/constants.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { VALID_STAR_RATING_SIZES } from '../constants';

describe('`constants`', () => {
describe('VALID_STAR_RATING_SIZES', () => {
it('should contain the correct sizes', () => {
// Arrange
const values = {
small: true,
medium: true,
large: true
};

// Act & Assert
expect(VALID_STAR_RATING_SIZES).toEqual(values);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export const RatingComponent = (args, { argTypes }) => ({

template: `<rating
v-bind="$props"
:starRating="2" />`
:starRatingSize="'medium'"
:starRating="2.5" />`
});

RatingComponent.storyName = 'f-rating';
Expand Down

0 comments on commit 3219260

Please sign in to comment.