Remove modal in useful links

This commit is contained in:
yuneng-jiang
2026-01-02 19:22:56 -08:00
parent dacfe153ba
commit e6da33dc4a
2 changed files with 189 additions and 59 deletions
@@ -1,10 +1,9 @@
import NotificationsManager from "@/components/molecules/notifications_manager";
import { getProxyBaseUrl, getPublicModelHubInfo, updateUsefulLinksCall } from "@/components/networking";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Modal } from "antd";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import UsefulLinksManagement from "./UsefulLinksManagement";
import NotificationsManager from "@/components/molecules/notifications_manager";
import { getPublicModelHubInfo, updateUsefulLinksCall, getProxyBaseUrl } from "@/components/networking";
vi.mock("@/components/networking", () => ({
getPublicModelHubInfo: vi.fn(),
@@ -25,8 +24,6 @@ const mockedUpdateUsefulLinksCall = vi.mocked(updateUsefulLinksCall);
const mockedGetProxyBaseUrl = vi.mocked(getProxyBaseUrl);
const mockedNotifications = vi.mocked(NotificationsManager);
let modalSuccessSpy: any;
describe("UsefulLinksManagement", () => {
beforeEach(() => {
mockedGetPublicModelHubInfo.mockResolvedValue({
@@ -37,11 +34,9 @@ describe("UsefulLinksManagement", () => {
});
mockedUpdateUsefulLinksCall.mockResolvedValue({});
mockedGetProxyBaseUrl.mockReturnValue("https://proxy.example.com");
modalSuccessSpy = vi.spyOn(Modal, "success").mockImplementation(() => ({ destroy: vi.fn() }) as any);
});
afterEach(() => {
modalSuccessSpy.mockRestore();
vi.clearAllMocks();
});
@@ -108,4 +103,153 @@ describe("UsefulLinksManagement", () => {
expect(mockedNotifications.success).toHaveBeenCalledWith("Link order saved successfully");
});
it("should display the Model Hub link", async () => {
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
expect(await screen.findByRole("link", { name: /public model hub/i })).toBeInTheDocument();
});
it("should edit a link when edit button is clicked", async () => {
const user = userEvent.setup();
mockedGetPublicModelHubInfo.mockResolvedValue({
docs_title: "Docs",
custom_docs_description: null,
litellm_version: "1.0.0",
useful_links: {
"Test Link": "https://test.example.com",
},
});
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument());
// Click edit button
const editButton = screen.getByTestId("edit-link-0-Test Link");
await user.click(editButton);
// Should show input fields in edit mode
expect(screen.getByDisplayValue("Test Link")).toBeInTheDocument();
expect(screen.getByDisplayValue("https://test.example.com")).toBeInTheDocument();
});
it("should update a link when save is clicked in edit mode", async () => {
const user = userEvent.setup();
mockedGetPublicModelHubInfo.mockResolvedValue({
docs_title: "Docs",
custom_docs_description: null,
litellm_version: "1.0.0",
useful_links: {
"Test Link": "https://test.example.com",
},
});
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument());
// Click edit button
const editButton = screen.getByTestId("edit-link-0-Test Link");
await user.click(editButton);
// Update the display name
const displayNameInput = screen.getByDisplayValue("Test Link");
await user.clear(displayNameInput);
await user.type(displayNameInput, "Updated Link");
// Click save
await user.click(screen.getByRole("button", { name: /save/i }));
await waitFor(() =>
expect(mockedUpdateUsefulLinksCall).toHaveBeenCalledWith("token", {
"Updated Link": { url: "https://test.example.com", index: 0 },
}),
);
expect(mockedNotifications.success).toHaveBeenCalledWith("Link updated successfully");
});
it("should cancel editing when cancel button is clicked", async () => {
const user = userEvent.setup();
mockedGetPublicModelHubInfo.mockResolvedValue({
docs_title: "Docs",
custom_docs_description: null,
litellm_version: "1.0.0",
useful_links: {
"Test Link": "https://test.example.com",
},
});
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
await waitFor(() => expect(screen.getByText("Test Link")).toBeInTheDocument());
// Click edit button
const editButton = screen.getByTestId("edit-link-0-Test Link");
await user.click(editButton);
// Update the display name
const displayNameInput = screen.getByDisplayValue("Test Link");
await user.clear(displayNameInput);
await user.type(displayNameInput, "Updated Link");
// Click cancel
await user.click(screen.getByRole("button", { name: /cancel/i }));
// Should go back to normal view
expect(screen.getByText("Test Link")).toBeInTheDocument();
expect(screen.queryByDisplayValue("Updated Link")).not.toBeInTheDocument();
});
it("should not move down the last item in rearrange mode", async () => {
const user = userEvent.setup();
mockedGetPublicModelHubInfo.mockResolvedValue({
docs_title: "Docs",
custom_docs_description: null,
litellm_version: "1.0.0",
useful_links: {
"First Link": "https://first.example.com",
"Second Link": "https://second.example.com",
},
});
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
await waitFor(() => expect(screen.getByText("First Link")).toBeInTheDocument());
// Enter rearrange mode
await user.click(screen.getByRole("button", { name: /rearrange order/i }));
// Try to move down the last item (should not do anything)
const secondLinkMoveDownButton = screen.getByTestId("move-down-1-Second Link");
await user.click(secondLinkMoveDownButton);
// Links should remain in same order
const linksAfter = screen.getAllByText(/First Link|Second Link/);
expect(linksAfter[0]).toHaveTextContent("First Link");
expect(linksAfter[1]).toHaveTextContent("Second Link");
});
it("should expand and collapse the component", async () => {
const user = userEvent.setup();
render(<UsefulLinksManagement accessToken="token" userRole="Admin" />);
await waitFor(() => expect(screen.getByText("Link Management")).toBeInTheDocument());
// Initially expanded
expect(screen.getByText("Manage Existing Links")).toBeInTheDocument();
// Click to collapse
await user.click(screen.getByText("Link Management"));
// Should be collapsed
expect(screen.queryByText("Manage Existing Links")).not.toBeInTheDocument();
// Click to expand again
await user.click(screen.getByText("Link Management"));
// Should be expanded
expect(screen.getByText("Manage Existing Links")).toBeInTheDocument();
});
});
@@ -1,11 +1,11 @@
import React, { useState, useEffect } from "react";
import { Modal } from "antd";
import { PlusCircleIcon, ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline";
import { isAdminRole } from "@/utils/roles";
import { getPublicModelHubInfo, updateUsefulLinksCall, getProxyBaseUrl } from "../networking";
import { Card, Title, Text, Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
import NotificationsManager from "@/components/molecules/notifications_manager";
import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
import NotificationsManager from "@/components/molecules/notifications_manager";
import { isAdminRole } from "@/utils/roles";
import { ChevronDownIcon, ChevronRightIcon, ExternalLinkIcon, PlusCircleIcon } from "@heroicons/react/outline";
import { Card, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text, Title } from "@tremor/react";
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { getProxyBaseUrl, getPublicModelHubInfo, updateUsefulLinksCall } from "../networking";
interface UsefulLinksManagementProps {
accessToken: string | null;
@@ -102,32 +102,6 @@ const UsefulLinksManagement: React.FC<UsefulLinksManagementProps> = ({ accessTok
});
await updateUsefulLinksCall(accessToken, linksObject);
// show success modal with public model hub link
Modal.success({
title: "Links Saved Successfully",
content: (
<div className="py-4">
<p className="text-gray-600 mb-4">
Your useful links have been saved and are now visible on the public model hub.
</p>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<p className="text-sm text-blue-800 mb-2 font-medium">View your updated model hub:</p>
<a
href={`${getProxyBaseUrl()}/ui/model_hub_table`}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-blue-600 hover:text-blue-800 underline text-sm font-medium"
>
Open Public Model Hub
</a>
</div>
</div>
),
width: 500,
okText: "Close",
maskClosable: true,
keyboard: true,
});
return true;
} catch (error) {
@@ -319,29 +293,41 @@ const UsefulLinksManagement: React.FC<UsefulLinksManagementProps> = ({ accessTok
</div>
<div className="flex items-center justify-between mb-2">
<Text className="text-sm font-medium text-gray-700">Manage Existing Links</Text>
{!isRearranging ? (
<button
onClick={handleStartRearranging}
className="text-xs bg-purple-50 text-purple-600 px-3 py-1.5 rounded hover:bg-purple-100 flex items-center"
<div className="flex items-center space-x-2">
<Link
href={`${getProxyBaseUrl()}/ui/model_hub_table`}
target="_blank"
rel="noopener noreferrer"
className="text-xs bg-blue-50 text-blue-600 px-3 py-1.5 rounded hover:bg-blue-100 flex items-center"
title="Open Public Model Hub"
>
Rearrange Order
</button>
) : (
<div className="flex space-x-2">
Public Model Hub
<ExternalLinkIcon className="w-4 h-4 ml-1" />
</Link>
{!isRearranging ? (
<button
onClick={handleSaveRearranging}
className="text-xs bg-green-600 text-white px-3 py-1.5 rounded hover:bg-green-700"
onClick={handleStartRearranging}
className="text-xs bg-purple-50 text-purple-600 px-3 py-1.5 rounded hover:bg-purple-100 flex items-center"
>
Save Order
Rearrange Order
</button>
<button
onClick={handleCancelRearranging}
className="text-xs bg-gray-50 text-gray-600 px-3 py-1.5 rounded hover:bg-gray-100"
>
Cancel
</button>
</div>
)}
) : (
<div className="flex space-x-2">
<button
onClick={handleSaveRearranging}
className="text-xs bg-green-600 text-white px-3 py-1.5 rounded hover:bg-green-700"
>
Save Order
</button>
<button
onClick={handleCancelRearranging}
className="text-xs bg-gray-50 text-gray-600 px-3 py-1.5 rounded hover:bg-gray-100"
>
Cancel
</button>
</div>
)}
</div>
</div>
<div className="rounded-lg custom-border relative">
<div className="overflow-x-auto">