Creating a LWC to graphically display a picklist to amp up your user experience. Not because you need to – but because you can:
Lightning Design System: https://summer-20.lightningdesignsystem.com/icons/

Clicking on a star will lock in the value and you will be able to access it inside a screen flow:



Here is the LWC (for filling out stars):
HTML
<template>
<div class="rating-container">
<h2>{headerText}</h2>
<div class="star-rating">
<template for:each={stars} for:item="star">
<lightning-icon
key={star.value}
icon-name={star.icon}
alternative-text={star.value}
size="medium"
onmouseover={handleStarHover}
onmouseout={handleStarMouseOut}
data-value={star.value}
class="my-icon"
onclick={handleStarClick}>
</lightning-icon>
</template>
</div>
</div>
</template>
Code language: HTML, XML (xml)
java script
import { LightningElement, api, track } from 'lwc';
export default class FeedbackStarRating extends LightningElement {
@api headerText;
_ratingValue = 0; // Private variable for ratingValue
selectedStarValue = 0; // Tracks currently selected stars
mouseOutTimeoutId;
// Setter ensures the value updates when assigned by the Flow
@api
get ratingValue() {
return this._ratingValue;
}
set ratingValue(value) {
this._ratingValue = value;
this.selectedStarValue = value; // Ensure stars update immediately
}
// Getter to dynamically update star icons
get stars() {
return [1, 2, 3, 4, 5].map(value => ({
value,
icon: value <= this.selectedStarValue ? 'utility:favorite' : 'utility:favorite_alt'
}));
}
handleStarHover(event) {
if (this.selectedStarValue === 0) {
clearTimeout(this.mouseOutTimeoutId);
this.selectedStarValue = parseInt(event.target.dataset.value, 10);
}
}
handleStarMouseOut() {
this.mouseOutTimeoutId = setTimeout(() => {
if (this.selectedStarValue === 0) {
this.selectedStarValue = 0;
}
}, 250);
}
handleStarClick(event) {
this.selectedStarValue = parseInt(event.target.dataset.value, 10);
this._ratingValue = this.selectedStarValue;
// Dispatch event for Flow or parent component
this.dispatchEvent(new CustomEvent('ratingchange', {
detail: { ratingValue: this.selectedStarValue }
}));
}
}
Code language: JavaScript (javascript)
CSS
.star-rating {
display: flex;
justify-content: flex-start; /* or center or flex-end */
}
.my-icon {
--lwc-iconSize: 2rem; /* Adjust the size as needed */
--slds-c-icon-color-foreground-default: yellow; /* Makes the stars yellow */
cursor: pointer;
}
:host {
background-color: #cee8fe; /* Sets the background color to light blue */
padding: 10px; /* Optional: Adds some padding around the component */
border-radius: 5px; /* Optional: Adds rounded corners for a softer look */
display: block;
}
Code language: CSS (css)
meta xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>51.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
<target>lightning__FlowScreen</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__FlowScreen">
<property name="headerText" type="String" label="Header Text" description="Header text for the star rating component"/>
<property name="ratingValue" type="Integer" label="Rating Value" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Code language: HTML, XML (xml)
For hearts

