diff --git a/src/components/Breadcrumbs/Breadcrumbs.scss b/src/components/Breadcrumbs/Breadcrumbs.scss new file mode 100644 index 00000000..7c7551bd --- /dev/null +++ b/src/components/Breadcrumbs/Breadcrumbs.scss @@ -0,0 +1,38 @@ +.deriv-breadcrumbs { + margin: 0; + padding: 0; + + &__item { + float: left; + list-style-type: none; + } + + &__text { + color: var(--du-text-less-prominent, #999999); + + &--active { + color: var(--du-text-prominent, #333333); + } + + &:not(:last-child) { + cursor: pointer; + + &:hover { + color: var(--du-text-general, #333333); + } + } + } + + &__chevron-icon { + margin: 0 5px; + vertical-align: middle; + + path { + fill: var(--du-text-less-prominent, #999999); + } + + @include mobile { + margin: 0 2.5px; + } + } +} diff --git a/src/components/Breadcrumbs/__tests__/Breadcrumbs.spec.tsx b/src/components/Breadcrumbs/__tests__/Breadcrumbs.spec.tsx new file mode 100644 index 00000000..e35dccf6 --- /dev/null +++ b/src/components/Breadcrumbs/__tests__/Breadcrumbs.spec.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import { render, screen, waitFor, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Breadcrumbs } from ".."; + +const items = [ + { value: 0, text: "Item1" }, + { value: 1, text: "Item2" }, + { value: 2, text: "Item3" }, + { value: 3, text: "Item4" }, +]; + +describe("Breadcrumb component", () => { + it("renders all provided items", () => { + const handleOnClick = jest.fn(); + render(); + + const list = screen.getByRole("list"); + const { getAllByRole } = within(list); + const renderedBreadCrumbs = getAllByRole("listitem"); + + expect(renderedBreadCrumbs.length).toBe(4); + }); + + it("triggers provided callback when item was clicked", async () => { + const handleOnClick = jest.fn(); + render(); + + const list = screen.getByRole("list"); + const { getByText } = within(list); + userEvent.click(getByText("Item1")); + + await waitFor(() => { + expect(handleOnClick).toHaveBeenCalledTimes(1); + }); + }); + + it("renders all provided items with custom separator ", () => { + const handleOnClick = jest.fn(); + render( + {`-->`}} + />, + ); + + const list = screen.getByRole("list"); + const { getAllByTestId } = within(list); + const renderedSeparators = getAllByTestId("dt_separator"); + + expect(renderedSeparators.length).toBe(3); + }); +}); diff --git a/src/components/Breadcrumbs/index.tsx b/src/components/Breadcrumbs/index.tsx new file mode 100644 index 00000000..d70bf7f7 --- /dev/null +++ b/src/components/Breadcrumbs/index.tsx @@ -0,0 +1,58 @@ +import { ComponentProps, ReactNode } from "react"; +import clsx from "clsx"; +import { LegacyChevronRight1pxIcon } from "@deriv/quill-icons"; +import { Text } from "../Text"; +import "./Breadcrumbs.scss"; + +type TItem = { + value?: string | number; + text: string | JSX.Element; +}; + +type BreadcrumbsProps = { + className?: string; + items: TItem[]; + handleOnClick: (item: TItem) => void; + separator?: ReactNode; + textSize?: ComponentProps["size"]; +}; + +export const Breadcrumbs = ({ + className, + items, + handleOnClick, + separator, + textSize = "sm", +}: BreadcrumbsProps) => { + return ( +
    + {items.map((item: TItem, idx: number) => { + const isLastItem = idx === items.length - 1; + + return ( +
  • + handleOnClick(item)} + > + {item.text} + + {!isLastItem && + (separator ?? ( + + ))} +
  • + ); + })} +
+ ); +}; diff --git a/src/main.ts b/src/main.ts index e64bc16d..09b5613f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,7 @@ export { Accordion } from "./components/Accordion"; export { ActionScreen } from "./components/ActionScreen"; export { Button } from "./components/Button"; export { Badge } from "./components/Badge"; +export { Breadcrumbs } from "./components/Breadcrumbs"; export { Checkbox } from "./components/Checkbox"; export { CircularProgressBar } from "./components/CircularProgressBar"; export { Divider } from "./components/Divider"; diff --git a/src/styles/base/_root.scss b/src/styles/base/_root.scss index bd51fcc5..07411ba0 100644 --- a/src/styles/base/_root.scss +++ b/src/styles/base/_root.scss @@ -78,6 +78,7 @@ --du-general-section-6: #{$color-gray-2}; --du-general-disabled: #{$color-gray-3}; --du-general-hover: #{$color-gray-4}; + --du-general-hover-1: #{$color-black-1}; --du-general-active: #{$color-gray-5}; // Icons and Texts --du-text-general: #{$color-black-1}; @@ -250,6 +251,7 @@ --du-general-section-6: #{$color-gray-7}; --du-general-disabled: #{$color-black-4}; --du-general-hover: #{$color-black-5}; + --du-general-hover-1: #{$color-white}; --du-general-active: #{$color-black-8}; // Icons and Texts --du-text-prominent: #{$color-white}; diff --git a/stories/Badge.stories.tsx b/stories/Badge.stories.tsx index ed16f3a8..5da7815c 100644 --- a/stories/Badge.stories.tsx +++ b/stories/Badge.stories.tsx @@ -11,7 +11,7 @@ const meta = { args: { variant: "contained", children: "label", - color: "lightblue", + color: "light-blue", badgeSize: "md", isBold: true, rightIcon: 👉, diff --git a/stories/Breadcrumbs.stories.tsx b/stories/Breadcrumbs.stories.tsx new file mode 100644 index 00000000..9daf4215 --- /dev/null +++ b/stories/Breadcrumbs.stories.tsx @@ -0,0 +1,31 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Breadcrumbs } from "../src/components/Breadcrumbs"; +import { fn } from "@storybook/test"; + +const meta = { + title: "Components/Breadcrumbs", + component: Breadcrumbs, + parameters: { + layout: "centered", + }, + args: { + textSize: "sm", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + name: "Default Breadcrumbs", + args: { + items: [ + { value: "Item1", text: "Item1" }, + { value: "Item2", text: "Item2" }, + { value: "Item3", text: "Item3" }, + { value: "Item4", text: "Item4" }, + ], + handleOnClick: fn(), + }, +};