From f6ab024ca606a5f3c59732df02c933063187124a Mon Sep 17 00:00:00 2001 From: Artur Santiago Date: Thu, 13 Feb 2025 16:53:59 -0300 Subject: [PATCH] feat: add rating field (#2636) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >⚠️ THIS PR DEPENDS ON [PR#2635](https://github.com/vtex/faststore/pull/2635) ⚠️ ## What's the purpose of this pull request? This pull request adds the RatingField component, which will initially be used in the modal for adding reviews to a product. ## How it works? This PR creates a new molecule component called `RatingField`. In addition to the "input" fields for the actionable rating, it can also receive an `id`, `label`, and `error` via props to be displayed in the form. The component uses the `Rating` component with the `actionable` view, passing the `onChange` handler to it. ## How to test it? ### Starters Deploy Preview [Preview](https://starter-git-feat-rating-field-vtex.vercel.app//review-and-ratings-playground) ## References [JIRA TASK: SFS-2078](https://vtex-dev.atlassian.net/browse/SFS-2078) [JIRA TASK: SFS-2077](https://vtex-dev.atlassian.net/browse/SFS-2077) [Figma](https://www.figma.com/design/YXU6IX2htN2yg7udpCumZv/Reviews-%26-Ratings?node-id=131-49445&t=iOanfcOwmDa4bolf-4) ![image](https://github.com/user-attachments/assets/56da1a26-1764-489c-90e9-2d81275715c3) ![image](https://github.com/user-attachments/assets/8351c008-3fca-4e36-ad6d-30518e005dbb) --------- Co-authored-by: Fanny Chien Co-authored-by: Fanny Chien --- packages/components/src/index.ts | 2 + .../src/molecules/RatingField/RatingField.tsx | 71 +++++++++++++++++++ .../src/molecules/RatingField/index.ts | 2 + .../components/molecules/Rating/styles.scss | 5 ++ .../molecules/RatingField/styles.scss | 44 ++++++++++++ packages/ui/src/styles/components.scss | 1 + 6 files changed, 125 insertions(+) create mode 100644 packages/components/src/molecules/RatingField/RatingField.tsx create mode 100644 packages/components/src/molecules/RatingField/index.ts create mode 100644 packages/ui/src/components/molecules/RatingField/styles.scss diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 3dde605808..fcae5e5602 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -142,6 +142,8 @@ export { default as RadioGroup, RadioOption } from './molecules/RadioGroup' export type { RadioGroupProps, RadioOptionProps } from './molecules/RadioGroup' export { default as Rating } from './molecules/Rating' export type { RatingProps } from './molecules/Rating' +export { default as RatingField } from './molecules/RatingField' +export type { RatingFieldProps } from './molecules/RatingField' export { default as RegionBar } from './molecules/RegionBar' export type { RegionBarProps } from './molecules/RegionBar' diff --git a/packages/components/src/molecules/RatingField/RatingField.tsx b/packages/components/src/molecules/RatingField/RatingField.tsx new file mode 100644 index 0000000000..0d2e5a8ed5 --- /dev/null +++ b/packages/components/src/molecules/RatingField/RatingField.tsx @@ -0,0 +1,71 @@ +import React from 'react' +import type { MutableRefObject } from 'react' +import { Label, Rating, type RatingProps } from '../..' + +interface DefaultProps { + /** + * ID to find this component in testing tools (e.g.: cypress, testing library, and jest). + */ + testId?: string + /** + * ID to identify input and corresponding label. + */ + id: string + /** + * The text displayed to identify the input rating. + */ + label: string + /** + * The error message displayed when an error occurs. + */ + error?: string + /** + * Component's ref. + */ + ratingRef?: MutableRefObject +} + +export type RatingFieldProps = DefaultProps & RatingProps + +const RatingField = ({ + id, + label, + error, + disabled, + length = 5, + value = 0, + onChange, + ratingRef, + testId = 'fs-rating-field', + ...otherProps +}: RatingFieldProps) => { + const shouldDisplayError = !disabled && error && error !== '' + + return ( +
+ + + {shouldDisplayError && ( + {error} + )} +
+ ) +} + +export default RatingField diff --git a/packages/components/src/molecules/RatingField/index.ts b/packages/components/src/molecules/RatingField/index.ts new file mode 100644 index 0000000000..06749f3abe --- /dev/null +++ b/packages/components/src/molecules/RatingField/index.ts @@ -0,0 +1,2 @@ +export { default } from './RatingField' +export type { RatingFieldProps } from './RatingField' diff --git a/packages/ui/src/components/molecules/Rating/styles.scss b/packages/ui/src/components/molecules/Rating/styles.scss index ac6817361a..709699eb9b 100644 --- a/packages/ui/src/components/molecules/Rating/styles.scss +++ b/packages/ui/src/components/molecules/Rating/styles.scss @@ -18,10 +18,12 @@ --fs-rating-actionable-icon-height : var(--fs-rating-actionable-icon-width); --fs-rating-actionable-icon-color : var(--fs-rating-color-empty); --fs-rating-actionable-icon-color-selected : var(--fs-rating-color); + --fs-rating-button-min-height : var(--fs-spacing-5); // -------------------------------------------------------- // Structural Styles // -------------------------------------------------------- + display: flex; [data-fs-icon] { @@ -31,6 +33,8 @@ } [data-fs-rating-button] { + --fs-button-small-min-height: var(--fs-rating-button-min-height); + color: unset; &[disabled] [data-fs-button-wrapper] { @@ -92,6 +96,7 @@ [data-fs-icon] { width: var(--fs-rating-actionable-icon-width); height: var(--fs-rating-actionable-icon-height); + color: var(--fs-rating-actionable-color); color: var(--fs-rating-actionable-icon-color); } } diff --git a/packages/ui/src/components/molecules/RatingField/styles.scss b/packages/ui/src/components/molecules/RatingField/styles.scss new file mode 100644 index 0000000000..6daa6cccff --- /dev/null +++ b/packages/ui/src/components/molecules/RatingField/styles.scss @@ -0,0 +1,44 @@ +[data-fs-rating-field] { + // -------------------------------------------------------- + // Design Tokens for Rating Field + // -------------------------------------------------------- + + // Label + --fs-rating-field-label-color : var(--fs-color-text-light); + --fs-rating-field-label-size : var(--fs-text-size-2); + --fs-rating-field-label-line-height : var(--fs-text-size-4); + + // Error + --fs-rating-field-error-message-size : var(--fs-text-size-legend); + --fs-rating-field-error-message-line-height : 1.1; + --fs-rating-field-error-message-color : var(--fs-color-danger-text); + + // -------------------------------------------------------- + // Structural Styles + // -------------------------------------------------------- + display: flex; + flex-direction: column; + + [data-fs-rating-field-label] { + margin-bottom: var(--fs-spacing-0); + font-size: var(--fs-rating-field-label-size); + line-height: var(--fs-rating-field-label-line-height); + color: var(--fs-rating-field-label-color); + } + + [data-fs-rating-field-error-message] { + margin-top: var(--fs-spacing-0); + font-size: var(--fs-rating-field-error-message-size); + line-height: var(--fs-rating-field-error-message-line-height); + color: var(--fs-rating-field-error-message-color); + } + + // -------------------------------------------------------- + // Variants Styles + // -------------------------------------------------------- + &[data-fs-rating-field-error="true"] { + [data-fs-rating-item="empty"] [data-fs-icon] { + color: var(--fs-rating-field-error-message-color); + } + } +} diff --git a/packages/ui/src/styles/components.scss b/packages/ui/src/styles/components.scss index e5be295692..431a094e8b 100644 --- a/packages/ui/src/styles/components.scss +++ b/packages/ui/src/styles/components.scss @@ -41,6 +41,7 @@ @import "../components/molecules/QuantitySelector/styles"; @import "../components/molecules/RadioField/styles"; @import "../components/molecules/Rating/styles"; +@import "../components/molecules/RatingField/styles"; @import "../components/molecules/RegionBar/styles"; @import "../components/molecules/SearchAutoComplete/styles"; @import "../components/molecules/SearchDropdown/styles";