diff --git a/packages/main/cypress/specs/RatingIndicator.cy.tsx b/packages/main/cypress/specs/RatingIndicator.cy.tsx index c0eafc8ab876..6af958374c92 100644 --- a/packages/main/cypress/specs/RatingIndicator.cy.tsx +++ b/packages/main/cypress/specs/RatingIndicator.cy.tsx @@ -1,39 +1,116 @@ import RatingIndicator from "../../src/RatingIndicator.js"; import { RATING_INDICATOR_ARIA_DESCRIPTION } from "../../src/generated/i18n/i18n-defaults.js"; +import "@ui5/webcomponents-icons/dist/heart.js"; +import "@ui5/webcomponents-icons/dist/heart-2.js"; +import "@ui5/webcomponents-icons/dist/thumb-up.js"; +import "@ui5/webcomponents-icons/dist/thumb-down.js"; describe("RatingIndicator", () => { - describe("Half Icon appearance", () => { - it("Half icon should be filled when rating indicator is disabled", () => { - const attributeValue = "favorite"; + describe("Custom Icons", () => { + it("should render default icons when no custom icons are specified", () => { + cy.mount(); - cy.mount(); + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-sel [ui5-icon]") + .first() + .should("have.attr", "name", "favorite"); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-unsel [ui5-icon]") + .first() + .should("have.attr", "name", "unfavorite"); + }); + + it("should render custom icons for selected and unselected states", () => { + cy.mount(); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-sel [ui5-icon]") + .first() + .should("have.attr", "name", "heart"); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-unsel [ui5-icon]") + .first() + .should("have.attr", "name", "heart-2"); + }); + + it("should render custom icon with default unselected icon when only iconSelected is specified", () => { + cy.mount(); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-sel [ui5-icon]") + .first() + .should("have.attr", "name", "thumb-up"); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-unsel [ui5-icon]") + .first() + .should("have.attr", "name", "unfavorite"); + }); + + it("should render custom unselected icon with default selected icon when only iconUnselected is specified", () => { + cy.mount(); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-sel [ui5-icon]") + .first() + .should("have.attr", "name", "favorite"); cy.get("[ui5-rating-indicator]") .shadow() - .find(".ui5-rating-indicator-item-half [ui5-icon]") - .should("have.attr", "name", attributeValue); + .find(".ui5-rating-indicator-item-unsel [ui5-icon]") + .first() + .should("have.attr", "name", "thumb-down"); }); - it("Half icon should be filled when rating indicator is readonly", () => { - const attributeValue = "favorite"; + it("should render custom icons in half-star state", () => { + cy.mount(); - cy.mount(); + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-half .ui5-rating-indicator-half-icon-left [ui5-icon]") + .should("have.attr", "name", "heart"); cy.get("[ui5-rating-indicator]") .shadow() - .find(".ui5-rating-indicator-item-half [ui5-icon]") - .should("have.attr", "name", attributeValue); + .find(".ui5-rating-indicator-item-half .ui5-rating-indicator-half-icon-right [ui5-icon]") + .should("have.attr", "name", "heart-2"); }); - it("Half icon should be border only when rating indicator is regular", () => { - const attributeValue = "unfavorite"; + it("should render custom icon (filled) in half-star state when readonly", () => { + cy.mount(); - cy.mount(); + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-half .ui5-rating-indicator-half-icon-left [ui5-icon]") + .should("have.attr", "name", "heart"); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-half .ui5-rating-indicator-half-icon-right [ui5-icon]") + .should("have.attr", "name", "heart"); + }); + + it("should render custom icon (filled) in half-star state when disabled", () => { + cy.mount(); + + cy.get("[ui5-rating-indicator]") + .shadow() + .find(".ui5-rating-indicator-item-half .ui5-rating-indicator-half-icon-left [ui5-icon]") + .should("have.attr", "name", "heart"); cy.get("[ui5-rating-indicator]") .shadow() - .find(".ui5-rating-indicator-item-half [ui5-icon]") - .should("have.attr", "name", attributeValue); + .find(".ui5-rating-indicator-item-half .ui5-rating-indicator-half-icon-right [ui5-icon]") + .should("have.attr", "name", "heart"); }); }); diff --git a/packages/main/src/RatingIndicator.ts b/packages/main/src/RatingIndicator.ts index f9f2793f1831..976dee370ed9 100644 --- a/packages/main/src/RatingIndicator.ts +++ b/packages/main/src/RatingIndicator.ts @@ -177,6 +177,25 @@ class RatingIndicator extends UI5Element { @property() tooltip?: string; + /** + * Defines the icon to be displayed for the selected (filled) rating symbol. + * + * @default "favorite" + * @public + * @since 2.20 + */ + @property() + iconSelected?: string; + + /** + * Defines the icon to be displayed for the unselected (empty) rating symbol. + * @default "unfavorite" + * @public + * @since 2.20 + */ + @property() + iconUnselected?: string; + /** * @private */ @@ -341,6 +360,14 @@ class RatingIndicator extends UI5Element { get ariaReadonly() { return this.readonly ? "true" : undefined; } + + get effectiveIconSelected() { + return this.iconSelected || "favorite"; + } + + get effectiveIconUnselected() { + return this.iconUnselected || "unfavorite"; + } } RatingIndicator.define(); diff --git a/packages/main/src/RatingIndicatorTemplate.tsx b/packages/main/src/RatingIndicatorTemplate.tsx index 80fd0aa8f4bb..d5843ec80a8f 100644 --- a/packages/main/src/RatingIndicatorTemplate.tsx +++ b/packages/main/src/RatingIndicatorTemplate.tsx @@ -1,8 +1,6 @@ import type RatingIndicator from "./RatingIndicator.js"; import type { Star } from "./RatingIndicator.js"; import Icon from "./Icon.js"; -import favorite from "@ui5/webcomponents-icons/dist/favorite.js"; -import unfavorite from "@ui5/webcomponents-icons/dist/unfavorite.js"; export default function RatingIndicatorTemplate(this: RatingIndicator) { return ( @@ -37,38 +35,40 @@ function starLi(this: RatingIndicator, star: Star) { if (star.selected) { return (
  • - +
  • ); } if (star.halfStar) { return (
  • - -
    - +
    + +
    +
    +
  • ); } if (this.readonly) { return (
  • - +
  • ); } if (this.disabled) { return (
  • - +
  • ); } return (
  • - +
  • ); } function halfStarIconName(this: RatingIndicator) { - return this.disabled || this.readonly ? favorite : unfavorite; + return this.disabled || this.readonly ? this.effectiveIconSelected : this.effectiveIconUnselected; } diff --git a/packages/main/src/themes/RatingIndicator.css b/packages/main/src/themes/RatingIndicator.css index 30e759f2691c..f587d87b7a0b 100644 --- a/packages/main/src/themes/RatingIndicator.css +++ b/packages/main/src/themes/RatingIndicator.css @@ -91,21 +91,35 @@ } .ui5-rating-indicator-item.ui5-rating-indicator-item-half { - color: var(--sapContent_UnratedColor); + display: flex; + flex-direction: row; +} + +.ui5-rating-indicator-half-icon-wrapper { + width: 50%; + height: 100%; + overflow: hidden; + position: relative; } -.ui5-rating-indicator-item [ui5-icon].ui5-rating-indicator-half-icon { +.ui5-rating-indicator-half-icon-wrapper [ui5-icon] { position: absolute; - inset-inline-start: 50%; + width: var(--_ui5_rating_indicator_item_width); + height: var(--_ui5_rating_indicator_item_height); +} + +.ui5-rating-indicator-half-icon-left { color: var(--sapContent_RatedColor); } -.ui5-rating-indicator-half-icon-wrapper { - width: 100%; - height: 100%; - position: absolute; - inset-inline-start: -50%; - top: 0; - z-index: 32; - overflow: hidden; -} \ No newline at end of file +.ui5-rating-indicator-half-icon-left [ui5-icon] { + inset-inline-start: 0; +} + +.ui5-rating-indicator-half-icon-right { + color: var(--sapContent_UnratedColor); +} + +.ui5-rating-indicator-half-icon-right [ui5-icon] { + inset-inline-end: 0; +} diff --git a/packages/main/test/pages/RatingIndicator.html b/packages/main/test/pages/RatingIndicator.html index 700648e3db52..f9b4568f774c 100644 --- a/packages/main/test/pages/RatingIndicator.html +++ b/packages/main/test/pages/RatingIndicator.html @@ -134,6 +134,32 @@

    sizes


    + +

    Custom Icons - Hearts

    + +
    +
    + +

    Custom Icons - Thumbs Up

    + +
    +
    + +

    Custom Icons - Hearts (readonly)

    + +
    +
    + +

    Custom Icons - Hearts (disabled)

    + +
    +
    + +

    Custom Icons - Only selected icon (circles)

    + +
    +
    + + + + + \ No newline at end of file diff --git a/packages/website/docs/_samples/main/RatingIndicator/Sizes/main.js b/packages/website/docs/_samples/main/RatingIndicator/Sizes/main.js index a4061d2ad8d7..6f7164a938a9 100644 --- a/packages/website/docs/_samples/main/RatingIndicator/Sizes/main.js +++ b/packages/website/docs/_samples/main/RatingIndicator/Sizes/main.js @@ -1 +1,3 @@ -import "@ui5/webcomponents/dist/RatingIndicator.js"; \ No newline at end of file +import "@ui5/webcomponents/dist/RatingIndicator.js"; +import "@ui5/webcomponents-icons/dist/favorite.js"; +import "@ui5/webcomponents-icons/dist/unfavorite.js"; \ No newline at end of file