Skip to content

Commit

Permalink
feat(ui5-rating-indicator): initial implementation (#1729)
Browse files Browse the repository at this point in the history
fifoosid authored Jun 8, 2020
1 parent 681de1f commit a28f201
Showing 13 changed files with 565 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/base/src/UI5Element.js
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import getConstructableStyle from "./theming/getConstructableStyle.js";
import createComponentStyleTag from "./theming/createComponentStyleTag.js";
import getEffectiveStyle from "./theming/getEffectiveStyle.js";
import Integer from "./types/Integer.js";
import Float from "./types/Float.js";
import { kebabToCamelCase, camelToKebabCase } from "./util/StringHelper.js";
import isValidPropertyName from "./util/isValidPropertyName.js";
import isSlot from "./util/isSlot.js";
@@ -306,6 +307,9 @@ class UI5Element extends HTMLElement {
if (propertyTypeClass === Integer) {
newValue = parseInt(newValue);
}
if (propertyTypeClass === Float) {
newValue = parseFloat(newValue);
}
this[nameInCamelCase] = newValue;
}
}
10 changes: 10 additions & 0 deletions packages/base/src/types/Float.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import DataType from "./DataType.js";

class Float extends DataType {
static isValid(value) {
// Assuming that integers are floats as well!
return Number(value) === value;
}
}

