diff --git a/resources/js/Components/Link.tsx b/resources/js/Components/Link.tsx index 99ed72f62..7a7876506 100644 --- a/resources/js/Components/Link.tsx +++ b/resources/js/Components/Link.tsx @@ -1,6 +1,6 @@ import { Link as InertiaLink } from "@inertiajs/react"; import cn from "classnames"; -import { type MouseEvent, useMemo } from "react"; +import { forwardRef, type MouseEvent, useMemo } from "react"; import { Icon } from "@/Components/Icon"; import { useExternalLinkContext } from "@/Contexts/ExternalLinkContext"; @@ -16,7 +16,7 @@ interface ClassNameProperties { interface Properties extends ClassNameProperties { external?: boolean; href: string; - children: React.ReactNode; + children?: React.ReactNode; "data-testid"?: string; showExternalIcon?: boolean; confirmBeforeProceeding?: boolean; @@ -84,82 +84,91 @@ export const LinkButton = ({ /> ); -export const Link = ({ - external = false, - disabled = false, - useAnchorTag = false, - variant, - className, - fontSize, - textColor, - showExternalIcon = true, - confirmBeforeProceeding = false, - iconClassName, - children, - href, - ...properties -}: Properties): JSX.Element => { - const { openConfirmationModal, hasDisabledLinkWarning, isDomainAllowed } = useExternalLinkContext(); - - if (external) { - const handleExternalClick = (event: MouseEvent): void => { - if (confirmBeforeProceeding) { - if (!hasDisabledLinkWarning && !isDomainAllowed(href)) { - event.preventDefault(); - event.stopPropagation(); - - openConfirmationModal(href); +export const Link = forwardRef( + ( + { + external = false, + disabled = false, + useAnchorTag = false, + variant, + className, + fontSize, + textColor, + showExternalIcon = true, + confirmBeforeProceeding = false, + iconClassName, + children, + href, + ...properties + }: Properties, + reference, + ): JSX.Element => { + const { openConfirmationModal, hasDisabledLinkWarning, isDomainAllowed } = useExternalLinkContext(); + + if (external) { + const handleExternalClick = (event: MouseEvent): void => { + if (confirmBeforeProceeding) { + if (!hasDisabledLinkWarning && !isDomainAllowed(href)) { + event.preventDefault(); + event.stopPropagation(); + + openConfirmationModal(href); + } + return; } - return; - } - stopPropagationAndBlur(event); - }; - - return ( - - {children} + stopPropagationAndBlur(event); + }; + + return ( + + {children} + + {showExternalIcon && ( + + )} + + ); + } - {showExternalIcon && ( - - )} - - ); - } + if (useAnchorTag) { + return ( + + {children} + + ); + } - if (useAnchorTag) { return ( - {children} - + ); - } - - return ( - - {children} - - ); -}; + }, +); + +Link.displayName = "Link"; diff --git a/resources/js/Components/Sidebar/Sidebar.test.tsx b/resources/js/Components/Sidebar/Sidebar.test.tsx index 5f9ceedf8..6e08cf518 100644 --- a/resources/js/Components/Sidebar/Sidebar.test.tsx +++ b/resources/js/Components/Sidebar/Sidebar.test.tsx @@ -11,11 +11,13 @@ describe("Sidebar", () => { subtitle="Customize your App Experience" > @@ -29,23 +31,27 @@ describe("Sidebar", () => { expect(screen.getByText("Settings")).toBeTruthy(); expect(screen.getByText("Customize your App Experience")).toBeTruthy(); - expect(screen.getAllByTestId("SidebarItem")).toHaveLength(3); + expect(screen.getAllByTestId("SidebarItem")).toHaveLength(2); + expect(screen.getAllByTestId("SidebarItem__disabled")).toHaveLength(1); }); it("should not render sidebar head if title and subtitle are undefined", () => { render( @@ -53,6 +59,7 @@ describe("Sidebar", () => { ); expect(screen.queryByText("SidebarHead")).not.toBeTruthy(); - expect(screen.getAllByTestId("SidebarItem")).toHaveLength(3); + expect(screen.getAllByTestId("SidebarItem")).toHaveLength(1); + expect(screen.getAllByTestId("SidebarItem__disabled")).toHaveLength(2); }); }); diff --git a/resources/js/Components/Sidebar/SidebarItem.tsx b/resources/js/Components/Sidebar/SidebarItem.tsx index 2620aade9..712439574 100644 --- a/resources/js/Components/Sidebar/SidebarItem.tsx +++ b/resources/js/Components/Sidebar/SidebarItem.tsx @@ -17,24 +17,47 @@ export const SidebarItem = ({ content={tooltip} disabled={tooltip === undefined} > - - {isTruthy(icon) && ( - - )} -
{title}
-
+ {isDisabled || href === undefined ? ( + + {isTruthy(icon) && ( + + )} + + {title} + + ) : ( + + {isTruthy(icon) && ( + + )} + + {title} + + )} ); diff --git a/resources/js/Components/Tabs.test.tsx b/resources/js/Components/Tabs.test.tsx index 28bed573e..5157ab223 100644 --- a/resources/js/Components/Tabs.test.tsx +++ b/resources/js/Components/Tabs.test.tsx @@ -39,7 +39,7 @@ describe("Tabs", () => { it("should render tab link", () => { render( - + Click Me , ); @@ -48,7 +48,17 @@ describe("Tabs", () => { expect(screen.getByTestId("test").parentElement?.tagName).toBe("A"); - expect(screen.getByTestId("test").parentElement?.getAttribute("href")).toBe("https://ardenthq.com"); + expect(screen.getByTestId("test").parentElement?.getAttribute("href")).toBe("https://ardenthq.com/"); + }); + + it("should render disabled link", () => { + render( + + Click Me + , + ); + + expect(screen.getByTestId("test").parentElement?.tagName).toBe("SPAN"); }); it("has click event", async () => { @@ -87,7 +97,10 @@ describe("Tabs", () => { it("marks link as selected", () => { render( - + Click Me , ); @@ -96,9 +109,9 @@ describe("Tabs", () => { }); it("marks link as disabled", () => { render( - + Click Me - , + , ); expect(screen.getByTestId("test").parentElement?.className).toContain("cursor-not-allowed"); diff --git a/resources/js/Components/Tabs.tsx b/resources/js/Components/Tabs.tsx index 700398e22..b4a8bc6fe 100644 --- a/resources/js/Components/Tabs.tsx +++ b/resources/js/Components/Tabs.tsx @@ -1,8 +1,10 @@ import cn from "classnames"; import { type AnchorHTMLAttributes, type ButtonHTMLAttributes, forwardRef, type HTMLAttributes } from "react"; import { Icon, type IconName } from "./Icon"; +import { Link as InertiaLink } from "@/Components/Link"; -interface TabAnchorProperties extends AnchorHTMLAttributes { +interface TabAnchorProperties extends Omit, "href"> { + href: string; disabled?: boolean; selected?: boolean; } @@ -75,7 +77,7 @@ const getTabClasses = ({ const Link = forwardRef( ({ className, disabled = false, selected = false, ...properties }, reference): JSX.Element => ( - ( ); Link.displayName = "Tabs.Link"; +const DisabledLink = forwardRef>( + ({ className, disabled = false, selected = false, ...properties }, reference): JSX.Element => ( + + ), +); +DisabledLink.displayName = "Tabs.DisabledLink"; + const Button = forwardRef( ({ className, disabled, selected = false, children, icon, ...properties }, reference): JSX.Element => (