diff --git a/ui/jest.setup.ts b/ui/jest.setup.ts index be96e4bc92..32d27029c9 100644 --- a/ui/jest.setup.ts +++ b/ui/jest.setup.ts @@ -31,6 +31,22 @@ global.ResizeObserver = class ResizeObserver { // jsdom: cmdk scrolls selected items into view Element.prototype.scrollIntoView = function scrollIntoView() {}; +// jsdom: SidebarProvider / useIsMobile +Object.defineProperty(window, "matchMedia", { + writable: true, + configurable: true, + value: jest.fn().mockImplementation((query: string) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + // Mock next/router jest.mock('next/router', () => ({ useRouter() { diff --git a/ui/src/components/chat/ChatInterface.tsx b/ui/src/components/chat/ChatInterface.tsx index 6c055ae144..ac43cc34d6 100644 --- a/ui/src/components/chat/ChatInterface.tsx +++ b/ui/src/components/chat/ChatInterface.tsx @@ -911,7 +911,7 @@ export default function ChatInterface({ selectedAgentName, selectedNamespace, se ); } return ( -
+
diff --git a/ui/src/components/chat/ChatLayoutUI.tsx b/ui/src/components/chat/ChatLayoutUI.tsx index 2a239c94f0..8bcaa845c3 100644 --- a/ui/src/components/chat/ChatLayoutUI.tsx +++ b/ui/src/components/chat/ChatLayoutUI.tsx @@ -108,17 +108,18 @@ export default function ChatLayoutUI({ agentSessions={sessions} isLoadingSessions={isLoadingSessions} /> -
- - {children} - +
+
+ + {children} + +
diff --git a/ui/src/components/sidebars/AgentDetailsSidebar.stories.tsx b/ui/src/components/sidebars/AgentDetailsSidebar.stories.tsx index 5758051984..721a1779ba 100644 --- a/ui/src/components/sidebars/AgentDetailsSidebar.stories.tsx +++ b/ui/src/components/sidebars/AgentDetailsSidebar.stories.tsx @@ -154,7 +154,6 @@ const mockTools: ToolsResponse[] = [ export const AgentWithTools: Story = { args: { - selectedAgentName: "kagent/momus-gpt", currentAgent: mockAgent, allTools: mockTools, }, @@ -162,7 +161,6 @@ export const AgentWithTools: Story = { export const AgentWithNoTools: Story = { args: { - selectedAgentName: "kagent/simple-agent", currentAgent: mockAgentNoTools, allTools: [], }, @@ -170,7 +168,6 @@ export const AgentWithNoTools: Story = { export const BYOAgent: Story = { args: { - selectedAgentName: "kagent/custom-agent", currentAgent: mockBYOAgent, allTools: [], }, diff --git a/ui/src/components/sidebars/AgentDetailsSidebar.tsx b/ui/src/components/sidebars/AgentDetailsSidebar.tsx index 0d877ed290..065f85cced 100644 --- a/ui/src/components/sidebars/AgentDetailsSidebar.tsx +++ b/ui/src/components/sidebars/AgentDetailsSidebar.tsx @@ -17,12 +17,11 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp import { Badge } from "@/components/ui/badge"; interface AgentDetailsSidebarProps { - selectedAgentName: string; currentAgent: AgentResponse; allTools: ToolsResponse[]; } -export function AgentDetailsSidebar({ selectedAgentName, currentAgent, allTools }: AgentDetailsSidebarProps) { +export function AgentDetailsSidebar({ currentAgent, allTools }: AgentDetailsSidebarProps) { const [toolDescriptions, setToolDescriptions] = useState>({}); const [expandedTools, setExpandedTools] = useState>({}); const [availableAgents, setAvailableAgents] = useState([]); @@ -233,28 +232,47 @@ export function AgentDetailsSidebar({ selectedAgentName, currentAgent, allTools // Declarative agents (including SandboxAgent with declarative spec) share model-backed config. const isDeclarativeLikeAgent = selectedTeam?.agent.spec.type === "Declarative"; + const agentNamespace = selectedTeam.agent.metadata.namespace ?? ""; + const agentName = selectedTeam.agent.metadata.name ?? ""; + const agentRef = `${agentNamespace}/${agentName}`; + const editHref = `/agents/new?${new URLSearchParams({ + edit: "true", + name: agentName, + namespace: agentNamespace, + }).toString()}`; + return ( <> - Agent Details + + Agent Details + + -
- - {selectedTeam?.agent.metadata.namespace}/{selectedTeam?.agent.metadata.name} {selectedTeam?.model && `(${selectedTeam?.model})`} - - + {agentRef} + + {selectedTeam?.model && ( +

+ {selectedTeam.model} +

+ )}

{selectedTeam?.agent.spec.description}

diff --git a/ui/src/components/sidebars/__tests__/AgentDetailsSidebar.test.tsx b/ui/src/components/sidebars/__tests__/AgentDetailsSidebar.test.tsx new file mode 100644 index 0000000000..44e65c9381 --- /dev/null +++ b/ui/src/components/sidebars/__tests__/AgentDetailsSidebar.test.tsx @@ -0,0 +1,74 @@ +/** + * @jest-environment jsdom + */ +import { render, screen } from "@testing-library/react"; +import { AgentDetailsSidebar } from "@/components/sidebars/AgentDetailsSidebar"; +import { SidebarProvider } from "@/components/ui/sidebar"; +import type { AgentResponse } from "@/types"; + +jest.mock("@/app/actions/agents", () => ({ + getAgents: jest.fn().mockResolvedValue({ data: [] }), +})); + +function renderSidebar(currentAgent: AgentResponse) { + return render( + + + , + ); +} + +const longNameAgent: AgentResponse = { + id: 1, + agent: { + metadata: { + name: "test-my-agent-qwen7b", + namespace: "ak-poc-testing", + }, + spec: { + description: "testing me agent bro", + type: "Declarative", + }, + }, + model: "vllm/Qwen/Qwen2.5-7B-Instruct", + modelProvider: "openai", + modelConfigRef: "ak-poc-testing/qwen7b", + deploymentReady: true, + accepted: true, + tools: [ + { + type: "Agent", + agent: { + name: "k8s-agent", + namespace: "ak-poc-testing", + kind: "Agent", + apiGroup: "kagent.dev", + }, + }, + ], +}; + +describe("AgentDetailsSidebar", () => { + it("shows the edit control in the header for agents with long names and model strings", () => { + renderSidebar(longNameAgent); + + const editLink = screen.getByRole("link", { + name: "Edit agent ak-poc-testing/test-my-agent-qwen7b", + }); + expect(editLink).toBeInTheDocument(); + expect(editLink).toHaveAttribute( + "href", + "/agents/new?edit=true&name=test-my-agent-qwen7b&namespace=ak-poc-testing", + ); + }); + + it("renders agent ref and model on separate truncated lines", () => { + renderSidebar(longNameAgent); + + expect(screen.getByText("ak-poc-testing/test-my-agent-qwen7b")).toBeInTheDocument(); + expect(screen.getByText("vllm/Qwen/Qwen2.5-7B-Instruct")).toBeInTheDocument(); + }); +});