feat: add review

This commit is contained in:
2024-08-12 17:44:58 +02:00
parent 89e819ece1
commit e2bacc283d
43 changed files with 3276 additions and 887 deletions

View File

@@ -0,0 +1,7 @@
import { reviewsStars } from './reviews-stars';
describe('reviewsStars', () => {
it('should work', () => {
expect(reviewsStars()).toEqual('reviews-stars');
});
});

View File

@@ -0,0 +1,53 @@
import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import './reviews-stars';
import { ReviewsStarsComponent } from './reviews-stars';
const meta: Meta<ReviewsStarsComponent> = {
title: 'Design System/Atoms/Reviews Stars',
component: 'z-reviews.stars',
render: (args) => html`
<z-reviews.stars
?allow-fractional="${args.allowFractional}"
rating="${args.rating}"
out-of="${args.outOf}"
.stars=${args.stars}
></z-reviews.stars>
`,
parameters: {
layout: 'centered',
},
argTypes: {
rating: {
control: 'number',
min: 1,
},
outOf: {
control: {
type: 'number',
min: 1,
},
},
stars: {
control: {
type: 'number',
min: 1,
},
},
allowFractional: {
control: 'boolean',
},
},
args: {
rating: 3,
outOf: 5,
stars: 5,
allowFractional: false,
},
};
export default meta;
export const Primary: StoryObj<ReviewsStarsComponent> = {};

View File

@@ -0,0 +1,140 @@
import { css, CSSResultArray, html, LitElement, TemplateResult } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
/**
* @cssprop --filled-star-color - The color of the filled stars.
* @cssprop --empty-star-color - The color of the empty stars.
*/
@customElement('z-reviews.stars')
export class ReviewsStarsComponent extends LitElement {
/// Private variables ///
/// Protected variables ///
@state() protected _filledStarCount = 0;
/// Public variables ///
@property({ type: Number }) public rating: number | undefined;
@property({ type: Number, attribute: 'out-of' }) public outOf: number | undefined;
@property({ type: Number }) public stars: number = 5;
@property({ type: Boolean, attribute: 'allow-fractional' }) public allowFractional = false;
/// constructor & lifecycle ///
override connectedCallback(): void {
super.connectedCallback();
this.validateProperties();
}
override updated(changedProperties: Map<string | number | symbol, unknown>): void {
super.updated(changedProperties);
this._filledStarCount = this.calculateFilledStarCount();
// this.requestUpdate();
}
/// Public methods ///
/// Protected methods ///
protected override render(): TemplateResult {
return html`
<div class="stars">${this.renderStars()}</div>
<div
class="stars stars--filled"
style="${styleMap({
'--filled-star-count': this._filledStarCount,
'--_filled-star-width': `${(this._filledStarCount / this.stars) * 100}%`,
})}"
>
${this.renderStars()}
</div>
`;
}
protected renderStars(): TemplateResult {
const stars: TemplateResult[] = [];
for (let i = 0; i < this.stars; i++) {
stars.push(this.renderStar());
}
return html`${stars}`;
}
protected renderStar(): TemplateResult {
return html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" class="icon">
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"
/>
</svg>
</svg>`;
}
/// Private methods ///
/**
* Validate the properties for the rules of availability.
*
* @throws {Error} If the properties are not valid.
*/
private validateProperties(): void {
console.log(this.rating, this.stars, this.outOf, this.allowFractional);
if (!this.rating) {
throw new Error('Rating is required.');
}
if (!this.outOf) {
throw new Error('Out of is required when rating is used.');
}
if (this.stars < 1) {
throw new Error('Stars cannot be less than 1.');
}
if (this.rating! > this.outOf) {
throw new Error('Rating cannot be greater than out of.');
}
}
private calculateFilledStarCount(): number {
if (this.allowFractional) {
return (this.rating! / this.outOf!) * this.stars;
}
return Math.round((this.rating! / this.outOf!) * this.stars);
}
/// Statics ///
static override styles: CSSResultArray = [
css`
:host {
display: flex;
position: relative;
}
.icon {
vertical-align: -0.125em;
flex: 0 0 1em;
height: 1em;
width: 1em;
fill: currentColor;
}
.stars {
display: flex;
color: var(--empty-star-color, #e4e5e9);
}
.stars--filled {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
opacity: 1;
right: calc(100% - var(--_filled-star-width));
color: var(--filled-star-color, #f7b731);
}
`,
];
}