2
0

first commit

This commit is contained in:
2024-08-09 00:39:27 +02:00
commit 79688abe2e
5698 changed files with 497838 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Children, Fragment, useEffect, useState } from "react";
type BreadcrumbProps = {
children: React.ReactNode;
};
export const Breadcrumb = ({ children }: BreadcrumbProps) => {
const childrenArray = Children.toArray(children);
const childrenSeperated = childrenArray.map((child, index) => {
// If not the last item in the array insert a /
if (index !== childrenArray.length - 1) {
return (
<Fragment key={index}>
{child}
<span>/</span>
</Fragment>
);
}
// Else return just the child
return child;
});
return (
<nav className="text-default text-sm font-normal leading-5">
<ol className="flex items-center space-x-2 rtl:space-x-reverse">{childrenSeperated}</ol>
</nav>
);
};
type BreadcrumbItemProps = {
children: React.ReactNode;
href: string;
listProps?: JSX.IntrinsicElements["li"];
};
export const BreadcrumbItem = ({ children, href, listProps }: BreadcrumbItemProps) => {
return (
<li {...listProps}>
<Link href={href}>{children}</Link>
</li>
);
};
export const BreadcrumbContainer = () => {
const pathname = usePathname();
const [, setBreadcrumbs] = useState<{ href: string; label: string }[]>();
useEffect(() => {
const rawPath = pathname; // Pathname doesn't include search params anymore
let pathArray = rawPath?.split("/") ?? [];
pathArray.shift();
pathArray = pathArray.filter((path) => path !== "");
const allBreadcrumbs = pathArray.map((path, idx) => {
const href = `/${pathArray.slice(0, idx + 1).join("/")}`;
return {
href,
label: path.charAt(0).toUpperCase() + path.slice(1),
};
});
setBreadcrumbs(allBreadcrumbs);
}, [pathname]);
};
export default Breadcrumb;

View File

@@ -0,0 +1,26 @@
import { Canvas, Meta, Story, ArgsTable } from "@storybook/addon-docs";
import { Examples, Example, Note, Title, CustomArgsTable } from "@calcom/storybook/components";
import { Breadcrumb, BreadcrumbItem } from "./Breadcrumb";
<Meta title="UI/Breadcrumbs" component={Breadcrumb} />
<Title title="Breadcrumbs" suffix="Brief" subtitle="Version 2.0 — Last Update: 22 Aug 2022" />
## Structure
<CustomArgsTable of={Breadcrumb} />
<Examples title="Breadcrumb style">
<Example title="Primary">
<Breadcrumb>
<BreadcrumbItem href="/">Home</BreadcrumbItem>
<BreadcrumbItem href="/">Test</BreadcrumbItem>
</Breadcrumb>
</Example>
</Examples>
## Usage
When hovering over the button, there should be a tooltip to explain the context of the button, so the user can understand the result of action.

View File

@@ -0,0 +1,105 @@
import { render, screen } from "@testing-library/react";
import { Breadcrumb, BreadcrumbItem } from "./Breadcrumb";
describe("Tests for Breadcrumb component", () => {
test("Should render correctly with no items", () => {
render(
<Breadcrumb>
<div>Dummy Child</div>
</Breadcrumb>
);
const breadcrumbNav = screen.getByRole("navigation");
expect(breadcrumbNav).toBeInTheDocument();
const separators = screen.queryAllByText("/");
expect(separators).toHaveLength(0);
});
test("Should render correctly with custom list props", () => {
render(
<Breadcrumb>
<BreadcrumbItem href="/" listProps={{ className: "custom-list" }}>
Home
</BreadcrumbItem>
<BreadcrumbItem href="/about" listProps={{ className: "custom-list" }}>
About
</BreadcrumbItem>
<BreadcrumbItem href="/contact" listProps={{ className: "custom-list" }}>
Contact
</BreadcrumbItem>
</Breadcrumb>
);
const customListItems = document.querySelectorAll(".custom-list");
expect(customListItems.length).toBe(3);
});
test("Should generate correct hrefs and labels", () => {
render(
<Breadcrumb>
<BreadcrumbItem href="/category">Category</BreadcrumbItem>
<BreadcrumbItem href="/category/item">Item</BreadcrumbItem>
</Breadcrumb>
);
const categoryLink = screen.getByText("Category");
const itemLink = screen.getByText("Item");
expect(categoryLink.getAttribute("href")).toBe("/category");
expect(itemLink.getAttribute("href")).toBe("/category/item");
});
test("Should /category be a anchor tag", async () => {
render(
<Breadcrumb>
<BreadcrumbItem href="/category">Category</BreadcrumbItem>
<BreadcrumbItem href="/category/item">Item</BreadcrumbItem>
</Breadcrumb>
);
const categoryLink = screen.getByText("Category");
const categoryAnchor = categoryLink.closest("a");
const categoryItem = categoryAnchor?.parentElement;
expect(categoryAnchor).toBeInTheDocument();
expect(categoryItem?.tagName).toBe("LI");
expect(categoryAnchor?.getAttribute("href")).toBe("/category");
});
test("Should not render separators when there is only one item", () => {
render(
<Breadcrumb>
<BreadcrumbItem href="/">Home</BreadcrumbItem>
</Breadcrumb>
);
const separators = screen.queryAllByText("/");
expect(separators).toHaveLength(0);
});
test("Should render breadcrumbs with correct order when rendered in reverse order", () => {
render(
<Breadcrumb>
<BreadcrumbItem href="/contact">Contact</BreadcrumbItem>
<BreadcrumbItem href="/about">About</BreadcrumbItem>
<BreadcrumbItem href="/">Home</BreadcrumbItem>
</Breadcrumb>
);
const breadcrumbList = screen.getByRole("list");
const breadcrumbItems = screen.getAllByRole("listitem");
expect(breadcrumbItems).toHaveLength(3);
expect(breadcrumbList).toContainElement(breadcrumbItems[2]);
expect(breadcrumbList).toContainElement(breadcrumbItems[1]);
expect(breadcrumbList).toContainElement(breadcrumbItems[0]);
expect(breadcrumbItems[2]).toHaveTextContent("Home");
expect(breadcrumbItems[1]).toHaveTextContent("About");
expect(breadcrumbItems[0]).toHaveTextContent("Contact");
const separators = screen.getAllByText("/");
expect(separators).toHaveLength(2);
});
});

View File

@@ -0,0 +1 @@
export { Breadcrumb, BreadcrumbContainer, BreadcrumbItem } from "./Breadcrumb";