Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ interface LearnMoreHoverCardProps {
onExpand: (tabId: string, sourceRect: DOMRect | null) => void;
triggerTooltip?: string;
triggerTooltipDelayMs?: number;
/** Message shown inside the hover card for disabled items (e.g. "Available locally") */
disabledMessage?: string;
}

export function LearnMoreHoverCard({
Expand All @@ -47,6 +49,7 @@ export function LearnMoreHoverCard({
onExpand,
triggerTooltip,
triggerTooltipDelayMs,
disabledMessage,
}: LearnMoreHoverCardProps) {
const entry = learnMoreContent[tabId];
const wrapperRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -194,6 +197,12 @@ export function LearnMoreHoverCard({
/>
</div>

{disabledMessage && (
<p className="text-xs text-muted-foreground/80 italic mb-2">
{disabledMessage}
</p>
)}

<div className="flex items-end justify-between gap-2">
<p className="text-sm text-muted-foreground">{entry.description}</p>
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@ vi.mock("@/components/ui/tooltip", () => ({
}));

vi.mock("@/components/learn-more/LearnMoreHoverCard", () => ({
LearnMoreHoverCard: ({ tabId, children }: any) => (
<div data-testid={`learn-more-${tabId}`}>{children}</div>
LearnMoreHoverCard: ({ tabId, children, disabledMessage }: any) => (
<div data-testid={`learn-more-${tabId}`}>
{disabledMessage && (
<span data-testid="disabled-message">{disabledMessage}</span>
)}
{children}
</div>
),
}));

Expand All @@ -41,7 +46,7 @@ describe("NavMain", () => {
mockSidebarOpen = true;
});

it("keeps disabled items visible without allowing navigation", () => {
it("shows learn-more hover card for disabled items with learn-more content", () => {
const onItemClick = vi.fn();

render(
Expand All @@ -56,11 +61,19 @@ describe("NavMain", () => {
},
]}
onItemClick={onItemClick}
learnMore={{ onExpand: vi.fn() }}
/>,
);

expect(screen.getByTitle(HOSTED_LOCAL_ONLY_TOOLTIP)).toBeInTheDocument();
expect(screen.getByText(HOSTED_LOCAL_ONLY_TOOLTIP)).toBeInTheDocument();
// Should show learn-more hover card with disabled message
expect(screen.getByTestId("learn-more-skills")).toBeInTheDocument();
expect(screen.getByTestId("disabled-message")).toHaveTextContent(
HOSTED_LOCAL_ONLY_TOOLTIP,
);
// No native title attribute (double tooltip fix)
expect(
screen.queryByTitle(HOSTED_LOCAL_ONLY_TOOLTIP),
).not.toBeInTheDocument();

const button = screen.getByRole("button", { name: "Skills" });
expect(button).toHaveAttribute("aria-disabled", "true");
Expand All @@ -69,6 +82,48 @@ describe("NavMain", () => {
expect(onItemClick).not.toHaveBeenCalled();
});

it("shows plain tooltip for disabled items without learn-more content", () => {
render(
<NavMain
items={[
{
title: "SomeDisabled",
url: "#no-learn-more",
icon: FakeIcon,
disabled: true,
disabledTooltip: "Not available",
},
]}
learnMore={{ onExpand: vi.fn() }}
/>,
);

expect(
screen.queryByTestId("learn-more-no-learn-more"),
).not.toBeInTheDocument();
expect(screen.getByText("Not available")).toBeInTheDocument();
expect(screen.queryByTitle("Not available")).not.toBeInTheDocument();
});

it("falls back to tooltip for disabled items when learnMore is not provided", () => {
render(
<NavMain
items={[
{
title: "Skills",
url: "#skills",
icon: FakeIcon,
disabled: true,
disabledTooltip: HOSTED_LOCAL_ONLY_TOOLTIP,
},
]}
/>,
);

expect(screen.queryByTestId("learn-more-skills")).not.toBeInTheDocument();
expect(screen.getByText(HOSTED_LOCAL_ONLY_TOOLTIP)).toBeInTheDocument();
});

it("still handles clicks for enabled items", () => {
const onItemClick = vi.fn();

Expand Down
17 changes: 13 additions & 4 deletions mcpjam-inspector/client/src/components/sidebar/nav-main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export function NavMain({
onExpand={learnMore.onExpand}
triggerTooltip={!sidebarOpen ? item.title : undefined}
triggerTooltipDelayMs={!sidebarOpen ? 1000 : undefined}
disabledMessage={item.disabled ? item.disabledTooltip : undefined}
>
{child}
</LearnMoreHoverCard>
Expand Down Expand Up @@ -124,14 +125,22 @@ export function NavMain({
);

if (item.disabled) {
if (shouldShowHoverCard(item)) {
return (
<SidebarMenuItem key={item.title}>
{wrapWithHoverCard(
item,
<div className="w-full cursor-not-allowed">{button}</div>,
)}
</SidebarMenuItem>
);
}

return (
<SidebarMenuItem key={item.title}>
<Tooltip>
<TooltipTrigger asChild>
<div
className="w-full cursor-not-allowed"
title={item.disabledTooltip}
>
<div className="w-full cursor-not-allowed">
{button}
</div>
</TooltipTrigger>
Expand Down
Loading