Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(PAR-596): IconLabel review variant refactor into ReviewsCounterLabel #54

Merged
merged 4 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 1 addition & 38 deletions src/components/IconLabel/IconLabel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,7 @@ const meta: Meta<typeof IconLabel> = {
argTypes: {
type: {
control: "select",
options: [
"default",
"ai-evaluation",
"date",
"dateWithPrefix",
"reviews",
"address",
"social",
],
options: ["default", "ai-evaluation", "date", "dateWithPrefix", "address", "social"],
},
platform: {
control: "select",
Expand Down Expand Up @@ -58,20 +50,6 @@ const meta: Meta<typeof IconLabel> = {
},
if: { arg: "type", eq: "period" },
},
posReviews: {
control: {
type: "number",
min: 0,
},
if: { arg: "type", eq: "reviews" },
},
negReviews: {
control: {
type: "number",
min: 0,
},
if: { arg: "type", eq: "reviews" },
},
address: {
control: "text",
if: { arg: "type", eq: "address" },
Expand Down Expand Up @@ -114,8 +92,6 @@ export const Playground: Story = {
percent: 77, // Default for evaluation
date: new Date(), // Default for date
prefix: "Applied on: ", // Default for dateWithPrefix
posReviews: 3, // Default for reviews
negReviews: 3, // Default for reviews
address: "0xE307051C410e970b861CC55CBFD5Acc7BB477750", // Default for address
link: "https://github.com/user", // Default for social
isVerified: false, // Default for social
Expand Down Expand Up @@ -261,16 +237,3 @@ export const SocialTwitter: Story = {
expect(linkText).toBeInTheDocument();
},
};
// Reviews Label Story
export const Reviews: Story = {
args: {
type: "reviews",
posReviews: 3,
negReviews: 2,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const reviewsText = await canvas.findByText("5 Reviews");
expect(reviewsText).toBeInTheDocument();
},
};
18 changes: 1 addition & 17 deletions src/components/IconLabel/IconLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,7 @@ import { getAddressLabel } from "@/lib/utils";
import { IconType } from "@/primitives/Icon";

import { IconLabelProps } from "./types";
import {
getEvaluation,
renderReviewIcons,
IconLabelContainer,
getFormattedLink,
RenderIcon,
} from "./utils";
import { getEvaluation, IconLabelContainer, getFormattedLink, RenderIcon } from "./utils";
import { variants } from "./variants";

export const IconLabel: React.FC<IconLabelProps> = (props) => {
Expand Down Expand Up @@ -88,16 +82,6 @@ export const IconLabel: React.FC<IconLabelProps> = (props) => {
)}`}</span>
</IconLabelContainer>
))
.with({ type: "reviews" }, ({ posReviews = 0, negReviews = 0, className }) => {
const totalReviews = Math.max(0, posReviews) + Math.max(0, negReviews);

return (
<IconLabelContainer type="reviews" className={className}>
<div className="flex items-center">{renderReviewIcons(posReviews, negReviews)}</div>
<span className={text()}>{`${totalReviews} Reviews`}</span>
</IconLabelContainer>
);
})
.with({ type: "address" }, ({ address, ens, className }) => {
const label = getAddressLabel(ens, address);

Expand Down
7 changes: 0 additions & 7 deletions src/components/IconLabel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ interface DateWithPrefixProps {
className?: string;
}

interface ReviewsProps {
type: "reviews";
posReviews?: number;
negReviews?: number;
className?: string;
}
interface AddressProps {
type: "address";
address?: string;
Expand Down Expand Up @@ -68,7 +62,6 @@ export type IconLabelProps =
| DateProps
| PeriodProps
| RoundPeriodProps
| ReviewsProps
| AddressProps
| SocialProps
| DateWithPrefixProps
Expand Down
34 changes: 1 addition & 33 deletions src/components/IconLabel/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Icon, IconType } from "@/primitives/Icon";

import { variants } from "./variants";

const { container, icon } = variants();
const { container } = variants();

export function getEvaluation(percent: number) {
percent = Math.min(Math.max(percent, 0), 100);
Expand All @@ -18,38 +18,6 @@ export function getEvaluation(percent: number) {
}
}

export function renderReviewIcons(posReviews: number, negReviews: number) {
const totalReviews = Math.max(0, posReviews) + Math.max(0, negReviews);
const icons = [];

for (let i = 0; i < totalReviews; i++) {
const isPositive = i < posReviews;
const isFirst = i === 0;
const classes = getReviewIconClasses(isFirst, isPositive);

icons.push(
<Icon key={i} type={isPositive ? IconType.CHECK : IconType.X} className={classes} />,
);
}

return icons;
}

function getReviewIconClasses(isFirst: boolean, isPositive: boolean): string {
const baseClasses = icon({ type: "reviews" });
let variantClasses = "";

if (isPositive) {
variantClasses = isFirst
? icon({ reviewType: "posFirst" })
: icon({ reviewType: "posNotFirst" });
} else {
variantClasses = icon({ reviewType: "neg" });
}

return `${baseClasses} ${variantClasses}`;
}

export const IconLabelContainer = ({
type,
iconVariant,
Expand Down
10 changes: 1 addition & 9 deletions src/components/IconLabel/variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,10 @@ export const variants = tv({
text: "text-[14px]/[17px] text-grey-700",
icon: "size-4 fill-grey-700",
},
reviews: {
container: "gap-2",
icon: "size-7 rounded-full border border-grey-100 bg-white",
},

verifiedBadge: {
icon: "h-5 w-[28px]",
},
},
reviewType: {
posFirst: { icon: "fill-green-600" },
posNotFirst: { icon: "-ml-2 fill-green-600" },
neg: { icon: "-ml-2 fill-red-200" },
},
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getReviewsCount } from "~checker/utils/getReviewsCount";
import { EvaluationAction, ProjectEvaluationAction } from "../ProjectEvaluationAction";
import { ProjectStatus } from "../ProjectEvaluationAction";
import { ProjectReview } from "../ProjectReviewList";
import { ReviewsCounterLabel } from "../ReviewsCounterLabel";

export interface ProjectEvaluationListProps {
evaluationStatus?: ProjectStatus;
Expand Down Expand Up @@ -50,7 +51,7 @@ export const ProjectEvaluationList = ({
width: "1fr",
render: (item) => {
const { nApproved, nRejected } = getReviewsCount(item.reviews);
return <IconLabel type="reviews" posReviews={nApproved} negReviews={nRejected} />;
return <ReviewsCounterLabel positiveReviews={nApproved} negativeReviews={nRejected} />;
},
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ListGrid, ListGridColumn } from "@/primitives/ListGrid";

import { getReviewsCount } from "~checker/utils/getReviewsCount";

import { ReviewsCounterLabel } from "../ReviewsCounterLabel";
import { ProjectReview } from "./types";

export interface ProjectReviewListProps {
Expand Down Expand Up @@ -45,7 +46,7 @@ export const ProjectReviewList = ({ reviewer, projects, action }: ProjectReviewL
width: "1fr",
render: (item) => {
const { nApproved, nRejected } = getReviewsCount(item.reviews);
return <IconLabel type="reviews" posReviews={nApproved} negReviews={nRejected} />;
return <ReviewsCounterLabel positiveReviews={nApproved} negativeReviews={nRejected} />;
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Meta, StoryObj } from "@storybook/react";

import { ReviewsCounterLabel } from "./ReviewsCounterLabel";

const meta: Meta<typeof ReviewsCounterLabel> = {
title: "features/Checker/Components/ReviewsCounterLabel",
component: ReviewsCounterLabel,
argTypes: {
positiveReviews: {
control: {
type: "number",
min: 0,
},
},
negativeReviews: {
control: {
type: "number",
min: 0,
},
},
className: {
control: "text",
description: "Additional CSS classes to apply to the component.",
},
},
};

export default meta;
type Story = StoryObj<typeof ReviewsCounterLabel>;

export const Default: Story = {
args: {
positiveReviews: 0,
negativeReviews: 0,
},
};

export const Pending: Story = {
args: {
positiveReviews: 0,
negativeReviews: 0,
},
};

export const WithoutMaxReviews: Story = {
args: {
positiveReviews: 1,
negativeReviews: 2,
},
};

export const WithMaxReviews: Story = {
args: {
positiveReviews: 3,
negativeReviews: 2,
},
};

export const TooManyReviews: Story = {
args: {
positiveReviews: 300,
negativeReviews: 200,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";

import { tv } from "tailwind-variants";

import { cn } from "@/lib/utils";

import { ReviewIconGroup } from "./components/ReviewIconGroup";

const variants = tv({
slots: {
container: "flex items-center gap-2",
text: "truncate text-[16px]/[24px]",
},
});

interface ReviewsCounterLabelProps {
positiveReviews?: number;
negativeReviews?: number;
className?: string;
}

export const ReviewsCounterLabel: React.FC<ReviewsCounterLabelProps> = ({
positiveReviews = 0,
negativeReviews = 0,
className,
}) => {
const { text, container } = variants();

const totalReviews = Math.max(0, positiveReviews) + Math.max(0, negativeReviews);

return (
<div className={cn(container(), className)}>
<ReviewIconGroup positiveReviews={positiveReviews} negativeReviews={negativeReviews} />
<span className={text()}>
{totalReviews ? `${totalReviews} Review${totalReviews > 1 ? "s" : ""}` : "Needs review"}
</span>
</div>
);
};
Loading
Loading