Merge pull request #41644 from BerriAI/litellm_remove_dead_helplink_helpicon

refactor(ui): remove unused HelpLink and HelpIcon components
This commit is contained in:
Mateo Wang 2026-09-18 08:46:08 -07:00 committed by GitHub
commit 57a59889ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 139 additions and 349 deletions

View file

@ -40,7 +40,7 @@ vi.mock("@/components/llm_calls/fetch_models", () => ({
fetchAvailableModels: vi.fn().mockResolvedValue([]),
}));
vi.mock("@/components/HelpLink", () => ({
vi.mock("@/components/DocsMenu", () => ({
DocsMenu: () => null,
}));

View file

@ -41,7 +41,7 @@ vi.mock("@/components/llm_calls/fetch_models", () => ({
fetchAvailableModels: vi.fn().mockResolvedValue([]),
}));
vi.mock("@/components/HelpLink", () => ({
vi.mock("@/components/DocsMenu", () => ({
DocsMenu: () => null,
}));

View file

@ -19,7 +19,7 @@ import AddProviderForm from "./add_provider_form";
import ProviderMarginTable from "./provider_margin_table";
import AddMarginForm from "./add_margin_form";
import PricingCalculator from "./pricing_calculator/index";
import { DocsMenu } from "@/components/HelpLink";
import { DocsMenu } from "@/components/DocsMenu";
import HowItWorks from "./how_it_works";
import { useDiscountConfig } from "./use_discount_config";
import { useMarginConfig } from "./use_margin_config";

View file

@ -0,0 +1,69 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../tests/test-utils";
import { DocsMenu } from "./DocsMenu";
describe("DocsMenu", () => {
const items = [
{ label: "Custom pricing", href: "https://docs.example.com/pricing" },
{ label: "Cost tracking", href: "https://docs.example.com/cost" },
];
it("should render the menu button with default text", () => {
renderWithProviders(<DocsMenu items={items} />);
expect(screen.getByRole("button", { name: /docs/i })).toBeInTheDocument();
});
it("should hide menu items initially", () => {
renderWithProviders(<DocsMenu items={items} />);
expect(screen.queryByText("Custom pricing")).not.toBeInTheDocument();
});
it("should show menu items when button is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<DocsMenu items={items} />);
await user.click(screen.getByRole("button", { name: /docs/i }));
expect(screen.getByText("Custom pricing")).toBeInTheDocument();
expect(screen.getByText("Cost tracking")).toBeInTheDocument();
});
it("should close the menu when an item is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<DocsMenu items={items} />);
await user.click(screen.getByRole("button", { name: /docs/i }));
await user.click(screen.getByText("Custom pricing"));
expect(screen.queryByText("Cost tracking")).not.toBeInTheDocument();
});
it("should set aria-expanded correctly based on menu state", async () => {
const user = userEvent.setup();
renderWithProviders(<DocsMenu items={items} />);
const button = screen.getByRole("button", { name: /docs/i });
expect(button).toHaveAttribute("aria-expanded", "false");
await user.click(button);
expect(button).toHaveAttribute("aria-expanded", "true");
});
it("should close menu when clicking outside", async () => {
const user = userEvent.setup();
renderWithProviders(
<div>
<DocsMenu items={items} />
<button>Outside</button>
</div>,
);
await user.click(screen.getByRole("button", { name: /docs/i }));
expect(screen.getByText("Custom pricing")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: /outside/i }));
expect(screen.queryByText("Custom pricing")).not.toBeInTheDocument();
});
});

View file

@ -0,0 +1,67 @@
import React, { useState, useRef, useEffect } from "react";
import { ExternalLink, ChevronDown } from "lucide-react";
interface DocMenuItem {
label: string;
href: string;
}
interface DocsMenuProps {
items: DocMenuItem[];
children?: React.ReactNode;
className?: string;
}
export const DocsMenu: React.FC<DocsMenuProps> = ({ items, children = "Docs", className = "" }) => {
const [isOpen, setIsOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [isOpen]);
return (
<div className={`relative inline-block ${className}`} ref={menuRef}>
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center gap-1 text-muted-foreground hover:text-foreground text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-1 rounded-sm px-2 py-1"
aria-expanded={isOpen}
aria-haspopup="true"
>
<span>{children}</span>
<ChevronDown className={`h-3 w-3 transition-transform ${isOpen ? "rotate-180" : ""}`} aria-hidden="true" />
</button>
{isOpen && (
<div className="absolute right-0 mt-1 w-56 bg-card rounded-lg shadow-lg border border-border py-1 z-floating">
{items.map((item, index) => (
<a
key={index}
href={item.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-between px-4 py-2 text-sm text-foreground hover:bg-accent transition-colors"
onClick={() => setIsOpen(false)}
>
<span>{item.label}</span>
<ExternalLink className="h-3.5 w-3.5 text-muted-foreground shrink-0 ml-2" aria-hidden="true" />
</a>
))}
</div>
)}
</div>
);
};

View file

@ -1,147 +0,0 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../tests/test-utils";
import { HelpLink, HelpIcon, DocsMenu } from "./HelpLink";
describe("HelpLink", () => {
it("should render with default children and open in new tab", () => {
renderWithProviders(<HelpLink href="https://docs.example.com" />);
const link = screen.getByRole("link", { name: /learn more/i });
expect(link).toHaveAttribute("href", "https://docs.example.com");
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute("rel", "noopener noreferrer");
});
it("should render custom children text", () => {
renderWithProviders(<HelpLink href="https://docs.example.com">Custom docs link</HelpLink>);
expect(screen.getByText("Custom docs link")).toBeInTheDocument();
});
it("should have the correct href", () => {
renderWithProviders(<HelpLink href="https://docs.example.com/test" />);
expect(screen.getByRole("link")).toHaveAttribute("href", "https://docs.example.com/test");
});
it("should include a screen-reader-only label for accessibility", () => {
renderWithProviders(<HelpLink href="https://docs.example.com" />);
expect(screen.getByText("(opens in a new tab)")).toBeInTheDocument();
});
});
describe("HelpIcon", () => {
it("should render a help button with accessible label", () => {
renderWithProviders(<HelpIcon content="Some help text" />);
expect(screen.getByRole("button", { name: /help information/i })).toBeInTheDocument();
});
it("should show tooltip content on hover", async () => {
const user = userEvent.setup();
renderWithProviders(<HelpIcon content="Tooltip help text" />);
await user.hover(screen.getByRole("button", { name: /help information/i }));
expect(screen.getByText("Tooltip help text")).toBeInTheDocument();
});
it("should hide tooltip content when not hovered", () => {
renderWithProviders(<HelpIcon content="Hidden tooltip" />);
expect(screen.queryByText("Hidden tooltip")).not.toBeInTheDocument();
});
it("should show learn more link when learnMoreHref is provided", async () => {
const user = userEvent.setup();
renderWithProviders(<HelpIcon content="Help text" learnMoreHref="https://docs.example.com" />);
await user.hover(screen.getByRole("button", { name: /help information/i }));
expect(screen.getByText("Learn more")).toBeInTheDocument();
});
it("should use custom learn more text when provided", async () => {
const user = userEvent.setup();
renderWithProviders(
<HelpIcon content="Help text" learnMoreHref="https://docs.example.com" learnMoreText="Read docs" />,
);
await user.hover(screen.getByRole("button", { name: /help information/i }));
const link = screen.getByRole("link", { name: /read docs/i });
expect(link).toHaveAttribute("href", "https://docs.example.com");
});
it("should not show learn more link when learnMoreHref is not provided", async () => {
const user = userEvent.setup();
renderWithProviders(<HelpIcon content="Help text" />);
await user.hover(screen.getByRole("button", { name: /help information/i }));
expect(screen.queryByRole("link")).not.toBeInTheDocument();
});
});
describe("DocsMenu", () => {
const items = [
{ label: "Custom pricing", href: "https://docs.example.com/pricing" },
{ label: "Cost tracking", href: "https://docs.example.com/cost" },
];
it("should render the menu button with default text", () => {
renderWithProviders(<DocsMenu items={items} />);
expect(screen.getByRole("button", { name: /docs/i })).toBeInTheDocument();
});
it("should hide menu items initially", () => {
renderWithProviders(<DocsMenu items={items} />);
expect(screen.queryByText("Custom pricing")).not.toBeInTheDocument();
});
it("should show menu items when button is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<DocsMenu items={items} />);
await user.click(screen.getByRole("button", { name: /docs/i }));
expect(screen.getByText("Custom pricing")).toBeInTheDocument();
expect(screen.getByText("Cost tracking")).toBeInTheDocument();
});
it("should close the menu when an item is clicked", async () => {
const user = userEvent.setup();
renderWithProviders(<DocsMenu items={items} />);
await user.click(screen.getByRole("button", { name: /docs/i }));
await user.click(screen.getByText("Custom pricing"));
expect(screen.queryByText("Cost tracking")).not.toBeInTheDocument();
});
it("should set aria-expanded correctly based on menu state", async () => {
const user = userEvent.setup();
renderWithProviders(<DocsMenu items={items} />);
const button = screen.getByRole("button", { name: /docs/i });
expect(button).toHaveAttribute("aria-expanded", "false");
await user.click(button);
expect(button).toHaveAttribute("aria-expanded", "true");
});
it("should close menu when clicking outside", async () => {
const user = userEvent.setup();
renderWithProviders(
<div>
<DocsMenu items={items} />
<button>Outside</button>
</div>,
);
await user.click(screen.getByRole("button", { name: /docs/i }));
expect(screen.getByText("Custom pricing")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: /outside/i }));
expect(screen.queryByText("Custom pricing")).not.toBeInTheDocument();
});
});