export default Float;
1 change: 1 addition & 0 deletions packages/main/bundle.esm.js
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ import Select from "./dist/Select.js";
import Switch from "./dist/Switch.js";
import MessageStrip from "./dist/MessageStrip.js";
import MultiComboBox from "./dist/MultiComboBox.js";
import RatingIndicator from "./dist/RatingIndicator.js";
import TabContainer from "./dist/TabContainer.js";
import Tab from "./dist/Tab.js";
import TabSeparator from "./dist/TabSeparator.js";
29 changes: 29 additions & 0 deletions packages/main/src/RatingIndicator.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<div class="ui5-rating-indicator-root"
role="slider"
aria-roledescription="{{_ariaRoleDescription}}"
aria-valuemin="0"
aria-valuenow="{{this.value}}"
aria-valuemax="{{this.maxValue}}"
aria-orientation="horizontal"
?aria-disabled="{{this.disabled}}"
?aria-readonly="{{this.readOnly}}"
tabindex="{{tabIndex}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
@click="{{_onclick}}"
@keydown="{{_onkeydown}}"
>
<div
class="ui5-rating-indicator-stars-wrapper"
>
{{#each _stars}}
{{#if this.selected}}
<div class="ui5-rating-indicator-icon ui5-rating-indicator-active-icon" data-value="{{this.index}}">&#9733;</div>
{{else if this.halfStar}}
<div class="ui5-rating-indicator-icon ui5-rating-indicator-half-icon" data-value="{{this.index}}">&#9734;</div>
{{else}}
<div class="ui5-rating-indicator-icon" data-value="{{this.index}}">&#9734;</div>
{{/if}}
{{/each}}
</div>
</div>
256 changes: 256 additions & 0 deletions packages/main/src/RatingIndicator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import {
isDown,
isUp,
isLeft,
isRight,
isSpace,
isEnter,
} from "@ui5/webcomponents-base/dist/Keys.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import Float from "@ui5/webcomponents-base/dist/types/Float.js";
import {
RATING_INDICATOR_TEXT,
} from "./generated/i18n/i18n-defaults.js";
import RatingIndicatorTemplate from "./generated/templates/RatingIndicatorTemplate.lit.js";

// Styles
import RatingIndicatorCss from "./generated/themes/RatingIndicator.css.js";

/**
* @public
*/
const metadata = {
tag: "ui5-rating-indicator",
properties: /** @lends sap.ui.webcomponents.main.RatingIndicator.prototype */ {

/**
* The indicated value of the rating
* <br><br>
* <b>Note:</b> If you set a number which is not round, it would be shown as follows:
* <ul>
* <li>1.0 - 1.2 -> 1</li>
* <li>1.3 - 1.7 -> 1.5</li>
* <li>1.8 - 1.9 -> 2</li>
* <ul>
* @type {Float}
* @defaultvalue 0
* @public
*/
value: {
type: Float,
defaultValue: 0,
},

/**
* The number of displayed rating symbols
* @type {Integer}
* @defaultvalue 5
* @public
*/
maxValue: {
type: Integer,
defaultValue: 5,
},

/**
* Defines whether the <code>ui5-rating-indicator</code> is disabled.
* @type {boolean}
* @defaultvalue false
* @public
*/
disabled: {
type: Boolean,
},

/**
* @type {boolean}
* @defaultvalue false
* @public
*/
readonly: {
type: Boolean,
},

/**
* @private
*/
_stars: {
type: Object,
multiple: true,
},

/**
* @private
*/
_focused: {
type: Boolean,
},
},
slots: /** @lends sap.ui.webcomponents.main.RatingIndicator.prototype */ {
//
},
events: /** @lends sap.ui.webcomponents.main.RatingIndicator.prototype */ {

/**
* The event is fired when the value changes.
*
* @event
* @public
*/
change: {},
},
};

/**
* @class
*
* <h3 class="comment-api-title">Overview</h3>
* The rating indicator is used to display a specific number of icons that are used to rate an item.
* Additionally, it is also used to display the average and overall ratings.
*
* <h3>Usage</h3>
* The preferred number of icons is between 5 and 7.
*
* <h3>Responsive Behavior</h3>
* You can change the size of the Rating Indicator by changing its <code>font-size</code> CSS property.
* <br>
* Example: <code><ui5-rating-indicator style="font-size: 3rem;"></ui5-rating-indicator></code>
*
* <h3>Usage</h3>
*
* For the <code>ui5-rating-indicator</code>
* <h3>ES6 Module Import</h3>
*
* <code>import @ui5/webcomponents/dist/RatingIndicator.js";</code>
*
* @constructor
* @author SAP SE
* @alias sap.ui.webcomponents.main.RatingIndicator
* @extends UI5Element
* @tagname ui5-rating-indicator
* @public
*/
class RatingIndicator extends UI5Element {
static get metadata() {
return metadata;
}

static get render() {
return litRender;
}

static get styles() {
return RatingIndicatorCss;
}

static get template() {
return RatingIndicatorTemplate;
}

static async onDefine() {
await Promise.all([
fetchI18nBundle("@ui5/webcomponents"),
]);
}

constructor() {
super();

this._liveValue = null; // stores the value to determine when to fire change
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
}

onBeforeRendering() {
this.calcState();
}

calcState() {
this._stars = [];

for (let i = 1; i < this.maxValue + 1; i++) {
const remainder = Math.round((this.value - Math.floor(this.value)) * 10);
let halfStar = false,
tempValue = this.value;

if (Math.floor(this.value) + 1 === i && remainder > 2 && remainder < 8) {
halfStar = true;
} else if (remainder <= 2) {
tempValue = Math.floor(this.value);
} else if (remainder >= 8) {
tempValue = Math.ceil(this.value);
}

this._stars.push({
selected: i <= tempValue,
index: i,
halfStar,
});
}
}

_onclick(event) {
if (this.disabled || this.readonly) {
return;
}

this.value = parseInt(event.target.getAttribute("data-value"));

if (this.value === 1 && this._liveValue === 1) {
this.value = 0;
}

if (this._liveValue !== this.value) {
this.fireEvent("change");
this._liveValue = this.value;
}
}

_onkeydown(event) {
if (this.disabled || this.readonly) {
return;
}

const down = isDown(event) || isLeft(event);
const up = isRight(event) || isUp(event) || isSpace(event) || isEnter(event);

if (down || up) {
event.preventDefault();

if (down && this.value > 0) {
this.value = Math.round(this.value - 1);
this.fireEvent("change");
} else if (up && this.value < this.maxValue) {
this.value = Math.round(this.value + 1);
this.fireEvent("change");
}
}
}

_onfocusin() {
if (this.disabled) {
return;
}

this._focused = true;
this._liveValue = this.value;
}

_onfocusout() {
this._focused = false;
}

get tabIndex() {
return this.disabled ? "-1" : "0";
}

get _ariaRoleDescription() {
return this.i18nBundle.getText(RATING_INDICATOR_TEXT);
}
}

RatingIndicator.define();

export default RatingIndicator;
3 changes: 3 additions & 0 deletions packages/main/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
@@ -265,6 +265,9 @@ MULTIINPUT_SHOW_MORE_TOKENS={0} More
#XTOL: Tooltip for panel expand title
PANEL_ICON=Expand/Collapse

#XBUT: Rating indicator aria-roledescription text
RATING_INDICATOR_TEXT=Rating Indicator

#XACT: ARIA description for the segmented button
SEGMENTEDBUTTON_ARIA_DESCRIPTION=Segmented button

48 changes: 48 additions & 0 deletions packages/main/src/themes/RatingIndicator.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
:host(:not([hidden])) {
display: inline-block;
font-size: 1.5rem;
cursor: pointer;
}

:host([disabled]) {
opacity: .6;
cursor: initial;
outline: none;
}

:host([readonly]) {
cursor: initial;
}

:host([_focused]) {
outline: 1px dotted var(--sapContent_FocusColor);
}

.ui5-rating-indicator-root {
outline: none;
}

.ui5-rating-indicator-icon {
position: relative;
color: var(--sapContent_UnratedColor);
user-select: none;
}

.ui5-rating-indicator-icon.ui5-rating-indicator-active-icon {
color: var(--sapContent_RatedColor);
}

.ui5-rating-indicator-icon.ui5-rating-indicator-half-icon:before {
content: "\2605";
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 100%;
color: var(--sapContent_RatedColor);
overflow: hidden;
}

.ui5-rating-indicator-stars-wrapper {
display: flex;
}
89 changes: 89 additions & 0 deletions packages/main/test/pages/RatingIndicator.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>

<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta charset="utf-8">

<title>Rating Indicator</title>
<script>
delete Document.prototype.adoptedStyleSheets
</script>

<script src="../../webcomponentsjs/webcomponents-loader.js"></script>
<script src="../../resources/bundle.esm.js" type="module"></script>
<script nomodule src="../../resources/bundle.es5.js"></script>

</head>

<body style="background-color: var(--sapBackgroundColor);">

<ui5-rating-indicator id="rating-indicator1"></ui5-rating-indicator>
<br>
<br>
<br>

<ui5-rating-indicator id="rating-indicator2" max-value="10" value="6"></ui5-rating-indicator>
<br>
<br>
<br>

<ui5-rating-indicator id="rating-indicator3" max-value="10" value="6"></ui5-rating-indicator>
<br>
<br>
<br>

<h3>test change event</h3>
<ui5-rating-indicator id="rating-indicator4" max-value="10" value="6"></ui5-rating-indicator>
<ui5-input value="0" id="change-event"></ui5-input>
<br>
<br>
<br>

<ui5-rating-indicator id="rating-indicator1" value="3.7"></ui5-rating-indicator>
<br>
<br>
<br>

<h3>readonly</h3>
<ui5-rating-indicator value="1" max-value="3" readonly></ui5-rating-indicator>
<br>
<br>
<br>

<h3>disabled</h3>
<ui5-rating-indicator value="3" disabled></ui5-rating-indicator>
<br>
<br>
<br>

<ui5-rating-indicator value="3" max-value="3"></ui5-rating-indicator>
<br>
<br>

<h3>sizes</h3>
<section>
<ui5-rating-indicator style="font-size: 1rem;"></ui5-rating-indicator>
<br>
<ui5-rating-indicator style="font-size: 2rem;"></ui5-rating-indicator>
<br>
<ui5-rating-indicator style="font-size: 3rem;"></ui5-rating-indicator>
<br>
<ui5-rating-indicator style="font-size: 4rem;"></ui5-rating-indicator>
<br>

</section>

<script>
var changeCount = 0;
var ratingIndicator4 = document.getElementById("rating-indicator4");
var changeEventInput = document.getElementById("change-event");

ratingIndicator4.addEventListener("change", event => {
changeEventInput.value = ++changeCount;
});
</script>
</body>

</html>
53 changes: 53 additions & 0 deletions packages/main/test/samples/RatingIndicator.sample.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<header>
<div class="control-header">Rating Indicator</div>
</header>
<style>
.snippet > ui5-rating-indicator {
margin: 0 1rem;
}
</style>

<div class="component-package">@ui5/webcomponents</div>

<div class="control-tag">&lt;ui5-rating-indicator&gt;</div>

<section>
<h3>Basic Rating Indicator</h3>
<div class="snippet">
<ui5-rating-indicator></ui5-rating-indicator>
<ui5-rating-indicator value="3"></ui5-rating-indicator>
</div>
<pre class="prettyprint lang-html"><xmp>
<ui5-rating-indicator></ui5-rating-indicator>
<ui5-rating-indicator value="3"></ui5-rating-indicator>
</xmp></pre>
</section>

<section>
<h3>Rating Indicator With Different Max Value</h3>
<div class="snippet">
<ui5-rating-indicator max-value="10" value="5"></ui5-rating-indicator>
<ui5-rating-indicator max-value="3" value="3"></ui5-rating-indicator>
</div>
<pre class="prettyprint lang-html"><xmp>
<ui5-rating-indicator max-value="10" value="5"></ui5-rating-indicator>
<ui5-rating-indicator max-value="3" value="3"></ui5-rating-indicator>
</xmp></pre>
</section>

<section>
<h3>Disabled Rating Indicator</h3>
<div class="snippet">
<ui5-rating-indicator value="4" disabled></ui5-rating-indicator>
<ui5-rating-indicator max-value="10" value="5" disabled></ui5-rating-indicator>
<ui5-rating-indicator max-value="3" value="3" disabled></ui5-rating-indicator>
</div>
<pre class="prettyprint lang-html"><xmp>
<ui5-rating-indicator value="4" disabled></ui5-rating-indicator>
<ui5-rating-indicator max-value="10" value="5" disabled></ui5-rating-indicator>
<ui5-rating-indicator max-value="3" value="3" disabled></ui5-rating-indicator>
</xmp></pre>
</section>

<!-- JSDoc marker -->

66 changes: 66 additions & 0 deletions packages/main/test/specs/RatingIndicator.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const assert = require("chai").assert;


describe("Rating Indicator general interaction", () => {
browser.url("http://localhost:8080/test-resources/pages/RatingIndicator.html");

it("Tests basic rating indicator rendering", () => {
const ratingIndicator = browser.$("#rating-indicator1");

assert.strictEqual(ratingIndicator.shadow$$(".ui5-rating-indicator-icon").length, 5, "Basic rating indicator renders 5 stars");
});

it("Tests max-value property", () => {
const ratingIndicator = browser.$("#rating-indicator2");

assert.strictEqual(ratingIndicator.shadow$$(".ui5-rating-indicator-icon").length, 10, "Basic rating indicator renders 5 stars");
});

it("Tests clicking on star", () => {
const ratingIndicator = browser.$("#rating-indicator3");
const thirdStar = ratingIndicator.shadow$$(".ui5-rating-indicator-icon")[2];

assert.strictEqual(ratingIndicator.getProperty("value"), 6, "Initial value is applied");

thirdStar.click();

assert.strictEqual(ratingIndicator.getProperty("value"), 3, "Value is changed on click");
});

it("Tests change event", () => {
const ratingIndicator = browser.$("#rating-indicator4");
const thirdStar = ratingIndicator.shadow$$(".ui5-rating-indicator-icon")[2];
const input = browser.$("#change-event");

assert.strictEqual(ratingIndicator.getProperty("value"), 6, "Initial value is applied");

thirdStar.click();

assert.strictEqual(ratingIndicator.getProperty("value"), 3, "Value is changed on click");

browser.keys("Enter");

assert.strictEqual(ratingIndicator.getProperty("value"), 4, "Value is changed on key press");

browser.keys("Space");

assert.strictEqual(ratingIndicator.getProperty("value"), 5, "Value is changed on key press");

browser.keys("ArrowUp");
browser.keys("ArrowRight");

assert.strictEqual(ratingIndicator.getProperty("value"), 7, "Value is changed on key press");

browser.keys("ArrowLeft");
browser.keys("ArrowLeft");
browser.keys("ArrowDown");
browser.keys("ArrowDown");
browser.keys("ArrowDown");
browser.keys("ArrowDown");
browser.keys("ArrowDown");

assert.strictEqual(ratingIndicator.getProperty("value"), 0, "Value is changed on key press");

assert.strictEqual(input.getProperty("value"), "12", "Input event is always fired")
});
});
2 changes: 2 additions & 0 deletions packages/playground/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -16,7 +16,9 @@ GEM
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
eventmachine (1.2.7)
eventmachine (1.2.7-x64-mingw32)
ffi (1.11.1)
ffi (1.11.1-x64-mingw32)
forwardable-extended (2.6.0)
http_parser.rb (0.6.0)
3 changes: 2 additions & 1 deletion packages/playground/build-scripts/samples-prepare.js
Original file line number Diff line number Diff line change
@@ -17,7 +17,8 @@ const newComponents = [
"NotificationListItem",
"NotificationListGroupItem",
"UploadCollection",
"Tree"
"Tree",
"RatingIndicator",
];

packages.forEach(package => {
2 changes: 2 additions & 0 deletions packages/theme-base/css-vars-usage.json
Original file line number Diff line number Diff line change
@@ -75,12 +75,14 @@
"--sapContent_ImagePlaceholderForegroundColor",
"--sapContent_LabelColor",
"--sapContent_NonInteractiveIconColor",
"--sapContent_RatedColor",
"--sapContent_Selected_Background",
"--sapContent_Selected_TextColor",
"--sapContent_Shadow0",
"--sapContent_Shadow1",
"--sapContent_Shadow2",
"--sapContent_TextShadow",
"--sapContent_UnratedColor",
"--sapCriticalColor",
"--sapCriticalElementColor",
"--sapCriticalTextColor",

0 comments on commit a28f201

Please sign in to comment.