HTML
<template>
<div class="rating-container">
<h2>{headerText}</h2>
<div class="icon-rating">
<template for:each={icons} for:item="icon">
<lightning-icon
key={icon.value}
icon-name={icon.icon}
alternative-text={icon.value}
size="medium"
onmouseover={handleIconHover}
onmouseout={handleIconMouseOut}
data-value={icon.value}
data-selected={icon.filled}
class="rating-icon"
onclick={handleIconClick}>
</lightning-icon>
</template>
</div>
</div>
</template>
Code language: HTML, XML (xml)
java script
import { LightningElement, api } from 'lwc';
export default class FeedbackRating extends LightningElement {
@api ratingValue;
@api headerText;
selectedIconValue = 0;
icons = [
{ value: 1, icon: 'utility:heart' },
{ value: 2, icon: 'utility:heart' },
{ value: 3, icon: 'utility:heart' },
{ value: 4, icon: 'utility:heart' },
{ value: 5, icon: 'utility:heart' },
];
mouseOutTimeoutId;
handleIconHover(event) {
if (this.selectedIconValue === 0) {
clearTimeout(this.mouseOutTimeoutId);
const hoverValue = parseInt(event.target.dataset.value, 10);
this.updateIcons(hoverValue, true);
}
}
handleIconMouseOut() {
this.mouseOutTimeoutId = setTimeout(() => {
if (this.selectedIconValue === 0) {
this.updateIcons(0, false);
}
}, 250);
}
handleIconClick(event) {
clearTimeout(this.mouseOutTimeoutId);
const clickedValue = parseInt(event.target.dataset.value, 10);
this.selectedIconValue = clickedValue;
this.ratingValue = clickedValue;
this.updateIcons(clickedValue, true);
this.dispatchEvent(new CustomEvent('ratingchange', {
detail: { ratingValue: clickedValue }
}));
}
updateIcons(value, isFilled) {
this.icons = this.icons.map(icon => ({
...icon,
filled: icon.value <= value
}));
}
}
Code language: JavaScript (javascript)
CSS
.icon-rating {
display: flex;
justify-content: flex-start; /* Aligns the icons to the left */
gap: 8px; /* Space between icons */
flex-wrap: wrap; /* Allows wrapping if there's not enough space */
}
.rating-icon {
--slds-c-icon-color-foreground-default: gray; /* Default color */
cursor: pointer;
transition: transform 0.2s ease-in-out;
}
.rating-icon:hover,
.rating-icon[data-selected="true"] {
--slds-c-icon-color-foreground-default: red; /* Fill color */
transform: scale(1.2);
}
:host {
background-color: #ffe5e5;
padding: 10px;
border-radius: 5px;
display: block;
}
Code language: CSS (css)
For Thumbs up
Update java script to
icons = [
{ value: 1, icon: 'utility:like' },
{ value: 2, icon: 'utility:like' },
{ value: 3, icon: 'utility:like' },
{ value: 4, icon: 'utility:like' },
{ value: 5, icon: 'utility:like' },
];
Code language: JavaScript (javascript)
Update CSS to
.rating-icon:hover,
.rating-icon[data-selected="true"] {
--slds-c-icon-color-foreground-default: rgb(255, 217, 0); /* Fill color */
transform: scale(1.2);
}
:host {
background-color: #8fe7e9;
padding: 10px;
border-radius: 5px;
display: block;
}
Code language: CSS (css)
If you want to add more values than 1 to 5 simply change the amount of icons:
// Update the icons array for 1 to 10 scale
icons = [
{ value: 1, icon: 'utility:heart' },
{ value: 2, icon: 'utility:heart' },
{ value: 3, icon: 'utility:heart' },
{ value: 4, icon: 'utility:heart' },
{ value: 5, icon: 'utility:heart' },
{ value: 6, icon: 'utility:heart' },
{ value: 7, icon: 'utility:heart' },
{ value: 8, icon: 'utility:heart' },
{ value: 9, icon: 'utility:heart' },
{ value: 10, icon: 'utility:heart' }
];
Code language: JavaScript (javascript)
In order to static ressources as an icon you have to modify your javascript like this:
import { LightningElement, api } from 'lwc';
import Icon from '@salesforce/resourceUrl/ncage'; // Static resource import
Code language: JavaScript (javascript)
// Update the icons array to use the static resource
icons = [
{ value: 1, icon: Icon }, // Using the static resource
{ value: 2, icon: Icon },
{ value: 3, icon: Icon },
{ value: 4, icon: Icon },
{ value: 5, icon: Icon }
];
Code language: JavaScript (javascript)
and your HTML like this
<template>
<div class="rating-container">
<h2>{headerText}</h2>
<div class="icon-rating">
<template for:each={icons} for:item="icon">
<img
key={icon.value}
src={icon.icon}
alt="Rating Icon"
data-value={icon.value}
class="rating-icon"
onclick={handleIconClick}
onmouseover={handleIconHover}
onmouseout={handleIconMouseOut} />
</template>
</div>
</div>
</template>
Code language: HTML, XML (xml)
Leave a Reply