View file

@ -1,199 +0,0 @@
import React, { useState, useRef, useEffect } from "react";
import { ExternalLink, ChevronDown } from "lucide-react";
interface HelpLinkProps {
href: string;
children?: React.ReactNode;
variant?: "inline" | "subtle" | "button";
className?: string;
}
interface DocMenuItem {
label: string;
href: string;
}
interface DocsMenuProps {
items: DocMenuItem[];
children?: React.ReactNode;
className?: string;
}
/**
* A reusable component for linking to documentation, styled similar to Linear's help links.
*
* @example
* // Inline "Learn more" style
* <HelpLink href="https://docs.litellm.ai/docs/proxy/custom_pricing">
* Learn more about custom pricing
* </HelpLink>
*
* @example
* // Subtle link (just icon + text, minimal styling)
* <HelpLink href="https://docs.litellm.ai/docs/proxy/cost_tracking" variant="subtle">
* View docs
* </HelpLink>
*
* @example
* // Button style (more prominent)
* <HelpLink href="https://docs.litellm.ai/docs/proxy/custom_pricing" variant="button">
* Custom Pricing Documentation
* </HelpLink>
*/
export const HelpLink: React.FC<HelpLinkProps> = ({
href,
children = "Learn more",
variant = "inline",
className = "",
}) => {
const baseClasses =
"inline-flex items-center gap-1.5 transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-1 rounded-sm";
const variantClasses = {
inline: "text-info text-sm font-medium hover:underline",
subtle: "text-muted-foreground hover:text-foreground text-xs",
button:
"text-info border border-border px-3 py-1.5 rounded-md bg-card hover:bg-accent text-sm font-medium shadow-xs",
};
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className={`${baseClasses} ${variantClasses[variant]} ${className}`}
title="Open documentation in a new tab"
>
<span>{children}</span>
<ExternalLink className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
<span className="sr-only">(opens in a new tab)</span>
</a>
);
};
/**
* A minimal help icon with tooltip for inline contextual help.
* Similar to Linear's "?" icons that appear next to labels.
*/
interface HelpIconProps {
content: React.ReactNode;
learnMoreHref?: string;
learnMoreText?: string;
}
export const HelpIcon: React.FC<HelpIconProps> = ({ content, learnMoreHref, learnMoreText = "Learn more" }) => {
const [showTooltip, setShowTooltip] = React.useState(false);
return (
<div className="relative inline-block ml-1.5">
<button
type="button"
className="inline-flex items-center justify-center w-4 h-4 text-muted-foreground hover:text-foreground transition-colors cursor-help focus:outline-hidden focus:ring-2 focus:ring-ring rounded-full"
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
onFocus={() => setShowTooltip(true)}
onBlur={() => setShowTooltip(false)}
aria-label="Help information"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="12" cy="12" r="10" strokeWidth="1.5" />
<path strokeLinecap="round" d="M12 17h0M12 13.5a1.5 1.5 0 0 1 1-1.415A1.5 1.5 0 1 0 12 9" strokeWidth="1.5" />
</svg>
</button>
{showTooltip && (
<div
className="absolute left-1/2 -translate-x-1/2 bottom-full mb-2 z-floating bg-gray-900 text-white p-3 rounded-lg text-xs shadow-lg w-64"
style={{ pointerEvents: "none" }}
>
<div className="mb-2">{content}</div>
{learnMoreHref && (
<a
href={learnMoreHref}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-info hover:text-blue-200 font-medium"
style={{ pointerEvents: "auto" }}
>
{learnMoreText}
<ExternalLink className="h-3 w-3" aria-hidden="true" />
</a>
)}
<div
className="absolute left-1/2 -translate-x-1/2 top-full w-0 h-0"
style={{
borderTop: "6px solid rgb(17 24 39)",
borderLeft: "6px solid transparent",
borderRight: "6px solid transparent",
}}
/>
</div>
)}
</div>
);
};
/**
* A dropdown menu for multiple documentation links.
* Linear-style: Single "Docs" button that expands to show multiple relevant links.
*
* @example
* <DocsMenu items={[
* { label: "Custom pricing for models", href: "https://docs.litellm.ai/docs/proxy/custom_pricing" },
* { label: "Spend tracking", href: "https://docs.litellm.ai/docs/proxy/cost_tracking" }
* ]}>
* Docs
* </DocsMenu>
*/
export const DocsMenu: React.FC<DocsMenuProps> = ({ items, children = "Docs", className = "" }) => {
const [isOpen, setIsOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [isOpen]);
return (
<div className={`relative inline-block ${className}`} ref={menuRef}>
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center gap-1 text-muted-foreground hover:text-foreground text-xs transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-1 rounded-sm px-2 py-1"
aria-expanded={isOpen}
aria-haspopup="true"
>
<span>{children}</span>
<ChevronDown className={`h-3 w-3 transition-transform ${isOpen ? "rotate-180" : ""}`} aria-hidden="true" />
</button>
{isOpen && (
<div className="absolute right-0 mt-1 w-56 bg-card rounded-lg shadow-lg border border-border py-1 z-floating">
{items.map((item, index) => (
<a
key={index}
href={item.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-between px-4 py-2 text-sm text-foreground hover:bg-accent transition-colors"
onClick={() => setIsOpen(false)}
>
<span>{item.label}</span>
<ExternalLink className="h-3.5 w-3.5 text-muted-foreground shrink-0 ml-2" aria-hidden="true" />
</a>
))}
</div>
)}
</div>
);
};