From cc28c5065a3395c5e6a400514b532e73535bf06b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ula=C5=9F=20Turan?=
 <56156254+ulasturann@users.noreply.github.com>
Date: Wed, 7 Sep 2022 11:36:51 +0300
Subject: [PATCH] Fixed #3219 - Rating Component with custom icons. (#3248)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: add new attributes to rating component.(onIcon, offIcon, onIconProps, offIconProps, cancelIcon, cancelIconProps).
fix: wrong value on the rating docs.
fix: typo on the rating demo page.

* build: add missing attributes on the api generator/rating.

* fix: typo on Rating Demo page.

Co-authored-by: Ulaş Turan <ulasturan@Ulas-MacBook-Pro.local>
---
 api-generator/components/rating.js  |  36 +++++
 components/doc/rating/index.js      | 201 ++++++++++++++++++++--------
 components/lib/rating/Rating.css    |   5 +
 components/lib/rating/Rating.js     |  36 +++--
 components/lib/rating/rating.d.ts   |   8 +-
 pages/rating/cancel.png             | Bin 0 -> 1850 bytes
 pages/rating/custom-icon-active.png | Bin 0 -> 4157 bytes
 pages/rating/custom-icon.png        | Bin 0 -> 1922 bytes
 pages/rating/index.js               |  16 ++-
 9 files changed, 233 insertions(+), 69 deletions(-)
 create mode 100644 pages/rating/cancel.png
 create mode 100644 pages/rating/custom-icon-active.png
 create mode 100644 pages/rating/custom-icon.png

diff --git a/api-generator/components/rating.js b/api-generator/components/rating.js
index 043b562291..c0850df133 100644
--- a/api-generator/components/rating.js
+++ b/api-generator/components/rating.js
@@ -35,6 +35,18 @@ const RatingProps = [
         default: 'true',
         description: 'When specified a cancel icon is displayed to allow removing the value.'
     },
