Added filter chips

This commit is contained in:
SwiftyOS
2024-10-11 12:25:49 +02:00
parent cf97e25b4a
commit 948279a67d
2 changed files with 175 additions and 0 deletions

View File

@@ -0,0 +1,123 @@
import type { Meta, StoryObj } from "@storybook/react";
import { FilterChips } from "./FilterChips";
import { userEvent, within, expect } from "@storybook/test";
const meta = {
title: "AGPTUI/FilterChips",
component: FilterChips,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
argTypes: {
badges: { control: "object" },
onFilterChange: { action: "onFilterChange" },
multiSelect: { control: "boolean" },
},
} satisfies Meta<typeof FilterChips>;
export default meta;
type Story = StoryObj<typeof meta>;
const defaultBadges = [
"Marketing",
"Sales",
"Content creation",
"Lorem ipsum",
"Lorem ipsum",
];
export const Default: Story = {
args: {
badges: defaultBadges,
multiSelect: true,
},
};
export const SingleSelect: Story = {
args: {
badges: defaultBadges,
multiSelect: false,
},
};
export const WithSelectedFilters: Story = {
args: {
badges: defaultBadges,
multiSelect: true,
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const marketingChip = canvas.getByText("Marketing").parentElement;
const salesChip = canvas.getByText("Sales").parentElement;
if (!marketingChip || !salesChip) {
throw new Error("Marketing or Sales chip not found");
}
await userEvent.click(marketingChip);
await userEvent.click(salesChip);
expect(marketingChip).toHaveClass("bg-neutral-100");
expect(salesChip).toHaveClass("bg-neutral-100");
},
};
export const WithFilterChangeCallback: Story = {
args: {
badges: defaultBadges,
multiSelect: true,
onFilterChange: (selectedFilters: string[]) => {
console.log("Selected filters:", selectedFilters);
},
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const salesChip = canvas.getByText("Sales");
const marketingChip = canvas.getByText("Marketing");
await userEvent.click(salesChip);
await userEvent.click(marketingChip);
},
};
export const EmptyBadges: Story = {
args: {
badges: [],
multiSelect: true,
},
};
export const LongBadgeNames: Story = {
args: {
badges: [
"Machine Learning",
"Natural Language Processing",
"Computer Vision",
"Data Science",
],
multiSelect: true,
},
};
export const SingleSelectBehavior: Story = {
args: {
badges: defaultBadges,
multiSelect: false,
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const salesChip = canvas.getByText("Sales").parentElement;
const marketingChip = canvas.getByText("Marketing").parentElement;
if (!salesChip || !marketingChip) {
throw new Error("Sales or Marketing chip not found");
}
await userEvent.click(salesChip);
expect(salesChip).toHaveClass("bg-neutral-100");
await userEvent.click(marketingChip);
expect(marketingChip).toHaveClass("bg-neutral-100");
expect(salesChip).not.toHaveClass("bg-neutral-100");
},
};

View File

@@ -0,0 +1,52 @@
import * as React from "react";
import { Badge } from "@/components/ui/badge";
interface FilterChipsProps {
badges: string[];
onFilterChange?: (selectedFilters: string[]) => void;
multiSelect?: boolean;
}
/** FilterChips is a component that allows the user to select filters from a list of badges. It is used on the Agent Store home page */
export const FilterChips: React.FC<FilterChipsProps> = ({
badges,
onFilterChange,
multiSelect = true,
}) => {
const [selectedFilters, setSelectedFilters] = React.useState<string[]>([]);
const handleBadgeClick = (badge: string) => {
setSelectedFilters((prevFilters) => {
let newFilters;
if (multiSelect) {
newFilters = prevFilters.includes(badge)
? prevFilters.filter((filter) => filter !== badge)
: [...prevFilters, badge];
} else {
newFilters = prevFilters.includes(badge) ? [] : [badge];
}
if (onFilterChange) {
onFilterChange(newFilters);
}
return newFilters;
});
};
return (
<div className="inline-flex h-14 items-center justify-start gap-5">
{badges.map((badge) => (
<Badge
key={badge}
variant={selectedFilters.includes(badge) ? "secondary" : "outline"}
className="h-1] flex cursor-pointer items-center justify-center gap-2.5 rounded-full border border-black/50 px-6 py-2"
onClick={() => handleBadgeClick(badge)}
>
<div className="font-neue text-xl font-medium leading-9 tracking-tight text-[#474747]">
{badge}
</div>
</Badge>
))}
</div>
);
};