+    {
+        name: 'cancelIcon',
+        type: 'string',
+        default: 'pi pi-ban',
+        description: 'ClassName of the cancel icon component.'
+    },
+    {
+        name: 'cancelIconProps',
+        type: 'object',
+        default: 'null',
+        description: 'Properties of the cancel icon.'
+    },
     {
         name: 'style',
         type: 'object',
@@ -47,6 +59,30 @@ const RatingProps = [
         default: 'null',
         description: 'ClassName of the component.'
     },
+    {
+        name: 'onIcon',
+        type: 'string',
+        default: 'pi pi-star-fill',
+        description: 'ClassName of the icon on component.'
+    },
+    {
+        name: 'offIcon',
+        type: 'string',
+        default: 'pi pi-star',
+        description: 'ClassName of the icon off component.'
+    },
+    {
+        name: 'onIconProps',
+        type: 'object',
+        default: 'null',
+        description: 'Properties of the on icon.'
+    },
+    {
+        name: 'offIconProps',
+        type: 'object',
+        default: 'null',
+        description: 'Properties of the off icon.'
+    },
     {
         name: 'tooltip',
         type: 'any',
diff --git a/components/doc/rating/index.js b/components/doc/rating/index.js
index 7f2ca44eb0..f78207b97a 100644
--- a/components/doc/rating/index.js
+++ b/components/doc/rating/index.js
@@ -12,6 +12,10 @@ const RatingDoc = memo(() => {
             content: `
 import React, { Component } from 'react';
 import { Rating } from 'primereact/rating';
+import * as CustomImage from './custom-icon.png';
+import * as CustomImageActive from './custom-icon-active.png';
+import * as CustomCancelImage from './cancel.png';
+import Image from 'next/image';
 
 export class RatingDemo extends Component {
 
@@ -20,26 +24,32 @@ export class RatingDemo extends Component {
 
         this.state = {
             val1: null,
-            val2: null
+            val2: null,
+            val3: null
         };
     }
 
     render() {
         return (
             <div>
-                <div className="card">
-                    <h5>Basic {this.state.val1}</h5>
-                    <Rating value={this.state.val1} onChange={(e) => this.setState({val1: e.value})} />
-
-                    <h5>Without Cancel</h5>
-                    <Rating value={this.state.val2} cancel={false} onChange={(e) => this.setState({val2: e.value})} />
-
-                    <h5>ReadOnly</h5>
-                    <Rating value={5} readOnly stars={10} cancel={false} />
-
-                    <h5>Disabled</h5>
-                    <Rating value={8} disabled stars={10} />
-                </div>
+            <div className="card">
+            <h5>Basic {this.state.val1}</h5>
+            <Rating value={this.state.val1} onChange={(e) => this.setState({val1: e.value})} />
+            <h5>Without Cancel</h5>
+            <Rating value={this.state.val2} cancel={false} onChange={(e) => this.setState({val2: e.value})} />
+            <h5>ReadOnly</h5>
+            <Rating value={5} readOnly stars={10} cancel={false} />
+            <h5>Disabled</h5>
+            <Rating value={8} disabled stars={10} />
+            <h5>Customization</h5>
+            <Rating
+                value={this.state.val3}
+                onIcon={<Image src={CustomImageActive} alt="custom-image-active" width="30px" height="30px" />}
+                offIcon={<Image src={CustomImage} alt="custom-image" width="30px" height="30px" />}
+                onChange={(e) => this.setState({val3: e.value})}
+                cancelIcon={<Image src={CustomCancelImage} alt="custom-cancel-image" width="30px" height="30px" />}
+            />
+        </div>
             </div>
         )
     }
@@ -51,26 +61,36 @@ export class RatingDemo extends Component {
             content: `
 import React, { useState } from 'react';
 import { Rating } from 'primereact/rating';
+import * as CustomImage from './custom-icon.png';
+import * as CustomImageActive from './custom-icon-active.png';
+import * as CustomCancelImage from './cancel.png';
+import Image from 'next/image';
 
 const RatingDemo = () => {
     const [val1, setVal1] = useState(null);
     const [val2, setVal2] = useState(null);
+    const [val3, setVal3] = useState(null);
 
     return (
         <div>
-            <div className="card">
-                <h5>Basic {val1}</h5>
-                <Rating value={val1} onChange={(e) => setVal1(e.value)} />
-
-                <h5>Without Cancel</h5>
-                <Rating value={val2} cancel={false} onChange={(e) => setVal2(e.value)} />
-
-                <h5>ReadOnly</h5>
-                <Rating value={5} readOnly stars={10} cancel={false} />
-
-                <h5>Disabled</h5>
-                <Rating value={8} disabled stars={10} />
-            </div>
+        <div className="card">
+        <h5>Basic {val1}</h5>
+        <Rating value={val1} onChange={(e) => setVal1(e.value)} />
+        <h5>Without Cancel</h5>
+        <Rating value={val2} cancel={false} onChange={(e) => setVal2(e.value)} />
+        <h5>ReadOnly</h5>
+        <Rating value={5} readOnly stars={10} cancel={false} />
+        <h5>Disabled</h5>
+        <Rating value={8} disabled stars={10} />
+        <h5>Customization</h5>
+        <Rating
+            value={val3}
+            onIcon={<Image src={CustomImageActive} alt="custom-image-active" width="30px" height="30px" />}
+            offIcon={<Image src={CustomImage} alt="custom-image" width="30px" height="30px" />}
+            onChange={(e) => setVal3(e.value)}
+            cancelIcon={<Image src={CustomCancelImage} alt="custom-cancel-image" width="30px" height="30px" />}
+        />
+    </div>
         </div>
     )
 }
@@ -81,26 +101,35 @@ const RatingDemo = () => {
             content: `
 import React, { useState } from 'react';
 import { Rating } from 'primereact/rating';
+import * as CustomImage from './custom-icon.png';
+import * as CustomImageActive from './custom-icon-active.png';
+import * as CustomCancelImage from './cancel.png';
+import Image from 'next/image';
 
 const RatingDemo = () => {
     const [val1, setVal1] = useState(null);
     const [val2, setVal2] = useState(null);
+    const [val3, setVal3] = useState(null);
 
     return (
         <div>
-            <div className="card">
-                <h5>Basic {val1}</h5>
-                <Rating value={val1} onChange={(e) => setVal1(e.value)} />
-
-                <h5>Without Cancel</h5>
-                <Rating value={val2} cancel={false} onChange={(e) => setVal2(e.value)} />
-
-                <h5>ReadOnly</h5>
-                <Rating value={5} readOnly stars={10} cancel={false} />
-
-                <h5>Disabled</h5>
-                <Rating value={8} disabled stars={10} />
-            </div>
+        <h5>Basic {val1}</h5>
+        <Rating value={val1} onChange={(e) => setVal1(e.value)} />
+        <h5>Without Cancel</h5>
+        <Rating value={val2} cancel={false} onChange={(e) => setVal2(e.value)} />
+        <h5>ReadOnly</h5>
+        <Rating value={5} readOnly stars={10} cancel={false} />
+        <h5>Disabled</h5>
+        <Rating value={8} disabled stars={10} />
+        <h5>Customization</h5>
+        <Rating
+            value={val3}
+            onIcon={<Image src={CustomImageActive} alt="custom-image-active" width="30px" height="30px" />}
+            offIcon={<Image src={CustomImage} alt="custom-image" width="30px" height="30px" />}
+            onChange={(e) => setVal3(e.value)}
+            cancelIcon={<Image src={CustomCancelImage} alt="custom-cancel-image" width="30px" height="30px" />}
+        />
+    </div>
         </div>
     )
 }
@@ -114,26 +143,35 @@ const RatingDemo = () => {
             content: `
 const { useState } = React;
 const { Rating } = primereact.rating;
+import * as CustomImage from './custom-icon.png';
+import * as CustomImageActive from './custom-icon-active.png';
+import * as CustomCancelImage from './cancel.png';
+import Image from 'next/image';
 
 const RatingDemo = () => {
     const [val1, setVal1] = useState(null);
     const [val2, setVal2] = useState(null);
+    const [val3, setVal3] = useState(null);
 
     return (
         <div>
-            <div className="card">
-                <h5>Basic {val1}</h5>
-                <Rating value={val1} onChange={(e) => setVal1(e.value)} />
-
-                <h5>Without Cancel</h5>
-                <Rating value={val2} cancel={false} onChange={(e) => setVal2(e.value)} />
-
-                <h5>ReadOnly</h5>
-                <Rating value={5} readOnly stars={10} cancel={false} />
-
-                <h5>Disabled</h5>
-                <Rating value={8} disabled stars={10} />
-            </div>
+        <h5>Basic {val1}</h5>
+        <Rating value={val1} onChange={(e) => setVal1(e.value)} />
+        <h5>Without Cancel</h5>
+        <Rating value={val2} cancel={false} onChange={(e) => setVal2(e.value)} />
+        <h5>ReadOnly</h5>
+        <Rating value={5} readOnly stars={10} cancel={false} />
+        <h5>Disabled</h5>
+        <Rating value={8} disabled stars={10} />
+        <h5>Customization</h5>
+        <Rating
+            value={val3}
+            onIcon={<Image src={CustomImageActive} alt="custom-image-active" width="30px" height="30px" />}
+            offIcon={<Image src={CustomImage} alt="custom-image" width="30px" height="30px" />}
+            onChange={(e) => setVal3(e.value)}
+            cancelIcon={<Image src={CustomCancelImage} alt="custom-cancel-image" width="30px" height="30px" />}
+        />
+    </div>
         </div>
     )
 }
@@ -184,12 +222,29 @@ import { Rating } from 'primereact/rating';
 
                     <h5>Cancel</h5>
                     <p>
-                        A cancel icon is displayed to reset the value by default, set <i>cancel</i> as false to remove this option.
+                        A cancel icon is displayed to reset the value by default, set <i>cancel</i> as false to remove this option, default is <span style={{ color: '#CC7E09' }}>'pi pi-ban'</span>.{' '}
                     </p>
 
                     <CodeHighlight>
                         {`
-<Rating value={value} onChange={(e) => setValue(e.value)} cancel={5} />
+<Rating value={value} onChange={(e) => setValue(e.value)} cancel={false} />
+`}
+                    </CodeHighlight>
+
+                    <h5>Custom Icons</h5>
+                    <p>
+                        Custom icons are used to override the default icons with these properties;
+                        <i>onIcon</i>
+                        <i>offIcon</i>
+                        <i>onIconClass</i>
+                        <i>offIconClass</i>
+                        <i>cancelIcon</i>
+                        <i>cancelIconProps</i>, default is <span style={{ color: '#CC7E09' }}>'pi pi-star'</span>.
+                    </p>
+
+                    <CodeHighlight>
+                        {`
+<Rating value={value} onIcon={<Image src={CustomImageActive} />} offIcon={<Image src={CustomImage} />} cancelIcon={<Image src={CustomCancelImage} />} onChange={(e) => setValue(e.value)} />
 `}
                     </CodeHighlight>
 
@@ -242,6 +297,18 @@ import { Rating } from 'primereact/rating';
                                     <td>true</td>
                                     <td>When specified a cancel icon is displayed to allow removing the value.</td>
                                 </tr>
+                                <tr>
+                                    <td>cancelIcon</td>
+                                    <td>string</td>
+                                    <td>pi pi-ban</td>
+                                    <td>ClassName of the cancel icon component.</td>
+                                </tr>
+                                <tr>
+                                    <td>cancelIconProps</td>
+                                    <td>object</td>
+                                    <td>null</td>
+                                    <td>Properties of the cancel icon.</td>
+                                </tr>
                                 <tr>
                                     <td>style</td>
                                     <td>object</td>
@@ -254,6 +321,30 @@ import { Rating } from 'primereact/rating';
                                     <td>null</td>
                                     <td>ClassName of the component.</td>
                                 </tr>
+                                <tr>
+                                    <td>onIcon</td>
+                                    <td>string</td>
+                                    <td>pi pi-star-fill</td>
+                                    <td>ClassName of the on icon component.</td>
+                                </tr>
+                                <tr>
+                                    <td>offIcon</td>
+                                    <td>string</td>
+                                    <td>pi pi-star</td>
+                                    <td>ClassName of the off icon component.</td>
+                                </tr>
+                                <tr>
+                                    <td>onIconProps</td>
+                                    <td>object</td>
+                                    <td>null</td>
+                                    <td>Properties of the on icon.</td>
+                                </tr>
+                                <tr>
+                                    <td>offIconProps</td>
+                                    <td>object</td>
+                                    <td>null</td>
+                                    <td>Properties of the off icon.</td>
+                                </tr>
                                 <tr>
                                     <td>tooltip</td>
                                     <td>any</td>
diff --git a/components/lib/rating/Rating.css b/components/lib/rating/Rating.css
index 181ee3368a..9898341dba 100644
--- a/components/lib/rating/Rating.css
+++ b/components/lib/rating/Rating.css
@@ -1,3 +1,8 @@
+.p-rating {
+    display: flex;
+    align-items: center;
+}
+
 .p-rating-icon {
     cursor: pointer;
 }
diff --git a/components/lib/rating/Rating.js b/components/lib/rating/Rating.js
index 4bda311aa4..3399ac86a1 100644
--- a/components/lib/rating/Rating.js
+++ b/components/lib/rating/Rating.js
@@ -1,6 +1,6 @@
 import * as React from 'react';
 import { Tooltip } from '../tooltip/Tooltip';
-import { classNames, ObjectUtils } from '../utils/Utils';
+import { classNames, IconUtils, ObjectUtils } from '../utils/Utils';
 
 export const Rating = React.memo(
     React.forwardRef((props, ref) => {
@@ -56,20 +56,26 @@ export const Rating = React.memo(
             }
         };
 
-        const createStars = () => {
+        const createIcons = () => {
             return Array.from({ length: props.stars }, (_, i) => i + 1).map((value) => {
-                const iconClassName = classNames('p-rating-icon', {
-                    'pi pi-star': !props.value || value > props.value,
-                    'pi pi-star-fill': value <= props.value
-                });
-
-                return <span className={iconClassName} onClick={(e) => rate(e, value)} key={value} tabIndex={tabIndex} onKeyDown={(e) => onStarKeyDown(e, value)}></span>;
+                const icon = value <= props.value ? { class: props.onIcon, props: props.onIconProps } : { class: props.offIcon, props: props.offIconProps };
+                const content = IconUtils.getJSXIcon(icon.class, { ...icon.props });
+                return (
+                    <span key={value} className="p-rating-icon" tabIndex={tabIndex} onClick={(e) => rate(e, value)} onKeyDown={(e) => onStarKeyDown(e, value)}>
+                        {content}
+                    </span>
+                );
             });
         };
 
         const createCancelIcon = () => {
             if (props.cancel) {
-                return <span className="p-rating-icon p-rating-cancel pi pi-ban" onClick={clear} tabIndex={tabIndex} onKeyDown={onCancelKeyDown}></span>;
+                const content = IconUtils.getJSXIcon(props.cancelIcon, { ...props.cancelIconProps });
+                return (
+                    <span className="p-rating-icon" onClick={clear} tabIndex={tabIndex} onKeyDown={onCancelKeyDown}>
+                        {content}
+                    </span>
+                );
             }
 
             return null;
@@ -91,13 +97,13 @@ export const Rating = React.memo(
             props.className
         );
         const cancelIcon = createCancelIcon();
-        const stars = createStars();
+        const icons = createIcons();
 
         return (
             <>
                 <div ref={elementRef} id={props.id} className={className} style={props.style} {...otherProps}>
                     {cancelIcon}
-                    {stars}
+                    {icons}
                 </div>
                 {hasTooltip && <Tooltip target={elementRef} content={props.tooltip} {...props.tooltipOptions} />}
             </>
@@ -118,5 +124,11 @@ Rating.defaultProps = {
     className: null,
     tooltip: null,
     tooltipOptions: null,
-    onChange: null
+    onChange: null,
+    onIcon: 'pi pi-star-fill',
+    offIcon: 'pi pi-star',
+    cancelIcon: 'p-rating-icon p-rating-cancel pi pi-ban',
+    cancelIconProps: null,
+    onIconProps: null,
+    offIconProps: null
 };
diff --git a/components/lib/rating/rating.d.ts b/components/lib/rating/rating.d.ts
index 9ce0578aec..9b9804d1ec 100644
--- a/components/lib/rating/rating.d.ts
+++ b/components/lib/rating/rating.d.ts
@@ -1,6 +1,6 @@
 import * as React from 'react';
 import TooltipOptions from '../tooltip/tooltipoptions';
-
+import { IconType } from '../utils';
 interface RatingChangeTargetOptions {
     name: string;
     id: string;
@@ -25,6 +25,12 @@ export interface RatingProps extends Omit<React.DetailedHTMLProps<React.InputHTM
     tooltipOptions?: TooltipOptions;
     onChange?(e: RatingChangeParams): void;
     children?: React.ReactNode;
+    onIcon?: IconType<RatingProps>;
+    offIcon?: IconType<RatingProps>;
+    cancelIcon: IconType<RatingProps>;
+    cancelIconProps: React.HTMLAttributes<HTMLSpanElement>;
+    onIconProps?: React.HTMLAttributes<HTMLSpanElement>;
+    offIconProps?: React.HTMLAttributes<HTMLSpanElement>;
 }
 
 export declare class Rating extends React.Component<RatingProps, any> {
diff --git a/pages/rating/cancel.png b/pages/rating/cancel.png
new file mode 100644
index 0000000000000000000000000000000000000000..8482598af6f1d00188f7df68de6fee784d0e0027
GIT binary patch
literal 1850
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

literal 0
HcmV?d00001

diff --git a/pages/rating/custom-icon-active.png b/pages/rating/custom-icon-active.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca38b0d290264d6bb588d814d9b91bd57eb9c82b
GIT binary patch
literal 4157
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
Hu0mjfq>QM-

literal 0
HcmV?d00001

diff --git a/pages/rating/custom-icon.png b/pages/rating/custom-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..7047c28f4e4ad63775ab9f9c3ed242d1834faea6
GIT binary patch
literal 1922
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
IM6N<$g7hHT?*IS*

literal 0
HcmV?d00001

diff --git a/pages/rating/index.js b/pages/rating/index.js
index 85f3c2e2a2..ead7cb20f8 100644
--- a/pages/rating/index.js
+++ b/pages/rating/index.js
@@ -3,16 +3,21 @@ import { Rating } from '../../components/lib/rating/Rating';
 import RatingDoc from '../../components/doc/rating';
 import { DocActions } from '../../components/doc/common/docactions';
 import Head from 'next/head';
+import * as CustomImage from './custom-icon.png';
+import * as CustomImageActive from './custom-icon-active.png';
+import * as CustomCancelImage from './cancel.png';
+import Image from 'next/image';
 
 const RatingDemo = () => {
     const [val1, setVal1] = useState(null);
     const [val2, setVal2] = useState(null);
+    const [val3, setVal3] = useState(null);
 
     return (
         <div>
             <Head>
                 <title>React Rating Component</title>
-                <meta name="description" content="Rating componentsis a star based selection input." />
+                <meta name="description" content="Rating component is a star based selection input." />
             </Head>
             <div className="content-section introduction">
                 <div className="feature-intro">
@@ -36,6 +41,15 @@ const RatingDemo = () => {
 
                     <h5>Disabled</h5>
                     <Rating value={8} disabled stars={10} />
+
+                    <h5>Customization</h5>
+                    <Rating
+                        value={val3}
+                        onIcon={<Image src={CustomImageActive} alt="custom-image-active" width="30px" height="30px" />}
+                        offIcon={<Image src={CustomImage} alt="custom-image" width="30px" height="30px" />}
+                        onChange={(e) => setVal3(e.value)}
+                        cancelIcon={<Image src={CustomCancelImage} alt="custom-cancel-image" width="30px" height="30px" />}
+                    />
                 </div>
             </div>